博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android listview局部刷新和模拟应用下载(zhu)
阅读量:6892 次
发布时间:2019-06-27

本文共 24912 字,大约阅读时间需要 83 分钟。

在android开发中,listview是比较常用的一个组件,在listview的数据需要更新的时候,一般会用notifyDataSetChanged()这个函数,但是它会更新listview中所有可视范围内的item,这样对性能肯定会有影响。比较常见的情景是android应用商店中的下载列表,当我们下载一款游戏的时候,只需要更新这款游戏对应的进度就可以了。本文就来模拟android应用商店的游戏下载,实现对listview的局部刷新,只实现一个简单的demo,不去真的下载文件。

1. 首先来创建代表应用商店中的app文件的类:AppFile.java,包含了一些基本的属性,源码:

package com.alexzhou.downloadfile; /** * author:alexzhou * email :zhoujiangbohai@163.com * date :2013-1-27 * * 游戏列表中的app文件 **/ public class AppFile {     public int id;    public String name;    // app的大小    public int size;    // 已下载大小    public int downloadSize;    // 下载状态:正常,正在下载,暂停,等待,已下载    public int downloadState;}

2. 由于实际开发时,AppFile的属性比较多,这里创建一个辅助类:DownloadFile.java,代表下载中的文件,源码:

package com.alexzhou.downloadfile; /** * author:alexzhou * email :zhoujiangbohai@163.com * date :2013-1-27 * * 下载的文件 **/ public class DownloadFile {     public int downloadID;    public int downloadSize;    public int totalSize;    public int downloadState;}

3. 接下来需要一个下载管理类:DownloadManager.java,它管理所有下载任务。当同时下载很多任务的时候,界面会卡,所以指定只能同时下载3个任务,每个任务会启动一个线程,这里使用了ExecutorService线程池。当提交了超过三个下载任务时,只执行前3个任务,第四个任务会等到前面有一个下载完成后再下载,以此类推。这里还用到了android提供的一个工具类SparseArray,它是用来替代HashMap的,性能比HashMap要好。下面看源码:

import java.util.ArrayList;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; import android.os.Handler;import android.os.Message;import android.util.Log;import android.util.SparseArray; /**author:alexzhouemail :zhoujiangbohai@163.comdate  :2013-1-27 下载管理 **/ public class DownloadManager {     // 下载状态:正常,暂停,下载中,已下载,排队中    public static final int DOWNLOAD_STATE_NORMAL = 0x00;    public static final int DOWNLOAD_STATE_PAUSE = 0x01;    public static final int DOWNLOAD_STATE_DOWNLOADING = 0x02;    public static final int DOWNLOAD_STATE_FINISH = 0x03;    public static final int DOWNLOAD_STATE_WAITING = 0x04;     // SparseArray是android中替代Hashmap的类,可以提高效率    private SparseArray
downloadFiles = new SparseArray
(); // 用来管理所有下载任务 private ArrayList
taskList = new ArrayList
(); private Handler mHandler; private final static Object syncObj = new Object(); private static DownloadManager instance; private ExecutorService executorService; private DownloadManager() { // 最多只能同时下载3个任务,其余的任务排队等待 executorService = Executors.newFixedThreadPool(3); } public static DownloadManager getInstance() { if(null == instance) { synchronized(syncObj) { instance = new DownloadManager(); } return instance; } return instance; } public void setHandler(Handler handler) { this.mHandler = handler; } // 开始下载,创建一个下载线程 public void startDownload(DownloadFile file) { downloadFiles.put(file.downloadID, file); DownloadTask task = new DownloadTask(file.downloadID); taskList.add(task); executorService.submit(task); } public void stopAllDownloadTask() { while(taskList.size() != 0) { DownloadTask task = taskList.remove(0); // 可以在这里做其他的处理 task.stopTask(); } // 会停止正在进行的任务和拒绝接受新的任务 executorService.shutdownNow(); } // 下载任务 class DownloadTask implements Runnable { private boolean isWorking = false; private int downloadId; public DownloadTask(int id) { this.isWorking = true; this.downloadId = id; } public void stopTask() { this.isWorking = false; } // 更新listview中对应的item public void update(DownloadFile downloadFile) { Message msg = mHandler.obtainMessage(); if(downloadFile.totalSize == downloadFile.downloadSize) downloadFile.downloadState = DOWNLOAD_STATE_FINISH; msg.obj = downloadFile; msg.sendToTarget(); } public void run() { // 更新下载文件的状态 DownloadFile downloadFile = downloadFiles.get(downloadId); downloadFile.downloadState = DOWNLOAD_STATE_DOWNLOADING; while(isWorking) { // 检测是否下载完成 if(downloadFile.downloadState != DOWNLOAD_STATE_DOWNLOADING) { downloadFiles.remove(downloadFile.downloadID); taskList.remove(this); isWorking = false; break; } //Log.e("", "downloadSize="+downloadFile.downloadSize+"; size="+downloadFile.totalSize); // 这里只是模拟了下载,每一秒更新一次item的下载状态 if(downloadFile.downloadSize <= downloadFile.totalSize) { this.update(downloadFile); } if(downloadFile.downloadSize < downloadFile.totalSize) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); downloadFile.downloadState = DOWNLOAD_STATE_PAUSE; this.update(downloadFile); downloadFiles.remove(downloadId); isWorking = false; break; } ++ downloadFile.downloadSize; } } } } }

4. 接下来就需要实现listview的adapter了,这里比较重要的一个函数是updateView,这是实现listview局部刷新的关键,通过索引index得到listview中对应位置的子view,然后再更新该view的数据。源码:

package com.alexzhou.downloadfile; import android.content.Context;import android.graphics.drawable.Drawable;import android.os.Handler;import android.os.Message;import android.util.Log;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Button;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.TextView; /**author:alexzhouemail :zhoujiangbohai@163.comdate  :2013-1-27 app列表的数据适配器 **/ public class AppListAdapter extends BaseAdapter {     private SparseArray
dataList = null; private LayoutInflater inflater = null; private Context mContext; private DownloadManager downloadManager; private ListView listView; public AppListAdapter(Context context, SparseArray
dataList) { this.inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.dataList = dataList; this.mContext = context; this.downloadManager = DownloadManager.getInstance(); this.downloadManager.setHandler(mHandler); } public void setListView(ListView view) { this.listView = view; } @Override public int getCount() { return dataList.size(); } @Override public Object getItem(int position) { return dataList.get(position); } @Override public long getItemId(int position) { return position; } // 改变下载按钮的样式 private void changeBtnStyle(Button btn, boolean enable) { if(enable) { btn.setBackgroundResource(R.drawable.btn_download_norm); } else { btn.setBackgroundResource(R.drawable.btn_download_disable); } btn.setEnabled(enable); } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; if (null == convertView) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.listitem_app, null); holder.layout = (LinearLayout) convertView .findViewById(R.id.gamelist_item_layout); holder.icon = (ImageView) convertView .findViewById(R.id.app_icon); holder.name = (TextView) convertView .findViewById(R.id.app_name); holder.size = (TextView) convertView .findViewById(R.id.app_size); holder.btn = (Button) convertView .findViewById(R.id.download_btn); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 这里position和app.id的值是相等的 final AppFile app = dataList.get(position); //Log.e("", "id="+app.id+", name="+app.name); holder.name.setText(app.name); holder.size.setText((app.downloadSize * 100.0f / app.size) + "%"); Drawable drawable = mContext.getResources().getDrawable(R.drawable.app_icon); holder.icon.setImageDrawable(drawable); switch(app.downloadState) { case DownloadManager.DOWNLOAD_STATE_NORMAL: holder.btn.setText("下载"); this.changeBtnStyle(holder.btn, true); break; case DownloadManager.DOWNLOAD_STATE_DOWNLOADING: holder.btn.setText("下载中"); this.changeBtnStyle(holder.btn, false); break; case DownloadManager.DOWNLOAD_STATE_FINISH: holder.btn.setText("已下载"); this.changeBtnStyle(holder.btn, false); break; case DownloadManager.DOWNLOAD_STATE_WAITING: holder.btn.setText("排队中"); this.changeBtnStyle(holder.btn, false); break; } holder.btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { DownloadFile downloadFile = new DownloadFile(); downloadFile.downloadID = app.id; downloadFile.downloadState = DownloadManager.DOWNLOAD_STATE_WAITING; app.downloadState = DownloadManager.DOWNLOAD_STATE_WAITING; downloadFile.downloadSize = app.downloadSize; downloadFile.totalSize = app.size; holder.btn.setText("排队中"); changeBtnStyle(holder.btn, false); downloadManager.startDownload(downloadFile); } }); return convertView; } static class ViewHolder { LinearLayout layout; ImageView icon; TextView name; TextView size; Button btn; } private Handler mHandler = new Handler() { public void handleMessage(Message msg) { DownloadFile downloadFile = (DownloadFile)msg.obj; AppFile appFile = dataList.get(downloadFile.downloadID); appFile.downloadSize = downloadFile.downloadSize; appFile.downloadState = downloadFile.downloadState; // notifyDataSetChanged会执行getView函数,更新所有可视item的数据 //notifyDataSetChanged(); // 只更新指定item的数据,提高了性能 updateView(appFile.id); } }; // 更新指定item的数据 private void updateView(int index) { int visiblePos = listView.getFirstVisiblePosition(); int offset = index - visiblePos; //Log.e("", "index="+index+"visiblePos="+visiblePos+"offset="+offset); // 只有在可见区域才更新 if(offset < 0) return; View view = listView.getChildAt(offset); final AppFile app = dataList.get(index); ViewHolder holder = (ViewHolder)view.getTag(); //Log.e("", "id="+app.id+", name="+app.name); holder.name.setText(app.name); holder.size.setText((app.downloadSize * 100.0f / app.size) + "%"); Drawable drawable = mContext.getResources().getDrawable(R.drawable.app_icon); holder.icon.setImageDrawable(drawable); switch(app.downloadState) { case DownloadManager.DOWNLOAD_STATE_DOWNLOADING: holder.btn.setText("下载中"); this.changeBtnStyle(holder.btn, false); break; case DownloadManager.DOWNLOAD_STATE_FINISH: holder.btn.setText("已下载"); this.changeBtnStyle(holder.btn, false); break; } }

3. 接下来需要一个下载管理类:DownloadManager.java,它管理所有下载任务。当同时下载很多任务的时候,界面会卡,所以指定只能同时下载3个任务,每个任务会启动一个线程,这里使用了ExecutorService线程池。当提交了超过三个下载任务时,只执行前3个任务,第四个任务会等到前面有一个下载完成后再下载,以此类推。这里还用到了android提供的一个工具类SparseArray,它是用来替代HashMap的,性能比HashMap要好。下面看源码:

package com.alexzhou.downloadfile; import java.util.ArrayList;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; import android.os.Handler;import android.os.Message;import android.util.Log;import android.util.SparseArray; /**author:alexzhouemail :zhoujiangbohai@163.comdate  :2013-1-27 下载管理 **/ public class DownloadManager {     // 下载状态:正常,暂停,下载中,已下载,排队中    public static final int DOWNLOAD_STATE_NORMAL = 0x00;    public static final int DOWNLOAD_STATE_PAUSE = 0x01;    public static final int DOWNLOAD_STATE_DOWNLOADING = 0x02;    public static final int DOWNLOAD_STATE_FINISH = 0x03;    public static final int DOWNLOAD_STATE_WAITING = 0x04;     // SparseArray是android中替代Hashmap的类,可以提高效率    private SparseArray
downloadFiles = new SparseArray
(); // 用来管理所有下载任务 private ArrayList
taskList = new ArrayList
(); private Handler mHandler; private final static Object syncObj = new Object(); private static DownloadManager instance; private ExecutorService executorService; private DownloadManager() { // 最多只能同时下载3个任务,其余的任务排队等待 executorService = Executors.newFixedThreadPool(3); } public static DownloadManager getInstance() { if(null == instance) { synchronized(syncObj) { instance = new DownloadManager(); } return instance; } return instance; } public void setHandler(Handler handler) { this.mHandler = handler; } // 开始下载,创建一个下载线程 public void startDownload(DownloadFile file) { downloadFiles.put(file.downloadID, file); DownloadTask task = new DownloadTask(file.downloadID); taskList.add(task); executorService.submit(task); } public void stopAllDownloadTask() { while(taskList.size() != 0) { DownloadTask task = taskList.remove(0); // 可以在这里做其他的处理 task.stopTask(); } // 会停止正在进行的任务和拒绝接受新的任务 executorService.shutdownNow(); } // 下载任务 class DownloadTask implements Runnable { private boolean isWorking = false; private int downloadId; public DownloadTask(int id) { this.isWorking = true; this.downloadId = id; } public void stopTask() { this.isWorking = false; } // 更新listview中对应的item public void update(DownloadFile downloadFile) { Message msg = mHandler.obtainMessage(); if(downloadFile.totalSize == downloadFile.downloadSize) downloadFile.downloadState = DOWNLOAD_STATE_FINISH; msg.obj = downloadFile; msg.sendToTarget(); } public void run() { // 更新下载文件的状态 DownloadFile downloadFile = downloadFiles.get(downloadId); downloadFile.downloadState = DOWNLOAD_STATE_DOWNLOADING; while(isWorking) { // 检测是否下载完成 if(downloadFile.downloadState != DOWNLOAD_STATE_DOWNLOADING) { downloadFiles.remove(downloadFile.downloadID); taskList.remove(this); isWorking = false; break; } //Log.e("", "downloadSize="+downloadFile.downloadSize+"; size="+downloadFile.totalSize); // 这里只是模拟了下载,每一秒更新一次item的下载状态 if(downloadFile.downloadSize <= downloadFile.totalSize) { this.update(downloadFile); } if(downloadFile.downloadSize < downloadFile.totalSize) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); downloadFile.downloadState = DOWNLOAD_STATE_PAUSE; this.update(downloadFile); downloadFiles.remove(downloadId); isWorking = false; break; } ++ downloadFile.downloadSize; } } } } }

4. 接下来就需要实现listview的adapter了,这里比较重要的一个函数是updateView,这是实现listview局部刷新的关键,通过索引index得到listview中对应位置的子view,然后再更新该view的数据。源码:

package com.alexzhou.downloadfile; import android.content.Context;import android.graphics.drawable.Drawable;import android.os.Handler;import android.os.Message;import android.util.Log;import android.util.SparseArray;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Button;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.TextView; /**author:alexzhouemail :zhoujiangbohai@163.comdate  :2013-1-27 app列表的数据适配器 **/ public class AppListAdapter extends BaseAdapter {     private SparseArray
dataList = null; private LayoutInflater inflater = null; private Context mContext; private DownloadManager downloadManager; private ListView listView; public AppListAdapter(Context context, SparseArray
dataList) { this.inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.dataList = dataList; this.mContext = context; this.downloadManager = DownloadManager.getInstance(); this.downloadManager.setHandler(mHandler); } public void setListView(ListView view) { this.listView = view; } @Override public int getCount() { return dataList.size(); } @Override public Object getItem(int position) { return dataList.get(position); } @Override public long getItemId(int position) { return position; } // 改变下载按钮的样式 private void changeBtnStyle(Button btn, boolean enable) { if(enable) { btn.setBackgroundResource(R.drawable.btn_download_norm); } else { btn.setBackgroundResource(R.drawable.btn_download_disable); } btn.setEnabled(enable); } @Override public View getView(int position, View convertView, ViewGroup parent) { final ViewHolder holder; if (null == convertView) { holder = new ViewHolder(); convertView = inflater.inflate(R.layout.listitem_app, null); holder.layout = (LinearLayout) convertView .findViewById(R.id.gamelist_item_layout); holder.icon = (ImageView) convertView .findViewById(R.id.app_icon); holder.name = (TextView) convertView .findViewById(R.id.app_name); holder.size = (TextView) convertView .findViewById(R.id.app_size); holder.btn = (Button) convertView .findViewById(R.id.download_btn); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 这里position和app.id的值是相等的 final AppFile app = dataList.get(position); //Log.e("", "id="+app.id+", name="+app.name); holder.name.setText(app.name); holder.size.setText((app.downloadSize * 100.0f / app.size) + "%"); Drawable drawable = mContext.getResources().getDrawable(R.drawable.app_icon); holder.icon.setImageDrawable(drawable); switch(app.downloadState) { case DownloadManager.DOWNLOAD_STATE_NORMAL: holder.btn.setText("下载"); this.changeBtnStyle(holder.btn, true); break; case DownloadManager.DOWNLOAD_STATE_DOWNLOADING: holder.btn.setText("下载中"); this.changeBtnStyle(holder.btn, false); break; case DownloadManager.DOWNLOAD_STATE_FINISH: holder.btn.setText("已下载"); this.changeBtnStyle(holder.btn, false); break; case DownloadManager.DOWNLOAD_STATE_WAITING: holder.btn.setText("排队中"); this.changeBtnStyle(holder.btn, false); break; } holder.btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { DownloadFile downloadFile = new DownloadFile(); downloadFile.downloadID = app.id; downloadFile.downloadState = DownloadManager.DOWNLOAD_STATE_WAITING; app.downloadState = DownloadManager.DOWNLOAD_STATE_WAITING; downloadFile.downloadSize = app.downloadSize; downloadFile.totalSize = app.size; holder.btn.setText("排队中"); changeBtnStyle(holder.btn, false); downloadManager.startDownload(downloadFile); } }); return convertView; } static class ViewHolder { LinearLayout layout; ImageView icon; TextView name; TextView size; Button btn; } private Handler mHandler = new Handler() { public void handleMessage(Message msg) { DownloadFile downloadFile = (DownloadFile)msg.obj; AppFile appFile = dataList.get(downloadFile.downloadID); appFile.downloadSize = downloadFile.downloadSize; appFile.downloadState = downloadFile.downloadState; // notifyDataSetChanged会执行getView函数,更新所有可视item的数据 //notifyDataSetChanged(); // 只更新指定item的数据,提高了性能 updateView(appFile.id); } }; // 更新指定item的数据 private void updateView(int index) { int visiblePos = listView.getFirstVisiblePosition(); int offset = index - visiblePos; //Log.e("", "index="+index+"visiblePos="+visiblePos+"offset="+offset); // 只有在可见区域才更新 if(offset < 0) return; View view = listView.getChildAt(offset); final AppFile app = dataList.get(index); ViewHolder holder = (ViewHolder)view.getTag(); //Log.e("", "id="+app.id+", name="+app.name); holder.name.setText(app.name); holder.size.setText((app.downloadSize * 100.0f / app.size) + "%"); Drawable drawable = mContext.getResources().getDrawable(R.drawable.app_icon); holder.icon.setImageDrawable(drawable); switch(app.downloadState) { case DownloadManager.DOWNLOAD_STATE_DOWNLOADING: holder.btn.setText("下载中"); this.changeBtnStyle(holder.btn, false); break; case DownloadManager.DOWNLOAD_STATE_FINISH: holder.btn.setText("已下载"); this.changeBtnStyle(holder.btn, false); break; } }}

布局文件listitem_app.xml:

listview中item样式文件style_listitem_background.xml:

item中的button样式文件style_btn_download.xml:

字符文件strings.xml

AndroidDownloadFile
下载

5. 最后创建MainActivity.java,源码:

package com.alexzhou.downloadfile; import android.app.Activity;import android.os.Bundle;import android.util.SparseArray;import android.widget.ListView; public class MainActivity extends Activity{    private SparseArray
appList = new SparseArray
(); private ListView listView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initUI(); } private void initData() { for(int i =0; i<20; i++) { AppFile app = new AppFile(); app.name = "快玩游戏--" + (i+1); app.size = 100; app.id = i; app.downloadState = DownloadManager.DOWNLOAD_STATE_NORMAL; app.downloadSize = 0; appList.put(app.id, app); } } private void initUI() { listView = (ListView)this.findViewById(R.id.listview); AppListAdapter adapter = new AppListAdapter(this, appList); adapter.setListView(listView); listView.setAdapter(adapter); } @Override protected void onDestroy() { super.onDestroy(); DownloadManager.getInstance().stopAllDownloadTask(); } }

布局文件activity_main.xml:

到此为止,代码部分已经全部完成了,下面来看看最终效果图:

这里对比一下分别使用updateView和notifyDataSetChanged时,有什么不一样,看看打印日志:

(1)使用notifyDataSetChanged时,listview可视范围内的所有子项都更新了。

 (2)使用updateView时,只更新了指定的子项。

实例源码地址:

 

转载地址:http://kvzdl.baihongyu.com/

你可能感兴趣的文章
golang 转换markdown文件为html
查看>>
hdu 3016 dp+线段树
查看>>
对象的类型转换
查看>>
maven 下载源码及 Javadoc
查看>>
MySQL优化三之MySQL配置
查看>>
Solidity oraclize query apikey加密
查看>>
flume学习(一)---flume总览
查看>>
c语言编译命令
查看>>
【Android UI设计与开发】8.顶部标题栏(一)ActionBar 奥义·详解
查看>>
cocos2d-x Schedule详解
查看>>
day12
查看>>
day33-2用java的jdbc修改数据库中表的内容
查看>>
scrollToItemAtIndexPath: atScrollPosition: animated:
查看>>
【转载】久坐如吸烟
查看>>
es6.3学习笔记
查看>>
MyEclipse6.5安装SVN插件的方法--在线安装
查看>>
TStringList.SaveToStream TStringStream.SaveToStream
查看>>
任务问题Oracle 技术支持之现场优化的思维路径
查看>>
C6455 CSL详解
查看>>
高血压的症状有哪些?
查看>>