`
zhiweiofli
  • 浏览: 515031 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

探秘ListView的ConvertView以及ViewHolder原理

阅读更多

最近在使用android的组件ExpandableListView,实现需求时,遇到不少奇怪的问题。

我的需求就是在ExpandableListView的父一级的view上添加两个按钮,实现添加子一级view的添加一级删除自己的功能。

 

自定义的layout如下:

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_parentView"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/iv_flag"
        android:layout_width="10dp"
        android:layout_height="10dp"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:layout_marginLeft="5dp"
        android:focusable="false" />

    <TextView
        android:id="@+id/tv_typename"
        android:layout_width="100dp"
        android:layout_height="30dp"
        android:layout_centerInParent="true"
        android:layout_marginLeft="10dp"
        android:focusable="false" />

    <ImageButton
        android:id="@+id/iv_dele"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="5dp"
        android:src="@drawable/del" 
        android:focusable="false"/>

    <ImageButton
        android:id="@+id/iv_add"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginRight="10dp"
        android:layout_toLeftOf="@id/iv_dele"
        android:src="@drawable/add" 
        android:focusable="false"/>

</RelativeLayout>

 

GroupView部分的Java代码如下:

 

	@Override
	public View getGroupView(int groupPosition, boolean isExpanded,
			View convertView, ViewGroup parent) {
	
		View parentView = null;

		if(groupViews.size()>groupPosition){
			parentView = groupViews.get(groupPosition);
		} else {
			parentView = newParentView(groupPosition);
			groupViews.add(parentView);
		}
		
		return parentView;

	}
	
	private View newParentView(int groupPosition){ 
		
		View parentView = li_parent.inflate(R.layout.list_parent_view, null);

		TextView textview = (TextView) parentView.findViewById(R.id.tv_typename);

		BigType bt = groupList.get(groupPosition);
		textview.setText(bt.getName());
		
		View v_dele = parentView.findViewById(R.id.iv_dele);
		v_dele.setTag(bt.getName());
		v_dele.setOnClickListener(new BtnDeleBigTypeListener(ct));

		View v_add = parentView.findViewById(R.id.iv_add);
		v_add.setTag(bt.getName());
		v_add.setOnClickListener(new BtnAddSmallTypeListener(ct));
		
		return parentView;
	}
 

当Activity加载该layout后,当没有展开子一级,父一级上的两个ImageButton按钮均点击正常,可是当点开子一级后,点击两个ImageButton,均得不到立即的响应 非得再产生了其他控件的点击事件(例如点击父一级,让其收起)后,之前点击 ImageButton的事件才会得到响应。一开始感觉是事件响应队列阻塞,可是为啥在产生新的事件后,所有的事件又会马上得到响应呢?

 

先做一个测试,代码见下,代码里的ViewHolder是一个自定义的类,也是后面ViewHolder模式的主要体现,这里先卖一关子...

 

	View cache = null;
       @Override
	public View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {
		
		ViewHolder _vh = null;
		if(convertView == null){ 
			_vh = new ViewHolder();
			convertView = newParentView(_vh);
			convertView.setTag(_vh);
			
			cache = convertView;
			
		} else {
			_vh = (ViewHolder) convertView.getTag();
			
			if(cache.equals(convertView)){
				Log.i(TAG, "equals old obj");
			}
		}
		
		return convertView;

	}

  上面代码主要就是想测试converView,究竟是拿来干什么的,google文档里头做了如此解析:


大致意思就是在重复利用此物之前要检查其是否为null!可是就没说清楚,这个convertView究竟是什么时候会存在!

 

现在来看看测试的结果,ExpandableListView总共有4个父节点,但是有以下三次输出结果:


这表明,ExpandableListView每次都在重画通过getGroupView拿到的View,而且只创建一次,之后都是都是同一个!这下子明白了,前面不能响应的按钮点击事件,原来是convertView这一覆盖层在搞鬼!因为我在代码里头每次都是在newPareView的,结果原来的convertView还在,结果就盖住了新的view,导致事件流分派出现问题,当再有convertView以外的事件发生,或ExpandableListView刷新,事件流恢复正常,响应也就正常了...

 

看来有时候真的要好好研究理解清楚API更深层次的用意才好做开发 啊!

 

不过话又说回来,其实ExpandableListView(包括ListView)的这个convertView是很有用的,因为ExpandableListView只是根据getGroupView返回的view去画列表,但是一般list列表的内容在同一级基本都会有共通性,只是加载的信息内容不同,所以重用convertView,可以很大程度上提升ListView的性能,特别是在加载大数据量时。这里配合ViewHolder模式,就可以更好地对久对象进行重用了!

 

下面是改良过得代码:

 

	@Override
	public View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {
		
		ViewHolder _vh = null;
		if(convertView == null){ 
			_vh = new ViewHolder();
			convertView = newParentView(_vh);
			convertView.setTag(_vh);
			
		} else {
			_vh = (ViewHolder) convertView.getTag();
		}
		
		BigType bt = groupList.get(groupPosition);
		String name = bt.getName();
		_vh.getTitle().setText(name);
		
		ImageButton _btn_dele = _vh.getBtn_dele();
		_btn_dele.setTag(name);
		_btn_dele.setFocusable(false);
		_btn_dele.setClickable(true);
		_btn_dele.setOnClickListener(new BtnDeleBigTypeListener(ct));
		
		ImageButton _btn_add = _vh.getBtn_add();
		_btn_add.setTag(bt.getName());
		_btn_add.setFocusable(false);
		_btn_add.setClickable(true);
		_btn_add.setOnClickListener(new BtnAddSmallTypeListener(ct));
		
		return convertView;

	}

	private View newParentView(ViewHolder vh){
		
		View parentView = li_parent.inflate(R.layout.list_parent_view, null);

		ImageView iv_flag = (ImageView)parentView.findViewById(R.id.iv_flag);
		vh.setImg_flag(iv_flag);
		
		TextView tv = (TextView) parentView.findViewById(R.id.tv_typename);
		vh.setTitle(tv);

		ImageButton btn_dele = (ImageButton) parentView.findViewById(R.id.iv_dele);
		vh.setBtn_dele(btn_dele);

		ImageButton btn_add = (ImageButton) parentView.findViewById(R.id.iv_add);
		vh.setBtn_add(btn_add);
		
		return parentView;
	}	
 

其实ViewHolder并不是什么神奇的工具,他只是一个简单的vo,保存着父一级view上个控件的应用,方便在重用时,加载新的内容而已。

 

class ViewHolder {
	
    private TextView title;  
    private ImageView img_flag;
    private ImageButton btn_add;
    private ImageButton btn_dele;
    
	public TextView getTitle() {
		return title;
	}
	public void setTitle(TextView title) {
		this.title = title;
	}
	public ImageView getImg_flag() {
		return img_flag;
	}
	public void setImg_flag(ImageView img_flag) {
		this.img_flag = img_flag;
	}
	public ImageButton getBtn_add() {
		return btn_add;
	}
	public void setBtn_add(ImageButton btn_add) {
		this.btn_add = btn_add;
	}
	public ImageButton getBtn_dele() {
		return btn_dele;
	}
	public void setBtn_dele(ImageButton btn_dele) {
		this.btn_dele = btn_dele;
	}
}   
 

 

  • 大小: 19.3 KB
  • 大小: 5.9 KB
分享到:
评论
1 楼 anyang763 2011-11-22  

相关推荐

    listview适配器 ListViewAdapter和ViewHolder代码.rar

    通过这个例子,你可以学习如何创建自定义的ListViewAdapter,理解ViewHolder的工作原理,并将其应用于实际项目中。同时,也可以尝试扩展这个适配器,例如添加点击事件监听、支持不同类型的列表项等,以适应更复杂的...

    ListViewAdapter和ViewHolder代码,安卓listview适配器.rar

    本资源包含了一个关于ListViewAdapter和ViewHolder的代码示例,帮助开发者更好地理解和实现这个功能。 **ListViewAdapter详解** ListViewAdapter是Android SDK提供的一个抽象类,它是BaseAdapter的子类。开发者需要...

    ListView使用ViewHolder模式提高效率

    "ListView使用ViewHolder模式提高效率"这一主题就是解决这个问题的关键知识点。 ViewHolder模式是Android开发中为了提升ListView性能而设计的一种优化策略。它通过减少findViewById的调用来改善视图的绘制速度。在...

    ListView的优化使用-抽取ViewHolder以及adapter类

    本篇将深入探讨如何通过抽取ViewHolder和优化Adapter来提升ListView的性能。 ViewHolder模式是解决ListView滚动卡顿的关键。在ListView的onCreateViewHolder和onBindViewHolder方法中,ViewHolder充当了一个缓存的...

    打造万能的ListView适配器(ViewHolder+CommonAdapter)

    为了提高ListView的性能和用户体验,开发者通常会采用`ViewHolder`模式和自定义`Adapter`,如`CommonAdapter`。本文将深入探讨如何利用这两种技术打造一个万能的ListView适配器。 首先,`ViewHolder`模式是优化...

    万能listView ViewHolder适配器

    - 在getView()方法中,首先根据position获取ViewHolder对象,如果convertView的Tag没有ViewHolder,就创建一个新的ViewHolder并将其设置为convertView的Tag。 - 然后,根据数据源中的数据填充ViewHolder中的组件,...

    listView中多个listItem布局时,convertView缓存及使用

    在Android开发中,ListView是一种常见的组件,用于展示大量的...开发者应熟练掌握convertView的使用,以及结合ViewHolder和多布局策略来优化ListView的性能。在实际开发中,合理运用这些技巧,可以显著提升用户体验。

    MyListView

    本文将深入探讨ListView的BaseAdapter用法,以及如何利用convertView与ViewHolder优化性能。 一、ListView基础 ListView是Android提供的一个可以显示大量数据的滚动视图,通过动态加载的方式,只显示当前可视区域...

    listview中的单选按钮多选按钮

    首先,我们需要理解ListView的工作原理。ListView通过ViewHolder模式优化性能,复用已创建的列表项视图(View)。这意味着当用户滚动列表时,已显示过的视图会被重新使用,因此我们需要确保在每个视图被重新显示时...

    ListView ,GridView 通用Adapter ViewHolder

    这个类可能包含了创建ViewHolder的方法,以及处理数据绑定和视图复用的逻辑。通过这种方式,无论数据类型如何变化,Adapter都能够灵活应对,同时保证列表的高性能和良好的用户体验。 总之,通用Adapter和ViewHolder...

    android_listView组件的使用_添加viewHolder机制

    本篇文章将深入探讨ListView的基本用法,数据源的添加,以及优化性能的ViewHolder机制。 首先,ListView的基础使用主要包括以下几个步骤: 1. **布局文件**:在XML布局文件中,我们需要创建一个ListView标签,并为...

    在ListView中嵌套Switch开关,点击ListView条目来切换Switch的状态

    - 为了提高ListView的滚动性能,通常会在`getView()`方法中使用convertView和ViewHolder模式。ViewHolder持有列表项中各个控件的引用,避免频繁的findViewById操作。 4. **Switch的使用**: - Switch是Android的...

    ViewHolder超级简洁写法

    本篇将详细讲解ViewHolder的超级简洁写法,以及如何通过这个设计模式优化ListView的性能。 ViewHolder模式的核心思想是减少findViewById()的调用,因为这个方法在执行时会进行大量的查找操作,消耗时间。通过复用已...

    ListView的单一布局优化demo

    使用了convertView和viewHolder两种优化方式,ListView的子布局是单一布局,对应的我的博客地址是: http://blog.csdn.net/u012320459/article/details/47667869

    Android ListView多种布局优化demo

    Android ListView多种布局优化demo,使用了两种优化手段,包括convertView,ViewHolder,对应的我的博客地址是: http://blog.csdn.net/u012320459/article/details/47667869

    android listview viewholder

    我们在使用ListView的时候,经常会使用的ViewHolder方式作为缓存,每次都需要手动的通过viewholder.icon = convertview.findVIewByid(id);一般的app都会有好几个页面会用到ListView,这样的话我们的代码量就会很大,...

    无废话Android 系列教程36-1 [模仿listview的工作原理&测试]

    为了测试ListView的工作原理,我们可以创建一个简单的应用,包含一个ListView,以及一个自定义Adapter。Adapter的数据源可以是一个数组或者数据库查询的结果。然后在`getView()`方法中,模拟数据填充和视图复用的...

    android BaseAdapter listView优化

    本篇文章将深入探讨ListView与BaseAdapter的工作原理,并详细介绍如何进行性能优化。 首先,我们来理解一下Adapter在Android中的作用。Adapter是连接数据源和UI组件的桥梁,它负责将数据转化为可显示的视图。在...

    android listview

    2. ViewHolder模式:利用convertView和ViewHolder,减少视图的创建和查找,提高列表滑动的流畅性。 3. 列表项复用:避免不必要的对象创建,比如在getView()方法中,通过判断convertView是否为空来重用已存在的视图。...

    ViewHolder的应用

    在Android开发中,ViewHolder模式是优化ListView性能的关键技术之一,特别是在处理大数据量列表时,能够显著提升滚动流畅性。本文将深入探讨ViewHolder的应用及其在ListView和Adapter中的作用。 首先,我们来理解...

Global site tag (gtag.js) - Google Analytics