ListView嵌套ProgressBar更新方式

很多时候我们都通过BaseAdapter.getView()中的convertView来提高ListView的性能,这个时候如果我的的ListView的Item里有一个正在更新ProgressBar,结果就悲惨了。。。 滑动界面时并没有达到我们想要的效果。解决这个问题其实很容易,在数据集中保存一下更新的进度,然后在getView中不断去设置进度。

还有一个问题就是,当有进度更新的时候,我们是要不断mAdapter.notifyDatasetChanged()来更新ListView吗?这样做当然可以,但是效率极其低下,要知道notifyDatasetChanged的代价是非常高的。那么,有没有更好的方式呢?当然有,那就是手动更新item的方式。

下面,我们来一步步的实现一个最佳的更新方式,至于何时选用手动更新,何时选用notify,我认为在有数据频繁更新或者只需要更新一条数据的时候要选择前者,普通的情况还是选择notify更容易一些。

首先,我们来建立一个表示任务的实体类:

public class Task {
	private String name;
	private boolean isDownload;
	private int progress;

	public Task() {}

	public Task(String name) {
		this.name = name;
	}

	...
	setter and getter
	...
}

很容易理解,name表示任务的标题, isDownload表示是否正在下载,我们下面会通过isDownload来控制ProgressBar的显示和隐藏,progress当然就是进度了。

ListView的布局我们来看一下:

<ListView
        android:id="@+id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:cacheColorHint="@android:color/transparent"
        android:divider="@android:color/darker_gray"
        android:dividerHeight="1dp"
        android:listSelector="@android:color/transparent" />

还有ListView的Item的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="10dp"
    android:paddingBottom="10dp"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/item_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <ProgressBar
        android:id="@+id/item_progress"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="invisible" />

</LinearLayout>

都是最简单的布局,我们直接略过,Item的布局包含一个标题和一个进度条。

如何来模拟下载过程呢? 看下面代码:

private void download(final int positionInAdapter) {
	...
	new Thread(new Runnable() {
		@Override
		public void run() {
			for (int i = 1; i < 101; i++) {
				final int progress = i;
				runOnUiThread(new Runnable() {
					@Override
					public void run() {
						publishProgress(positionInAdapter, progress);
					}
				});
				SystemClock.sleep(500);
			}
		}
	}).start();
}

这段代码的意思是每隔500ms更新一下进度,而positionInAdapter这个参数表示我们点击的这个item在Adapter中的位置(也就是在数据集中的位置)。

在来看看Adapter怎么写的,

public class MyAdapter extends BaseAdapter {
	private Context mContext;
	private ArrayList<Task> mTasks;

	public MyAdapter(Context context, ArrayList<Task> tasks) {
		mContext = context;
		mTasks = tasks;
	}

	@Override
	public int getCount() {
		return mTasks.size();
	}

	@Override
	public Object getItem(int position) {
		return mTasks.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		final ViewHolder holder;
		if(convertView == null) {
			convertView = View.inflate(mContext, R.layout.item, null);
			holder = new ViewHolder();
			holder.name = (TextView) convertView.findViewById(R.id.item_name);
			holder.progress = (ProgressBar) convertView.findViewById(R.id.item_progress);
			convertView.setTag(holder);
		}else {
			holder = (ViewHolder) convertView.getTag();
		}

		holder.name.setText(mTasks.get(position).getName());

		if(mTasks.get(position).isDownload()) {
			holder.progress.setVisibility(View.VISIBLE);
			holder.progress.setProgress(mTasks.get(position).getProgress());
		}else {
			holder.progress.setVisibility(View.INVISIBLE);
		}

		return convertView;
	}

	static class ViewHolder {
		TextView name;
		ProgressBar progress;
	}
}

一个最普通的Adapter写法,稍微值得注意的是40~45行,我们根据isDownload来判断是否显示和更新ProgressBar。

到这里我们的基本工作就算做完了,下面就是要在Activity中来更新ListView的item了,而不是通过notifyDatasetChanged方法。

在Activity中,我们通过ListView的ItemClick来实现模拟下载,在ItemClick中我们调用download方法,来看看download中我们省略的那些代码。

