Android实现类似ListView模式的回收和更新机制的瀑布流

博客地址:http://blog.csdn.net/u010593680/article/details/43771857(转载请保留原文地址)

项目地址:https://git.oschina.net/0-0Xuan/XWaterFall

分析问题:

在做项目中遇到了需要使用瀑布流的情况,于是便和往常一样使用ScroollView模式的瀑布流,但是瀑布流效果容易实现,可一旦加载大量图片,则一不了心就内存溢出了,而瀑布流往往需要添加大量的图片,内存管理可以说是必要之举,那么问题就来了,如何在瀑布流中进行内存管理呢?

解决方法一: 使用各种ImageLoader。将图片的Bitmap全都在ImageLoader中进行统计管理,如果图片使用内存超过限制了,则释放部分图片的Bitmap,但这里就有个显而易见的问题,即释放哪个图片的bitmap?这往往是最困难的一步,很多模式采取释放最近最少使用的Bitmap,但统计bitmap的使用本身就是一件比较难的事情,所以ImageLoader主要是用于快速高效的加载图片

解决方法二:使用自定义View,当View出现在手机屏幕上时,WindowsManager会调用View的onDraw方法来描绘View,这样即可实时的确定哪个View的图片需要使用,再加载对应的图片进内存,这样就可最大限度减少内存的使用,这个办法几乎能完美解决内存溢出的问题,但是,使用该方法需要在onDraw()方法调用时,再加载图片,那么加载图片的延迟将使用户体验大打折扣,当然也可以在代码中添加一些联系的代码,当某个View显示时,一并加载该View附近的View的图片,但如此一来各个View的联系就十分密切,代码编写难度会提高不少。

解决方法三:模拟ListView使得该瀑布流可以回收不显示的View。知道了哪些view没有被显示,那么只要回收这些View的图片的bitmap即可。

本博客主要介绍第三种解决方法!

实现目标:一、使waterfall能够发现正在屏幕中显示的View,并且能够设置滑动时,加载View的正向和反向需要缓存的View的个数,以提前加载View,使用户体验更好

实现目标二、使waterfall能实现类似于ListView的notifyDataSetChange()方法,使得调用notifyDataSetChange()后ListView将检查原来的View是否有更新,如果有更新则进行相应的更新。这样就能方便的修改、增加、删除waterfall中的View了。

XWaterFall的使用:

第一步继承XWaterFall,继承后会显示7个必需实现的方法:

