`
wangxinxin1212
  • 浏览: 29856 次
社区版块
存档分类
最新评论

ListView 异步加载头像与ListView批量加载的实现

阅读更多

想实现一个类似于微信联系人列表的功能。网上查看了很多实现方案,主要有以下2种方案:

1.在服务器端将图片的文件流通过base64编码,再经过json/xml数据格式传送给Android客户端,客户端对图片流进行解码,使用ImagView的setImageBitmap()方法渲染ImageView。

  • 优点:图片数据可以跟随对象(javaBean)传送,解析操作比较简单。
  • 缺点:此方案只适传送较小的图片,而且传送数据时要注意base64编码产生的空格问题(可以再进行URL编码)。

2.使用异步加载方式来 加载头像,并将头像图片存入缓存文件中。

  • 优点:避免了上一种只能传输较小图片的问题。使用异步方式避免了UI无响应,带来更好的用户体验。
  • 缺点:实现方式较上一种复杂。

第一种方式这里就不进行说明了。下面主要对第二种实现方案来说明。考虑到用户一次加载较多数据会影响性能,这里加入了批量加载。

先上传效果图:



 

 当ListView滚动到最后一行时会加载新的数据,并提示加载等待信息。还有一种常见的做法是在ListView底部放置【加载更多】按钮来触发继续加载操作。

 具体是实现步骤如下:

  1. 创建加载等待环形进度条:
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical|center_horizontal">
    
        <ProgressBar
            android:layout_width="40dp"
            android:layout_height="40dp"
            style="@android:style/Widget.ProgressBar.Small"/>
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="数据加载中。。。。。。"/>
    </LinearLayout>
     
  2. 创建ListView布局,这里的ListView布局放置在一个FrameLayout里,代码如下:
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        tools:context="com.example.xinxin.pandachild.NearFragment">
    
    
        <ListView
            android:id="@+id/nearListView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="10dp"
            ></ListView>
    
    </FrameLayout>
    
  3. 创建ListView条目布局,代码如下:
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_margin="10dp"
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <ImageView
            android:id="@+id/logoImage"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:layout_margin="10dp"/>
    
        <TextView
            android:id="@+id/userName"
            android:textSize="28sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"/>
    </LinearLayout>
     
  4. 实现自定的Adapter,详细可以参考自定义Adapter的知识,主要代码如下:
    /**
     * Created by xinxin on 2015/8/24.
     */
    public class NearAdapter extends BaseAdapter {
        private List<PUser> list;
        private Context context;
        private LayoutInflater layoutInflater;
        private File catchFile;
        private String catchPath;
    
        public NearAdapter(Context context, List<PUser> list) {
            this.context = context;
            this.list = list;
            layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            initCatchFile();
        }
    
        /**
         * 初始化缓存目录
         */
        private void initCatchFile(){
            catchPath = this.context.getCacheDir().getAbsolutePath() + "/png";
            catchFile = new File(catchPath);
            if (!catchFile.exists()) catchFile.mkdirs();
        }
        //该方法在批量加载ListView数据时使用
        public void addData(List<PUser> list) {
            if (this.list == null) this.list = new ArrayList<>();
            this.list.addAll(list);
        }
    
        @Override
        public int getCount() {
            return list.size();
        }
    
        @Override
        public Object getItem(int position) {
            return list.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        /**
         * 重写父类方法
         * 使用缓存将ListView条目布局进行缓存
         */
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView logImage = null;
            TextView userName = null;
            if (convertView == null) {
                convertView = layoutInflater.inflate(R.layout.near_list_item, null);
                logImage = (ImageView) convertView.findViewById(R.id.logoImage);
                userName = (TextView) convertView.findViewById(R.id.userName);
                ViewCatch viewCatch = new ViewCatch();
                viewCatch.logoImage = logImage;
                viewCatch.userNaem = userName;
                convertView.setTag(viewCatch);
            } else {
                ViewCatch viewCatch = (ViewCatch) convertView.getTag();
                logImage = viewCatch.logoImage;
                userName = viewCatch.userNaem;
            }
    
            PUser pUser = list.get(position);
            userName.setText(pUser.getUserName());
            //异步加载图片
            asyncImageLoad(logImage, pUser.getLogoUrl());
            return convertView;
        }
    
        /**
         * 缓存ListView条目类
         */
        class ViewCatch {
            public ImageView logoImage;
            public TextView userNaem;
        }
    
        /**
         * 异步加载图片
         * @param imageView
         * @param url
         */
        public void asyncImageLoad(final ImageView imageView, final String url) {
            AsyncTask<Integer, Integer, Uri> asyncTask = new AsyncTask<Integer, Integer, Uri>() {
    
                @Override
                protected Uri doInBackground(Integer... params) {//执行加载图片URI操作,并返回图片URI
                    return getImageUri(url);
                }
    
                @Override
                protected void onPostExecute(Uri uri) {//跟新主线程UI
                    if (uri != null)
                        if (imageView != null)
                            imageView.setImageURI(uri);
                }
            };
            asyncTask.execute();
        }
    
        /**
         * 获得图片URI
         * 加载过的文件会存放在缓存目录
         * @param url
         * @return
         */
        private Uri getImageUri(String url) {
            Uri imageUri = null;
            //将图片地址进行MD5,
            String catchFileName = catchPath + "/" + MD5.GetMD5Code(url) + ".png";
            if (!FileUtil.exists(catchFileName)) {//判断缓存目录是否存在该图片资源
                FileUtil.writeFile(catchFileName, HttpUtil.getInputStream(url, null));
            }
            //将文件类型转成URI格式
            imageUri = Uri.fromFile(new File(catchFileName));
            return imageUri;
        }
    
    
    }
     
  5. 主界面的实现,这里在fragment中实现与activity大致相似,主要是通过ListView的OnScrollListener监听事件来控制LlistView的加载,主要代码如下:
    public class NearFragment extends Fragment {
    
        private Context context;
        private ListView listView;
        private NearAdapter adapter;
        private boolean loadFinished;
        private boolean loadAll;
        private LinearLayout footer;
        private PUserService pUserService = new PUserService();
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            context = this.getActivity();
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_near, container, false);
            footer = (LinearLayout) inflater.inflate(R.layout.footer, null);
            listView = (ListView) view.findViewById(R.id.nearListView);
            setOnScrollListener();
            new Thread(new Runnable() {//启动子线程初始化ListView,防止主线程响应超时
                @Override
                public void run() {
                    try {
                        List<PUser> list = pUserService.getUserList();//获得数据
                        handler.sendMessage(handler.obtainMessage(100, list));//发送消息
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            return view;
        }
    
        /**
         * 处理ListView初始化
         */
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                List<PUser> list = (List<PUser>) msg.obj;
                if (list != null) {
                    listView.addFooterView(footer);//在实例化前添加footer,此处必须
                    adapter = new NearAdapter(context, list);
                    listView.setAdapter(adapter);
                    listView.removeFooterView(footer);//去除footer
                }
            }
        };
    
        /**
         * 处理批量加载
         */
        Handler scrollHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                List<PUser> list = (List<PUser>) msg.obj;
                if (list != null && list.size() > 0) {
                    adapter.addData(list);
                    adapter.notifyDataSetChanged();
                    loadFinished = true;
    
                }
                listView.removeFooterView(footer);
            }
        };
    
        /**
         * 实现LilstView批量加载数据
         */
        private void setOnScrollListener() {
            loadFinished = true;
            loadAll = false;
            listView.setOnScrollListener(new AbsListView.OnScrollListener() {
                private boolean finished;//可以控制ListView滚动是否完全停止
                private int pageSize = 20;
    
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
                    if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                        finished = true;
                    }
                }
    
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                    if (!loadAll && totalItemCount > 0 && (firstVisibleItem + visibleItemCount) == totalItemCount && loadFinished) {//为了测试方便,没有判断finished;
                        listView.addFooterView(footer);//添加等待进度条
                        finished = false;
                        loadFinished = false;
                        final int startNo = totalItemCount;
                        new Thread(new Runnable() {//启动子线程,加载数据
                            @Override
                            public void run() {
                                //获得分页数据
                                List<PUser> list = pUserService.getUserList(startNo + 1, pageSize + startNo);
                                if (list != null && list.size() > 0) {
                                    //不处理
                                } else {
                                    loadAll = true;
                                }
                                //发送消息给主线程
                                scrollHandler.sendMessage(scrollHandler.obtainMessage(100, list));
                            }
                        }).start();
                    }
                }
            });
        }
    }
     说明:这里只给出了主要的实现方式,及部分代码,PUser 及 业务处理类就不贴了,当然也不足与待优化的地方。继续学习!

 

 

  • 大小: 659.3 KB
分享到:
评论

相关推荐

    listview异步加载.rar

    1. **Adapter**:Adapter是连接ListView与数据源的桥梁,负责将数据转化为ListView可显示的View。在处理大数据量时,我们通常不一次性加载所有数据,而是采用分页的方式,只加载用户可视范围内的数据。 2. **...

    Android实现ListView异步加载图片

    要实现异步加载图片,我们需要使用 Thread 或 AsyncTask 来加载图片。这里我们使用 Thread 来加载图片。在 loadDrawable 方法中,我们首先检查图片是否已经加载,如果已经加载,则直接返回缓存的图片,如果没有加载...

    ListView异步加载图片

    2. 实现异步加载:使用AsyncTask或其他异步框架(如Handler、Loader)来下载和解码图片。在后台线程中,先检查图片是否已存在于缓存中,如果存在,直接返回;否则,从网络下载图片并解码后存入缓存。 3. 图片加载回...

    listview 异步加载网络图片

    9. **ListView与RecyclerView对比**:虽然现在RecyclerView是更推荐的列表控件,但ListView仍广泛存在于一些老项目中。RecyclerView相比ListView有更强大的复用机制和更灵活的布局管理,但在异步加载网络图片方面,...

    android listView 异步加载图片

    这里的“android listView 异步加载图片”指的是在不阻塞UI线程的情况下,从网络、本地存储或其他来源加载图片到ListView中的技术。 这篇名为“ImageLoader”的Java文件很可能就是一个实现图片异步加载的工具类。在...

    ListView异步加载图片进度条

    这些库提供了方便的API,可以直接设置进度条回调,实现异步加载图片并显示进度。 五、代码规范性 1. 封装图片加载逻辑:为了代码的可读性和复用性,应将图片加载的逻辑封装在一个单独的类或模块中,避免在ListView...

    android listview异步加载图片实例(线程池+数据库+本地保存)

    本实例将详细讲解如何实现“Android ListView异步加载图片”,结合线程池、数据库和本地保存来优化性能。 首先,我们需要理解异步加载的概念。在Android中,由于主线程负责用户界面的更新,因此不应在主线程中执行...

    Android Listview异步加载图片

    在Android中,我们可以使用AsyncTask、Handler、Thread、Runnable或者第三方库如Picasso、Glide等来实现异步加载。 1. **AsyncTask**:AsyncTask是一个轻量级的异步任务框架,适合处理短时间的后台任务。我们可以在...

    listview异步加载源码

    其Adapter同样支持异步加载,可以通过实现ViewHolder和DiffUtil来进一步优化加载效率。 **6. 数据缓存策略** 为了减少网络请求和数据库查询的次数,可以采用数据缓存策略,如内存缓存和磁盘缓存。例如,使用...

    Android ListView异步加载图片

    Android 异步加载图片,对ListView的异步加载图片的功能演示,主要根据url读取图片返回流的方法。为了方便演示,将请求图片的链接先固定,每读取好一个图片就更新,界面比较简单,当然你可以做成比较好的,像很多好...

    ListView异步加载网络图片

    1. 创建一个图片加载器类,实现异步加载功能。这个类通常包含一个HashMap来存储URL与SoftReference的映射关系。 2. 当需要加载图片时,首先检查HashMap中是否存在对应的SoftReference。如果存在且Bitmap未被回收,...

    listview异步加载图片

    本文将详细讲解如何在ListView中实现异步加载图片以及三级缓存的原理。 ### 一、异步加载图片的必要性 1. **避免阻塞主线程**:Android应用的UI更新必须在主线程进行,而图片加载通常需要较长的时间,如果在主线程...

    官方 listview 异步加载图片

    在这个场景下,"有动画效果,圆角效果,不卡屏,速度快"意味着在实现异步加载的同时,还考虑了图片的展示效果和用户体验。 在Android中,实现ListView图片异步加载有许多库可以选用,其中一个非常流行的是Universal...

    listview 异步加载图片

    6. **占位符与加载动画**:在图片加载期间,可以显示一个占位符或加载动画,提供更好的用户体验。占位符通常是与实际图片大小一致的纯色块,而加载动画则可以是简单的转动图标。 7. **图片加载监听**:添加图片加载...

    android listView图片异步加载(拖动时不加载,双缓存)

    本文将详细介绍如何在ListView中实现图片的异步加载,并且在用户拖动时不加载,以提高滚动流畅性。 一、异步加载原理 异步加载是指在后台线程中执行耗时操作,例如网络请求或解码图片,避免阻塞主线程。Android提供...

Global site tag (gtag.js) - Google Analytics