private void download(final int positionInAdapter) {
	mTasks.get(positionInAdapter).setDownload(true);
	if(positionInAdapter >= mListView.getFirstVisiblePosition() &&
			positionInAdapter <= mListView.getLastVisiblePosition()) {
		int positionInListView = positionInAdapter - mListView.getFirstVisiblePosition();
		ProgressBar item = (ProgressBar) mListView.getChildAt(positionInListView)
				.findViewById(R.id.item_progress);
		item.setVisibility(View.VISIBLE);
	}
	...
}

上来,我们先去更新一下task列表中表示下载的boolean变量,表示该条数据正在下载。

下面一个判断是关键,我们需要判断当前点击该条item是否在ListView的可见域内(这个判断其实是多余的,既然能点击到,肯定是可见了,但是为了严谨,我们还是加了这个判断),这里为什么要这么判断呢?来看下图。

在该图中,我们ListView第一个可见项对应在数据集中的位置应该是2,但是0和1是不可见的。所以,我们只需要判断一下,当前数据集中的位置是否在ListView.getFirstVisibleItem()和ListView.getLastVisibleItem()之间就可以判断出该item是否处于可见区域内了。

接下来第5行,我们通过positionInAdapter - mListView.getFirstVisibleItem()来获取当前item在ListView中的位置,如果我们点击对应图片中的4号位置,那么我们需要更新ListView中第4-2=2的item。现在我们又可以获取ListView的Item了,当然我们就可以获取该Item中的ProgressBar了,6~8行,我们精确的获取到了ProgressBar,并且更新ProgressBar为显示状态。

到目前为止,可以通过ItemClick来实现控制ProgressBar的显示了,当然,我们没有使用notifyDatasetChanged()。

接下来,我们来看看是如果更新进度的。更新进度的过程主要在publishProgress方法中。

public void publishProgress(final int positionInAdapter, final int progress) {
//	mTasks.get(positionInAdapter).setDownload(true);
	mTasks.get(positionInAdapter).setProgress(progress);

	if(positionInAdapter >= mListView.getFirstVisiblePosition() &&
			positionInAdapter <= mListView.getLastVisiblePosition()) {
		int positionInListView = positionInAdapter - mListView.getFirstVisiblePosition();
		ProgressBar item = (ProgressBar) mListView.getChildAt(positionInListView)
				.findViewById(R.id.item_progress);
		item.setProgress(progress);
	}
}

代码和上面的非常相似,其实原理也是一样的。

首先来看看这个方法的参数:positionInAdapter表示在数据集中的位置,progress表示进度,这个不难理解。

接下来,我们通过更新task任务列表中对应条目的progress来保存一下现在的进度,但是,我们没有notify。

接下来还是同样的逻辑,只不过,我们把控制ProgressBar显示的部分替换成了更新ProgressBar进度了。

这样,我们就实现了,在不调用notifyDatasetChanged()的情况下来更新ListView的Item的目的。这样做有一个很明显的好处就是,每次,我们只更新一条item,其他的item我们并没有去更新,而notifyDatasetChanged的实现方式是,保存当前的位置,并更新所有的item,然后恢复位置。这样一比较,我们这种方式的优势就体现出来了。

有人可能还会有疑问,我们在滑动的时候怎么处理呢? 别忘了,我们在更新的前面都在task列表中更新了数据,所以在滑动的时候,我们交给Adapter.getView()去处理了。

时间: 2024-10-12 08:39:48

ListView嵌套ProgressBar更新方式的相关文章

ListView混搭ProgressBar最佳更新方式

很多时候我们都通过BaseAdapter.getView()中的convertView来提高ListView的性能,这个时候如果我的的ListView的Item里有一个正在更新ProgressBar,结果就悲惨了... 滑动界面时并没有达到我们想要的效果.解决这个问题其实很容易,在数据集中保存一下更新的进度,然后在getView中不断去设置进度. 还有一个问题就是,当有进度更新的时候,我们是要不断mAdapter.notifyDatasetChanged()来更新ListView吗?这样做当然可

Android ListView嵌套ListView的实现方式

首先刚到北京一个月,产品经理让做一个类似于商城的东东,起初感觉没什么难度,(不就一个电子商务app嘛,以前也做过啊),但是当看到有需求是这样的 然后就开始做,起初太懒了,就在网上找,找到了一个ListView嵌套ListView的一哥们的讲解的大致思路的,然后根据那哥们的思路自己写了一个demo,感觉效果还挺好,不卡, 第一种实现方式:这种方式有个问题就像我项目中的问题,子列中的值如果是加减变化的,对应的每个父类的item的总价格会动态变化的话用此方式就会出现一定的问题,如果不需要实现像我项目图

