您的位置:首页 > 移动开发 > Android开发

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

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

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

[java] view plain copy

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,代表下载中的文件,源码:

[java] view plain copy

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要好。下面看源码:

[java] view plain copy

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的数据。源码:

[java] view plain copy

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:

[html] view plain copy

<?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:

[html] view plain copy

<?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:

[html] view plain copy

<?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:

[html] view plain copy

<?xml version="1.0" encoding="utf-8"?>

<resources>

<string name="app_name">AndroidDownloadFile</string>

<string name="download">下载</string>

</resources>

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

[java] view plain copy

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:

[html] view plain copy

<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

转载请注明来自:Alex
Zhou的程序世界,本文链接:http://codingnow.cn/Android/1059.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: