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

转自:http://codingnow.cn/android/1059.html 


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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,代表下载中的文件,源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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要好。下面看源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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:alexzhou
email :zhoujiangbohai@163.com
date  :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<DownloadFile> downloadFiles = new SparseArray<DownloadFile>();
     // 用来管理所有下载任务
     private ArrayList<DownloadTask> taskList = new ArrayList<DownloadTask>();
     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的数据。源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
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:alexzhou
email :zhoujiangbohai@163.com
date  :2013-1-27
 
app列表的数据适配器
  **/
 
public class AppListAdapter extends BaseAdapter {
 
     private SparseArray<AppFile> dataList = null ;
     private LayoutInflater inflater = null ;
     private Context mContext;
     private DownloadManager downloadManager;
     private ListView listView;
 
     public AppListAdapter(Context context, SparseArray<AppFile> 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:

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

listview中item样式文件style_listitem_background.xml:

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

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

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

字符文件strings.xml:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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<AppFile> appList = new SparseArray<AppFile>();
 
     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:

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

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

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

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

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


内容概要:文章基于4A架构(业务架构、应用架构、数据架构、技术架构),对SAP的成本中心利润中心进行了详细对比分析。业务架构上,成本中心是成本控制的责任单元,负责成本归集与控制,而利润中心是利润创造的独立实体,负责收入、成本利润的核算。应用架构方面,两者都依托于SAP的CO模块,但功能有所区分,如成本中心侧重于成本要素归集预算管理,利润中心则关注内部交易核算获利能力分析。数据架构中,成本中心与利润中心存在多对一的关系,交易数据通过成本归集、分摊利润计算流程联动。技术架构依赖SAP S/4HANA的内存计算ABAP技术,支持实时核算与跨系统集成。总结来看,成本中心利润中心在4A架构下相互关联,共同为企业提供精细化管理决策支持。 适合人群:从事企业财务管理、成本控制或利润核算的专业人员,以及对SAP系统有一定了解的企业信息化管理人员。 使用场景及目标:①帮助企业理解成本中心利润中心在4A架构下的运作机制;②指导企业在实施SAP系统时合理配置成本中心利润中心,优化业务流程;③提升企业对成本利润的精细化管理水平,支持业务决策。 其他说明:文章不仅阐述了理论概念,还提供了具体的应用场景技术实现方式,有助于读者全面理解并应用于实际工作中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值