Listview嵌套Viewpager实现仿淘宝搜狐广告主页,并实现listview的下拉刷新

Android实现功能:Listview嵌套viewpager仿淘宝搜狐视频主页面,和listview的下拉刷新. 什么都不说了:直接上图说效果 listview嵌套viewpager实现仿淘宝的广告滑动主页面 源码连接:(http://download.csdn.net/detail/qq_30000411/9528977) APK下载连接:(http://download.csdn.net/detail/qq_30000411/9528973) 下面给出我源码的主要文件构成: MyListV

C#如何解决对ListView控件更新以及更新时界面闪烁问题

第一个问题:如何更新ListView控件内容 很多时候运行窗体程序时,由于程序中使用了多线程加之操作不当,所以在对控件操作时会出现下面这样的异常: 这是因为我们在窗体中添加的控件都有属于自己的线程,所以不能从其它线程来访问它. 那要如何解决? 使用委托:MethodInvoker,我用这个挺方便的.下面举一个用例: 1 //调用InitListView(),便可以对ListView控件进行自由更新啦 2 private void InitListView() 3 { 4 MethodInvoke

Android零基础入门第44节:ListView数据动态更新

经过前面几期的学习,关于ListView的一些基本用法大概学的差不多了,但是你可能发现了,所有ListView里面要填充的数据都是静态的,但在实际开发中,这些数据往往都是动态变化的,比如数据内容发生改变.增加几行.或者删除几行,这就涉及到ListView数据的更新问题. 接下来通过一个简单的示例程序来学习ListView的数据更新. 继续使用WidgetSample工程,在app/main/res/layout/目录下创建updatedata_layout.xml文件,在其中填充如下代码片段:

ListView嵌套ListView优化

在做业务时候,一个ListView显示多种数据类型,我们想到的方法是ListView在嵌套一个ListView,对于子ListView 占父ListView的一行,就解决了问题,但是这样的逻辑是不是有点奇怪呢? 第一,父ListView的长度size需要加1,因为增加了一行放子listView public int getCount() { return datas.size()+1; } 第二,需要判断在什么地方插入子listView ,就要用到getViewTypeCount()和getIt

android滑动组件嵌套一般思路,多任务手势思路,触摸传递思路,【例】listview嵌套viewpager

在android UI开发中,我们经常会遇到这种需求: 两个支持滑动的组件,比如listview嵌套多个listview,listview的item是一个viewpager或gallary?亦或是scrollview嵌套scrollview等等. 一般情况下,你还可能需要支持如下几种功能: ¤ 两层组件都可以滑动 ¤ 不让两个组件同时滑动,或者让两个组件同时滑动并可以自己调节 ¤ 不影响底层view的子view和嵌套view的子view的点击事件 实现上述功能时,我们也经常遇到一些问题: ¤ 点

android listView嵌套gridview的使用心得

在开发的过程中可能需要用到listview嵌套gridview的场景,但是在Android中,不能在一个拥有Scrollbar的组件中嵌入另一个拥有Scrollbar的组件,因为这不科学,会混淆滑动事件,导致只显示一到两行数据.那么就换一种思路,首先让子控件的内容全部显示出来,禁用了它的滚动.如果超过了父控件的范围则显示父控件的scrollbar滚动显示内容,思路是这样,一下是代码.具体的方法是自定义GridView组件,继承自GridView.重载onMeasure方法: public cla

ListView嵌套GridView

首先,我们通过两个实例来了解下本篇文章所讲的重点,看下图: 微博: 陌陌: 大家应该对这两款软件并不陌生,接下来,我将列举下本文将要实现的几个点: 1.ListView嵌套GridView,互不冲突,界面显示及操作正常: 2.GridView中的图片依据屏幕大小,自动扩大或缩小以适应屏幕,并始终保持正方形: 3.图片数量不同时,布局不同(如上图的4张图片和6张图片): 4.点击ListView的item和点击Gridview的item互不冲突: 5.在GridView区域外的任何一点点击都可以触