• 转:android listview局部刷新和模拟应用下载


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

    1. package com.alexzhou.downloadfile;  
    2.    
    3. /** 
    4.  * author:alexzhou  
    5.  * email :zhoujiangbohai@163.com  
    6.  * date :2013-1-27 
    7.  *  
    8.  * 游戏列表中的app文件 
    9.  **/  
    10.    
    11. public class AppFile {  
    12.    
    13.     public int id;  
    14.     public String name;  
    15.     // app的大小  
    16.     public int size;  
    17.     // 已下载大小  
    18.     public int downloadSize;  
    19.     // 下载状态:正常,正在下载,暂停,等待,已下载  
    20.     public int downloadState;  
    21. }  

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

    1. package com.alexzhou.downloadfile;  
    2.    
    3. /** 
    4.  * author:alexzhou  
    5.  * email :zhoujiangbohai@163.com  
    6.  * date :2013-1-27 
    7.  *  
    8.  * 下载的文件 
    9.  **/  
    10.    
    11. public class DownloadFile {  
    12.    
    13.     public int downloadID;  
    14.     public int downloadSize;  
    15.     public int totalSize;  
    16.     public int downloadState;  
    17. }  


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

    1. package com.alexzhou.downloadfile;  
    2.   
    3. import java.util.ArrayList;  
    4. import java.util.concurrent.ExecutorService;  
    5. import java.util.concurrent.Executors;  
    6.   
    7. import android.os.Handler;  
    8. import android.os.Message;  
    9. import android.util.Log;  
    10. import android.util.SparseArray;  
    11.   
    12. /** 
    13. author:alexzhou  
    14. email :zhoujiangbohai@163.com 
    15. date  :2013-1-27 
    16.  
    17. 下载管理 
    18.  **/  
    19.   
    20. public class DownloadManager {  
    21.       
    22.     // 下载状态:正常,暂停,下载中,已下载,排队中  
    23.     public static final int DOWNLOAD_STATE_NORMAL = 0x00;  
    24.     public static final int DOWNLOAD_STATE_PAUSE = 0x01;  
    25.     public static final int DOWNLOAD_STATE_DOWNLOADING = 0x02;  
    26.     public static final int DOWNLOAD_STATE_FINISH = 0x03;  
    27.     public static final int DOWNLOAD_STATE_WAITING = 0x04;  
    28.       
    29.     // SparseArray是android中替代Hashmap的类,可以提高效率  
    30.     private SparseArray<DownloadFile> downloadFiles = new SparseArray<DownloadFile>();  
    31.     // 用来管理所有下载任务  
    32.     private ArrayList<DownloadTask> taskList = new ArrayList<DownloadTask>();  
    33.     private Handler mHandler;  
    34.     private final static Object syncObj = new Object();  
    35.     private static DownloadManager instance;  
    36.     private ExecutorService executorService;  
    37.       
    38.     private DownloadManager()  
    39.     {  
    40.         // 最多只能同时下载3个任务,其余的任务排队等待  
    41.         executorService = Executors.newFixedThreadPool(3);  
    42.     }  
    43.       
    44.     public static DownloadManager getInstance()  
    45.     {  
    46.         if(null == instance)  
    47.         {  
    48.             synchronized(syncObj) {  
    49.                 instance = new DownloadManager();  
    50.             }  
    51.             return instance;  
    52.         }  
    53.         return instance;  
    54.     }  
    55.       
    56.     public void setHandler(Handler handler) {  
    57.         this.mHandler =  handler;  
    58.     }  
    59.   
    60.     // 开始下载,创建一个下载线程  
    61.     public void startDownload(DownloadFile file) {  
    62.         downloadFiles.put(file.downloadID, file);  
    63.         DownloadTask task = new DownloadTask(file.downloadID);  
    64.         taskList.add(task);  
    65.         executorService.submit(task);  
    66.     }  
    67.       
    68.     public void stopAllDownloadTask() {  
    69.         while(taskList.size() != 0)  
    70.         {  
    71.             DownloadTask task = taskList.remove(0);  
    72.             // 可以在这里做其他的处理  
    73.             task.stopTask();  
    74.         }  
    75.         // 会停止正在进行的任务和拒绝接受新的任务  
    76.         executorService.shutdownNow();  
    77.           
    78.     }  
    79.   
    80.     // 下载任务  
    81.     class DownloadTask implements Runnable {  
    82.   
    83.         private boolean isWorking = false;  
    84.         private int downloadId;  
    85.   
    86.         public DownloadTask(int id)  
    87.         {  
    88.             this.isWorking = true;  
    89.             this.downloadId = id;  
    90.         }  
    91.           
    92.         public void stopTask()  
    93.         {  
    94.             this.isWorking = false;  
    95.         }  
    96.           
    97.         // 更新listview中对应的item  
    98.         public void update(DownloadFile downloadFile)  
    99.         {  
    100.             Message msg = mHandler.obtainMessage();  
    101.             if(downloadFile.totalSize == downloadFile.downloadSize)  
    102.                 downloadFile.downloadState = DOWNLOAD_STATE_FINISH;  
    103.             msg.obj = downloadFile;  
    104.             msg.sendToTarget();  
    105.               
    106.         }  
    107.           
    108.         public void run() {  
    109.             // 更新下载文件的状态  
    110.             DownloadFile downloadFile = downloadFiles.get(downloadId);  
    111.             downloadFile.downloadState = DOWNLOAD_STATE_DOWNLOADING;  
    112.             while(isWorking)  
    113.             {  
    114.                 // 检测是否下载完成  
    115.                 if(downloadFile.downloadState != DOWNLOAD_STATE_DOWNLOADING)  
    116.                 {  
    117.                     downloadFiles.remove(downloadFile.downloadID);  
    118.                     taskList.remove(this);  
    119.                     isWorking = false;  
    120.                     break;  
    121.                 }  
    122.                 //Log.e("", "downloadSize="+downloadFile.downloadSize+"; size="+downloadFile.totalSize);  
    123.                 // 这里只是模拟了下载,每一秒更新一次item的下载状态  
    124.                 if(downloadFile.downloadSize <= downloadFile.totalSize)  
    125.                 {  
    126.                     this.update(downloadFile);  
    127.                 }  
    128.                   
    129.                 if(downloadFile.downloadSize < downloadFile.totalSize)  
    130.                 {  
    131.                     try {  
    132.                         Thread.sleep(100);  
    133.                     } catch (InterruptedException e) {  
    134.                         e.printStackTrace();  
    135.                         downloadFile.downloadState = DOWNLOAD_STATE_PAUSE;  
    136.                         this.update(downloadFile);  
    137.                         downloadFiles.remove(downloadId);  
    138.                         isWorking = false;  
    139.                         break;  
    140.                     }  
    141.                       
    142.                     ++ downloadFile.downloadSize;  
    143.                 }  
    144.             }  
    145.           
    146.         }  
    147.     }  
    148.   
    149. }  




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

    1. package com.alexzhou.downloadfile;  
    2.   
    3. import android.content.Context;  
    4. import android.graphics.drawable.Drawable;  
    5. import android.os.Handler;  
    6. import android.os.Message;  
    7. import android.util.Log;  
    8. import android.util.SparseArray;  
    9. import android.view.LayoutInflater;  
    10. import android.view.View;  
    11. import android.view.View.OnClickListener;  
    12. import android.view.ViewGroup;  
    13. import android.widget.BaseAdapter;  
    14. import android.widget.Button;  
    15. import android.widget.ImageView;  
    16. import android.widget.LinearLayout;  
    17. import android.widget.ListView;  
    18. import android.widget.TextView;  
    19.   
    20. /** 
    21. author:alexzhou  
    22. email :zhoujiangbohai@163.com 
    23. date  :2013-1-27 
    24.  
    25. app列表的数据适配器 
    26.  **/  
    27.   
    28. public class AppListAdapter extends BaseAdapter {  
    29.   
    30.     private SparseArray<AppFile> dataList = null;  
    31.     private LayoutInflater inflater = null;  
    32.     private Context mContext;  
    33.     private DownloadManager downloadManager;  
    34.     private ListView listView;  
    35.   
    36.     public AppListAdapter(Context context, SparseArray<AppFile> dataList) {  
    37.         this.inflater = (LayoutInflater) context  
    38.                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
    39.         this.dataList = dataList;  
    40.         this.mContext = context;  
    41.         this.downloadManager = DownloadManager.getInstance();  
    42.         this.downloadManager.setHandler(mHandler);  
    43.     }  
    44.   
    45.     public void setListView(ListView view)  
    46.     {  
    47.         this.listView = view;  
    48.     }  
    49.       
    50.     @Override  
    51.     public int getCount() {  
    52.         return dataList.size();  
    53.     }  
    54.   
    55.     @Override  
    56.     public Object getItem(int position) {  
    57.         return dataList.get(position);  
    58.     }  
    59.   
    60.     @Override  
    61.     public long getItemId(int position) {  
    62.         return position;  
    63.     }  
    64.       
    65.     // 改变下载按钮的样式  
    66.     private void changeBtnStyle(Button btn, boolean enable)  
    67.     {  
    68.         if(enable)  
    69.         {  
    70.             btn.setBackgroundResource(R.drawable.btn_download_norm);  
    71.         }  
    72.         else  
    73.         {  
    74.             btn.setBackgroundResource(R.drawable.btn_download_disable);  
    75.         }  
    76.         btn.setEnabled(enable);  
    77.     }  
    78.   
    79.     @Override  
    80.     public View getView(int position, View convertView, ViewGroup parent) {  
    81.   
    82.         final ViewHolder holder;  
    83.         if (null == convertView) {  
    84.             holder = new ViewHolder();  
    85.             convertView = inflater.inflate(R.layout.listitem_app, null);  
    86.             holder.layout = (LinearLayout) convertView  
    87.                     .findViewById(R.id.gamelist_item_layout);  
    88.             holder.icon = (ImageView) convertView  
    89.                     .findViewById(R.id.app_icon);  
    90.             holder.name = (TextView) convertView  
    91.                     .findViewById(R.id.app_name);  
    92.             holder.size = (TextView) convertView  
    93.                     .findViewById(R.id.app_size);  
    94.             holder.btn = (Button) convertView  
    95.                     .findViewById(R.id.download_btn);  
    96.             convertView.setTag(holder);  
    97.         } else {  
    98.             holder = (ViewHolder) convertView.getTag();  
    99.         }  
    100.   
    101.         // 这里position和app.id的值是相等的  
    102.         final AppFile app = dataList.get(position);  
    103.         //Log.e("", "id="+app.id+", name="+app.name);  
    104.   
    105.         holder.name.setText(app.name);  
    106.         holder.size.setText((app.downloadSize * 100.0f / app.size) + "%");  
    107.   
    108.         Drawable drawable = mContext.getResources().getDrawable(R.drawable.app_icon);  
    109.         holder.icon.setImageDrawable(drawable);  
    110.   
    111.         switch(app.downloadState)  
    112.         {  
    113.         case DownloadManager.DOWNLOAD_STATE_NORMAL:  
    114.             holder.btn.setText("下载");  
    115.             this.changeBtnStyle(holder.btn, true);  
    116.             break;  
    117.         case DownloadManager.DOWNLOAD_STATE_DOWNLOADING:  
    118.             holder.btn.setText("下载中");  
    119.             this.changeBtnStyle(holder.btn, false);  
    120.             break;  
    121.         case DownloadManager.DOWNLOAD_STATE_FINISH:  
    122.             holder.btn.setText("已下载");  
    123.             this.changeBtnStyle(holder.btn, false);  
    124.             break;  
    125.         case DownloadManager.DOWNLOAD_STATE_WAITING:  
    126.             holder.btn.setText("排队中");  
    127.             this.changeBtnStyle(holder.btn, false);  
    128.             break;  
    129.         }  
    130.         holder.btn.setOnClickListener(new OnClickListener() {  
    131.             @Override  
    132.             public void onClick(View v) {  
    133.                 DownloadFile downloadFile = new DownloadFile();  
    134.                 downloadFile.downloadID = app.id;  
    135.                 downloadFile.downloadState = DownloadManager.DOWNLOAD_STATE_WAITING;  
    136.                 app.downloadState = DownloadManager.DOWNLOAD_STATE_WAITING;  
    137.                 downloadFile.downloadSize = app.downloadSize;  
    138.                 downloadFile.totalSize = app.size;  
    139.                 holder.btn.setText("排队中");  
    140.                 changeBtnStyle(holder.btn, false);  
    141.                 downloadManager.startDownload(downloadFile);  
    142.             }  
    143.         });  
    144.         return convertView;  
    145.     }  
    146.   
    147.     static class ViewHolder {  
    148.         LinearLayout layout;  
    149.         ImageView icon;  
    150.         TextView name;  
    151.         TextView size;  
    152.         Button btn;  
    153.     }  
    154.       
    155.       
    156.     private Handler mHandler = new Handler() {  
    157.           
    158.         public void handleMessage(Message msg)  
    159.         {  
    160.             DownloadFile downloadFile = (DownloadFile)msg.obj;  
    161.             AppFile appFile = dataList.get(downloadFile.downloadID);  
    162.             appFile.downloadSize = downloadFile.downloadSize;  
    163.             appFile.downloadState = downloadFile.downloadState;  
    164.               
    165.             // notifyDataSetChanged会执行getView函数,更新所有可视item的数据  
    166.             //notifyDataSetChanged();  
    167.             // 只更新指定item的数据,提高了性能  
    168.             updateView(appFile.id);  
    169.         }  
    170.     };  
    171.       
    172.     // 更新指定item的数据  
    173.     private void updateView(int index)  
    174.     {  
    175.         int visiblePos = listView.getFirstVisiblePosition();  
    176.         int offset = index - visiblePos;  
    177.         //Log.e("", "index="+index+"visiblePos="+visiblePos+"offset="+offset);  
    178.         // 只有在可见区域才更新  
    179.         if(offset < 0) return;  
    180.           
    181.         View view = listView.getChildAt(offset);  
    182.         final AppFile app = dataList.get(index);  
    183.         ViewHolder holder = (ViewHolder)view.getTag();  
    184.         //Log.e("", "id="+app.id+", name="+app.name);  
    185.   
    186.         holder.name.setText(app.name);  
    187.         holder.size.setText((app.downloadSize * 100.0f / app.size) + "%");  
    188.         Drawable drawable = mContext.getResources().getDrawable(R.drawable.app_icon);  
    189.         holder.icon.setImageDrawable(drawable);  
    190.   
    191.         switch(app.downloadState)  
    192.         {  
    193.         case DownloadManager.DOWNLOAD_STATE_DOWNLOADING:  
    194.             holder.btn.setText("下载中");  
    195.             this.changeBtnStyle(holder.btn, false);  
    196.             break;  
    197.         case DownloadManager.DOWNLOAD_STATE_FINISH:  
    198.             holder.btn.setText("已下载");  
    199.             this.changeBtnStyle(holder.btn, false);  
    200.             break;  
    201.         }  
    202.   
    203.     }  
    204. }  



    布局文件listitem_app.xml:

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    3.     android:id="@+id/gamelist_item_layout"  
    4.     android:layout_width="fill_parent"  
    5.     android:layout_height="wrap_content"  
    6.     android:gravity="center_vertical"  
    7.     android:background="@drawable/style_listitem_background"  
    8.     android:paddingBottom="5dp"  
    9.     android:paddingTop="5dp" >  
    10.    
    11.     <ImageView  
    12.         android:id="@+id/app_icon"  
    13.         android:layout_width="53dip"  
    14.         android:layout_height="53dip"  
    15.         android:layout_marginLeft="5dip"  
    16.         android:adjustViewBounds="false"  
    17.         android:padding="5dp" />  
    18.    
    19.     <LinearLayout  
    20.         android:layout_width="match_parent"  
    21.         android:layout_height="60dp"  
    22.         android:layout_marginLeft="5dp"  
    23.         android:layout_weight="1"  
    24.         android:gravity="center_vertical"  
    25.         android:orientation="vertical" >  
    26.    
    27.         <TextView  
    28.             android:id="@+id/app_name"  
    29.             android:layout_width="wrap_content"  
    30.             android:layout_height="wrap_content"  
    31.             android:singleLine="true"  
    32.             android:text=""  
    33.             android:textColor="#000000"  
    34.             android:textSize="13sp" />  
    35.    
    36.         <TextView  
    37.             android:id="@+id/app_size"  
    38.             android:layout_width="wrap_content"  
    39.             android:layout_height="wrap_content"  
    40.             android:textColor="#000000"  
    41.             android:textSize="10sp" />  
    42.    
    43.     </LinearLayout>  
    44.    
    45.     <Button  
    46.         android:id="@+id/download_btn"  
    47.         android:layout_width="55dip"  
    48.         android:layout_height="30dip"  
    49.         android:layout_marginRight="10dip"  
    50.         android:background="@drawable/style_btn_download"  
    51.         android:focusable="false"  
    52.         android:text="@string/download"  
    53.         android:textColor="#ffffffff"  
    54.         android:textSize="12sp" />  
    55.    
    56. </LinearLayout>  


    listview中item样式文件style_listitem_background.xml:

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <selector xmlns:android="http://schemas.android.com/apk/res/android">  
    3.    <!-- 没有焦点时的背景颜色 -->  
    4.   <item android:state_window_focused="false"  >  
    5.       <shape>    
    6.         <gradient   
    7.             android:startColor="#ffffff"   
    8.             android:endColor="#E3E3E3"   
    9.             android:angle="-90" />    
    10.       </shape>   
    11.   </item>  
    12.    
    13.    <!-- 非触摸模式下获得焦点并单击时的背景颜色 -->      
    14.   <item android:state_focused="true" android:state_pressed="true"  
    15.         android:drawable="@drawable/bg_listview_item_selected" />  
    16.    <!--触摸模式下单击时的背景颜色  -->  
    17.   <item android:state_focused="false" android:state_pressed="true"  
    18.         android:drawable="@drawable/bg_listview_item_selected" />   
    19.    <!--选中时的背景颜色  -->  
    20.   <item android:state_selected="true"  android:drawable="@drawable/bg_listview_item_selected" />   
    21.   <!--获得焦点时的背景  颜色-->  
    22.   <item android:state_focused="true" android:drawable="@drawable/bg_listview_item_selected" />   
    23. </selector>  


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

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <selector xmlns:android="http://schemas.android.com/apk/res/android">  
    3.     <item android:state_pressed="true"  
    4.         android:drawable="@drawable/btn_download_pressed" />  
    5.     <item android:drawable="@drawable/btn_download_norm" />  
    6. </selector>  


    字符文件strings.xml:

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <resources>  
    3.     <string name="app_name">AndroidDownloadFile</string>  
    4.     <string name="download">下载</string>  
    5. </resources>  


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

    1. package com.alexzhou.downloadfile;  
    2.    
    3. import android.app.Activity;  
    4. import android.os.Bundle;  
    5. import android.util.SparseArray;  
    6. import android.widget.ListView;  
    7.    
    8. public class MainActivity extends Activity   
    9. {  
    10.     private SparseArray<AppFile> appList = new SparseArray<AppFile>();  
    11.    
    12.     private ListView listView;  
    13.    
    14.     @Override  
    15.     public void onCreate(Bundle savedInstanceState)  
    16.     {  
    17.         super.onCreate(savedInstanceState);  
    18.         setContentView(R.layout.activity_main);  
    19.         initData();  
    20.         initUI();  
    21.     }  
    22.    
    23.     private void initData()  
    24.     {  
    25.         for(int i =0; i<20; i++)  
    26.         {  
    27.             AppFile app = new AppFile();  
    28.             app.name = "快玩游戏--" + (i+1);  
    29.             app.size = 100;  
    30.             app.id = i;  
    31.             app.downloadState = DownloadManager.DOWNLOAD_STATE_NORMAL;  
    32.             app.downloadSize = 0;  
    33.             appList.put(app.id, app);  
    34.         }  
    35.     }  
    36.    
    37.     private void initUI()  
    38.     {  
    39.         listView = (ListView)this.findViewById(R.id.listview);  
    40.         AppListAdapter adapter = new AppListAdapter(this, appList);  
    41.         adapter.setListView(listView);  
    42.         listView.setAdapter(adapter);  
    43.     }  
    44.    
    45.     @Override  
    46.     protected void onDestroy() {  
    47.         super.onDestroy();  
    48.         DownloadManager.getInstance().stopAllDownloadTask();  
    49.     }  
    50.    
    51. }  


    布局文件activity_main.xml:

    1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    2. android:orientation="vertical"  
    3. android:layout_width="fill_parent"  
    4. android:layout_height="fill_parent"  
    5. >  
    6. <ListView  
    7. android:id="@+id/listview"  
    8. android:layout_width="fill_parent"  
    9. android:layout_height="fill_parent"  
    10. android:fastScrollEnabled="true"  
    11. />  
    12. </LinearLayout>  


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



    这里对比一下分别使用updateView和notifyDataSetChanged时,有什么不一样,看看打印日志:
    (1)使用notifyDataSetChanged时,listview可视范围内的所有子项都更新了。


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



    实例源码地址 : http://pan.baidu.com/share/link?shareid=229182&uk=167811495

    转载请注明来自:Alex Zhou的程序世界,本文链接:http://codingnow.cn/Android/1059.html

  • 相关阅读:
    博客搬迁
    android中listview的item滑动删除效果(已解决listview点击问题)
    来把博客园变成“原谅”的颜色
    Datatables后台服务器端分页、根据条件重新查询、主要技术问题
    JQuery双列表交互模态窗口,列表项互相、上下移动
    Angular页面刷新保存变量数据,运用localstorage
    Angular中使用datatable.js出现错误“ui-router TypeError: Cannot read property 'childNodes' of undefined”的解决方法
    使用swiper-animate.js制作h5宣传页
    使用JavaScript/JQuery 操作SVG元素的几个关键技巧
    mac os系统使用Visual Studio Code打开浏览器查看HTML文件
  • 原文地址:https://www.cnblogs.com/1995hxt/p/5795413.html
Copyright © 2020-2023  润新知