/**
	 * notifyDataSetChange时调用来获取子View,不建议在此进行大量的内存操作,如图片bitmap的加载,应将该操作放置在<span style="font-family: Arial, Helvetica, sans-serif;">onResumeView(int position, View view)中</span>
	 *
	 * @param position
	 * @return 返回需要添加的View
	 */
	@Override
	public View getView(int position) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 如果能确定对应的View的高度则可在子类覆盖该方法
	 *
	 * @param position
	 * @return
	 */
	@Override
	public int getHeight(int position) {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * 返回position对应的View的标识,用以判断View是否需要更新
	 *
	 * @param position
	 * @return
	 */
	@Override
	public Object getMark(int position) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 *
	 * @return WaterFall中子View的数量
	 */
	@Override
	public int getContentChildCount() {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 *
	 * @return 一次notifyDataSetChanged()最多添加的View的个数
	 */
	@Override
	public int getNewChildNum() {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * 有新超出显示屏的View时调用该方法
	 *
	 * @param position
	 * @param view
	 */
	@Override
	protected void onRecycleView(int position, View view) {
		// TODO Auto-generated method stub

	}

	/**
	 * 有新进入显示屏的View时调用该方法
	 *
	 * @param position
	 * @param view
	 */
	@Override
	protected void onResumeView(int position, View view) {
		// TODO Auto-generated method stub

	}

了解了方法的实现要求,我们现在来实现一个显示美女和美女position的瀑布流,实现效果如下:

实现的代码是:

public class ImageWaterFall extends XWaterFall {

	private String TAG = "ImageWaterFall";
	private Context context;
	private List<String> imgs;
	private LayoutInflater inflater;

	public ImageWaterFall(Context context, List<String> imgs) {
		super(context,3,0);//设置waterfall的context、列数、宽度
		// TODO Auto-generated constructor stub
		this.context = context;
		this.inflater = LayoutInflater.from(context);
		this.imgs = imgs;

	}

	@Override
	public View getView(int position) {
		// TODO Auto-generated method stub
		View view = inflater.inflate(R.layout.item_my_image, null);
		MyImageView iv = (MyImageView) view.findViewById(R.id.iv);
		iv.setName("美女"+position);
		TextView tv = (TextView) view.findViewById(R.id.tvName);
		tv.setText("美女: " + position);
		return view;
	}

	@Override
	public int getContentChildCount() {
		// TODO Auto-generated method stub
		return imgs.size();
	}

	@Override
	public int getNewChildNum() {
		// TODO Auto-generated method stub
		return 12;
	}

	@Override
	public void onScrollToBottom() {
		// TODO Auto-generated method stub
		notifyDataSetChanged();
	}

	@Override
	public Object getMark(int position) {
		// TODO Auto-generated method stub
		return imgs.get(position);
	}

	@Override
	protected void onRecycleView(int position, View view) {
		// TODO Auto-generated method stub
		MyImageView iv = (MyImageView) view.findViewById(R.id.iv);
		TextView tv = (TextView)view.findViewById(R.id.tvName);
		tv.setBackgroundColor(context.getResources().getColor(R.color.orange));
		String url = imgs.get(position);
		Bitmap bm = ImageLoader.getBitmap(url);
		if (bm != null && !bm.isRecycled())
			bm.recycle();
		iv.setImageBitmap(null);
		ImageLoader.deleteBitmap(url);
		android.view.ViewGroup.LayoutParams params = iv.getLayoutParams();
		params.height = iv.getHeight();
		params.width = iv.getWidth();
		iv.setLayoutParams(params);
		iv.recycle();
	}

	@Override
	protected void onResumeView(int position, View view) {
		// TODO Auto-generated method stub
		TextView tv = (TextView)view.findViewById(R.id.tvName);
		tv.setBackgroundColor(context.getResources().getColor(R.color.green));
		MyImageView iv = (MyImageView) view.findViewById(R.id.iv);
		ImageLoader.loadImg(imgs.get(position), iv);
		iv.resume();
	}

	@Override
	public int getChildHeight(int position) {
		// TODO Auto-generated method stub
		return 0;
	}

}

需要注意的是:

一、getChildHeight()中返回的是子View的高度,如果不能事先知道View的高度,则可以返回0或负数,那么会自动使用默认的选择最短列的算法

二、当发生View的更新和添加等操作,将会调用getView方法,但可能该View没在显示屏和缓冲区内,在这里耗费大量内存显然不合适,而当View第一次出现在显示屏和缓冲区中时,将会调用onResumeView()方法,所以onResumeView()更适合作为加载图片或其他非常耗费内存方法调用的地方

三、保证某个View的onResumeView()方法会在getView()之后,并且只有当View第一次出现在显示屏和缓冲区中时才会被调用

当某个View第一次离开显示屏和缓冲区中时,onRecycleView()会被调用

onResumeView()和onRecycleView()会被交替调用,并且保证先调用onResumeView(),才有可能调用onRecycleView()

在ImageWaterFall代码中也进行第三点的测试:当onResumeView()和onRecycleView()被调用时,会分别调用MyImageView的resume()和recycle()

public class MyImageView extends ImageView {

	private String TAG = "MyImageView";
	private String name;
	private int state; //用来检验onResumeView和onRecycleView交替执行,且先onResumeView再有onRecycleView

	public MyImageView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		 this.setOnClickListener(onclick);//XWaterFall不会影响上层View的OnClickListener等操作
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public void setImageBitmap(Bitmap bm) {
		// TODO Auto-generated method stub
		// Log.v(name, "setImageBitmap");
		super.setImageBitmap(bm);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		// Log.v(name, "onDraw :" + drawNum++ );
		try {
			super.onDraw(canvas);
		} catch (Exception e) {
			e.printStackTrace();
			Log.v(TAG, name);
		}
	}

	public void recycle(){
		Log.v(TAG, "XX回收" + name);
		state--;
		if(state!= 0){
			Log.v(TAG, "XX错误: state:" + state);
		}
	}

	public void resume(){
		Log.v(TAG, "XX恢复" + name);
		state++;
		if(state != 1){
			Log.v(TAG, "XX错误: state:" + state);
		}
	}

	OnClickListener onclick = new OnClickListener() {

		@Override
		public void onClick(View v) {
			// TODO Auto-generated method stub
			Log.v(TAG, "OnClickListener");
		}
	};

}

XWaterFall的实现弄好了,接下来就是具体的使用了:

第一步:创建:ImageWaterFall iwf = new ImageWaterFall(this, imgs);

第二步:添加到某容器里:llContent.addView(iwf);

第三步:更新ImageWaterfall中的内容:iwf.notifyDataSetChanged();

前面两步和普通的View的使用类似,第三步则是添加ImageWaterfall子View的操作

还可以设置正反方向上缓存的数量,设置较大的缓冲数量有助于减少用户等待查看的时间,改善用户体验

(手指向上滑,则准备显示的是下方的View,所以下方为正方向,上方为反方向,手指向下滑,则准备显示的是上方的View,所以上方为正方向,下方为反方向)

<span style="white-space:pre">	</span>iwf.setPositionCacheNum(18);//设置加载View时正向的缓存的View的数量
	iwf.setOppositeCacheNum(18);//设置加载View时反向的缓存的View的数量

接下来介绍如何更新、删除、插入View到ImageWaterFall中:

switch(view.getId()){
		case R.id.btRefrash://更新position为1的View

			imgs.set(1, "http://g.hiphotos.baidu.com/image/pic/item/1ad5ad6eddc451da9f2e8e8cb5fd5266d11632f8.jpg");
			iwf.notifyDataSetChanged();
			break;
		case R.id.btDelete://删除position为1的View
			imgs.remove(1);
			iwf.notifyDataSetChanged();
			break;
		case R.id.btInseart://在position为1的View前插入新View
			imgs.add(1, "http://h.hiphotos.baidu.com/image/pic/item/810a19d8bc3eb1350c58efbca41ea8d3fd1f441d.jpg");
			iwf.notifyDataSetChanged();
			break;
		}

注意上面的imgs对应的是ImageWaterFall中的private List<String> imgs;这个用法和ListView中的用法非常相似

XWaterFall的使用就介绍完毕了,源码在博客顶部,如有任何问题,欢迎反馈!!

时间: 2025-02-01 08:42:22

Android实现类似ListView模式的回收和更新机制的瀑布流的相关文章

Android开发:ListView、AdapterView、RecyclerView全面解析

目录 AdapterView简介 AdapterView本身是一个抽象类,AdapterView及其子类的继承关系如下图: 特征: AdapterView继承自ViewGroup,本质是个容器 AdapterView可以包含多个"列表项",并将这多个列表项以合适的形式展示 AdapterView显示的列表项内容由Adapter提供 它派生的子类在用法上也基本相似,只是在显示上有一定区别,因此把他们也归为一类. 由AdapterView直接派生的三个类: AbsListView.AbsS

Android — 长按ListView 利用上下文菜单(ActionMode) 进行批量事件处理

好久没写博客拉``````` 近期最终略微闲一点了``````` 无聊拿手机清理短信.发现批量事件的处理还是挺管用的`````` 那么自己也来山寨一记看看效果吧````` 闲话少说,首先,我们来看下手机自带的短信功能里运行批量删除时的效果: 然后  是我们自己简单山寨的效果:       模拟的操作过程非常easy,但也非常有代表性. 我们假定我们所处的场景为.进入一个存放联系人列表的界面. 于是,首先我们定义了一个进度框,模拟提示正在从网络上下载数据. 接着.当网络数据成功下载到移动设备上后,

Android UI学习 - ListView (android.R.layout.simple_list_item_1是个什么东西)

Android UI学习 - ListView 2010-06-20 18:21:35 标签:Android UI 移动开发 ListView ListActivity 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://android.blog.51cto.com/268543/336162 ListActivity ListActivity是一个专门显示ListView的Activity类,它内置了ListView对象,只要我

Android复杂自定义Listview实现

在Android中实现Listview对新人来说比较难以理解,本人看了若干文章后觉得可以使用以下思路来让新人更好理解(同时也做好记录,免得自己以后忘记). 可参考博客:http://cinderella7.blog.51cto.com/7607653/1281696  (这里用MVC的思想去理解Listview,个人认为还是不错的) http://blog.csdn.net/jueblog/article/details/11857281   (一个完整的实现) ----------------

Android PullToRefresh (ListView GridView 下拉刷新) 使用详解

Android PullToRefresh (ListView GridView 下拉刷新) 使用详解 标签: Android下拉刷新pullToRefreshListViewGridView 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38238749,本文出自:[张鸿洋的博客] 群里一哥们今天聊天偶然提到这个git hub上的控件:pull-to-r

【Android UI】ListView系列一(基础篇)

----–ListView基础 ListView是一个用于显示滚动的列表项的视图组.listview通过一个适配器adapter将数据以列表的形式展示出来.listview可以说是Android应用层开发非常重要的组件之一,几乎每个App都有它的身影,通过适配器可以使listview呈现出各种不同的样式和风格,作为Android开发人员必须熟练掌握. 下面介绍一下listview的一些常用属性: 1.divider属性:在列表条目之间显示的可绘制对象或颜色. 布局代码 <RelativeLayo

Android自定义类似ProgressDialog效果的Dialog

Android自定义类似ProgressDialog效果的Dialog. 方法如下: 1.首先准备两张自己要定义成哪样子的效果的图片和背景图片(也可以不要背景). 如我要的效果: 2.定义loading_dialog.xml布局文件(这里你也可以按自己的布局效果定义,关键是要有个imageView): [html] view plaincopy <?xml version="1.0" encoding="utf-8"?> <LinearLayout

Android ActionBar的Overlay模式如何不遮盖顶部内容的问题

关于actionbar的overlay模式请参考 如何让android的actionbar浮动且透明 一文.这篇文章讲的是如何在这种模式下让actionbar不遮住顶部的内容. 这 一般是这样的场景,在一个ListView显示图片的界面中,当ListView向下滑动的时候,actionbar是是浮动在GridView上面一层 的,但是当ListView滚动到顶部,顶部的内容是完全显示出来的,当然这种情况一般ActionBar我们会做成透明效果. 其实很多人都能想到的是,将ListView加上一个

Android自学历程—Builder()模式

前一篇文章,在学习OKHttp的时候遇到Builder pattern,当然那时候还不知道这是Builder模式,只是觉得奇怪怎么后面跟了好多个点,后来通过了解才明白这是Android 中的建造者模式.稍微学习过android的,一定用过AlertDialog.buider,说来惭愧!当时没写博客,也就局限于会用的阶层.通过这篇,希望能更加的了解. Builder Design pattern in Java 建造者设计模式在Java中是一种创造类型的模式.例如,用来创建对象,类似如Factor