Listview的使用与优化(上)

Listview几乎是最常见但是却比较难使用的控件了,这篇文章由浅入深讲解了listview的使用和优化。

首先来看listview的最简单使用,依赖ArrayAdapter

public class MyActivity extends Activity{
		
	@Override
	protected void onCreate(Bundle savedInstanceState) {		
		super.onCreate(savedInstanceState);			
		setContentView(R.layout.activity_main);
		ListView mylist = (ListView) findViewById(R.id.mylist);
		ArrayList<String> arr = new ArrayList<String>();
		Collections.addAll(arr, new String[]{"ssss","asdas","wqeqweqw",
				"ewrrcxxx","ssss","asdas","wqeqweqw","ewrrcxxx"});		
                //设置适配器
		mylist.setAdapter(new ArrayAdapter(this, android.R.layout.simple_expandable_list_item_1, 
				arr));
	}
}

然后是调用 ContentProvider作为数据源的使用,下来以电话薄作为例子,依赖 SimpleCursorAdapter

public class MyActivity extends Activity{
		
	@Override
	protected void onCreate(Bundle savedInstanceState) {		
		super.onCreate(savedInstanceState);			
		setContentView(R.layout.activity_main);
		ListView mylist = (ListView) findViewById(R.id.mylist);
		
		Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);
		//让Activity替我们管理cursor,使他们同生命周期
		startManagingCursor(cursor);
		//配置SimpleCursorAdapter
		mylist.setAdapter(
		new SimpleCursorAdapter(this, android.R.layout.simple_expandable_list_item_1, 
				cursor,new String[]{People.NAME}, new int[]{android.R.id.text1}));
	}
}
最后是更为复杂的布局,包括imageview,textview

public class MyActivity extends Activity{
		
	@Override
	protected void onCreate(Bundle savedInstanceState) {		
		super.onCreate(savedInstanceState);			
		setContentView(R.layout.activity_main);
		ListView mylist = (ListView) findViewById(R.id.mylist);
		
		/**
		 * 其中string[]数组为map的key值
		 * int[]数组为对应控件ID
		 */
		SimpleAdapter adapter = new SimpleAdapter(this,getData(),R.layout.vlist,
				new String[]{"title","info","img"},
				new int[]{R.id.title,R.id.info,R.id.img});							
	
		mylist.setAdapter(adapter);
	}
	
	private List<Map<String, Object>> getData() {
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); 
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("title", "G1");
		map.put("info", "google 1");
		map.put("img", R.drawable.ic_launcher);
		list.add(map);
	
		map = new HashMap<String, Object>();
		map.put("title", "G2");
		map.put("info", "google 2");
		map.put("img", R.drawable.ic_launcher);
		list.add(map);
	
		map = new HashMap<String, Object>();
		map.put("title", "G3");
		map.put("info", "google 3");
		map.put("img", R.drawable.ic_launcher);
		list.add(map);
	
		return list;
	} 
}

从上面例子可以看出,使用SimpleAdapter,可以设置Imageview,需要做的是传入一个 List<Map<String,?>>,然后 后两个参数数组,分别对应map的key值和控件ID

在来看添加了选择框的listview

public class MyActivity extends Activity{
		
	@Override
	protected void onCreate(Bundle savedInstanceState) {		
		super.onCreate(savedInstanceState);			
		setContentView(R.layout.activity_main);
		ListView mylist = (ListView) findViewById(R.id.mylist);
		ArrayList<String> arr = new ArrayList<String>();
		Collections.addAll(arr, new String[]{"ssss","asdas","wqeqweqw",
				"ewrrcxxx","ssss","asdas","wqeqweqw","ewrrcxxx"});
						
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 
				android.R.layout.simple_list_item_single_choice, 
				arr); 
		mylist.setAdapter(adapter);
		mylist.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
		
	}		
}
上面代码中有一个setChoiceMode来设置选择框是单选,其他选项还有 CHOICE_MODE_MULTIPLE(多选), CHOICE_MODE_NONE(默认), CHOICE_MODE_SINGLE(单选)

当然我们也可以在xml里面设置这个选项

android:choiceMode="multipleChoice" 
如果是多选按钮,我们还要为listview设置一个MultiChoiceModeListener。另外, 我们需要使用getCheckItemIds 来获得当前的选项id(不是position)。


以上都是listview的简单使用,不涉及交互,只是简单的UI展示,下面要说明自定义Adapter来展示更复杂的功能

首先说listview绘制视图的原理:系统首先调用getCount()函数,根据他的返回值得到ListView的长度,然后一条条的绘制

package com.test;

import java.util.List;

import org.w3c.dom.ls.LSInput;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter {

	List<String> mlist = null;
	Context mContext = null;		
	LayoutInflater mInflater; 
	ViewHolder holder;
	public MyAdapter(List<String> mlist, Context mContext) {
		super();
		this.mlist = mlist;
		this.mContext = mContext;
		mInflater = LayoutInflater.from(mContext);
	}

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

	@Override
	public String getItem(int position) {
		return mlist.get(position);
	}

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

	//静态内部类
	static class ViewHolder{
        ImageView img;
        TextView text;
    }  
	
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		View view = convertView;
		//缓存机制
		if(view == null){
			view = mInflater.inflate(R.layout.vlist, null);
			holder = new ViewHolder();
	        holder.text = (TextView) view.findViewById(R.id.title);
	        holder.img = (ImageView) view.findViewById(R.id.img);	        
	        view.setTag(holder);
		}else{
			//减少了findviewbyid()
			holder = (ViewHolder)view.getTag(); 
		}
		//为控件设置值
		holder.text.setText(mlist.get(position));
		holder.img.setImageResource(R.drawable.ic_launcher);
		return view;
	}	
}


上面代码有几个优化的地方:

1,使用convertView进行视图缓存,从而减少inflate()操作

对XML布局文件的inflate操作开销大,尽管为了更高效的解析,布局文件被编译成了二进制形式,然而inflate()方法会调用rinflate()从根视图递归地遍历一整棵由XML块(包装编译后的XML文件)组成的树,并且实例化每个子视图。

adapter内部有一个Recycler的构件用于缓存视图,原理如下


2,使用viewHolder,getTag()方式来重新找到控件,减少findViewById()

其中viewHolder是一个静态内部类,静态类只会在第一次加载时会耗费比较长时间,但是后面就可以很好帮助加载,同时保证了内存中只有一个ViewHolder,节省了内存的开销。

而使用getTag()方法可以更加快捷地找到控件,减少了findviewbyid();


下面再提几点listview的优化:

1,ExpandableListView 与 ListActivity 由官方提供的,里面要使用到的ListView是已经经过优化的ListView,如果大家的需求可以用Google自带的ListView满足的的话尽量用官方的,绝对没错!

2,对于图片的优化,这个非常重要,可以写另外一篇文章来说明,总的来说就是使用弱引用,缓存机制,异步加载

3,快速滑动时不显示图片。当快速滑动列表时(SCROLL_STATE_FLING),item中的图片或获取需要消耗资源的view,可以不显示出来;而处于其他两种状态(SCROLL_STATE_IDLE 和SCROLL_STATE_TOUCH_SCROLL),则将那些view显示出来

4,避免listview周围控件的大小变化,引起listview的变化,因为一旦listview大小变化时,就会产生重绘


getView()方法已经做了优化,所以很多聪明的想法其实是不必要的,例如:

(1)本地缓存视图,当某个位置总是显示同样元素的时候,便把该位置对应的视图缓存起来供下次同样位置的请求使用。
(2)猜测getView()调用顺序,因为getView()的调用顺序是没有保证的(为了提高性能),有几种方法展示ListView,有时候从底部开始,有时候从顶部开始,有时候从中间开始,也就是说你得到的最后一个视图未必是屏幕最下方的视图。

listview的基本优化就是以上,但是我还有提一个listview根据需要加载不同项目布局(item layout)的情况。

你需要做这些:

  1. 重(@Override)写 getViewTypeCount() – 返回你有多少个不同的布局
  2. 重写 getItemViewType(int) – 由position返回view type id
  3. 根据view item的类型,在getView中创建正确的convertView
下面是一个简单例子:
package com.test;

import java.util.List;

import org.w3c.dom.ls.LSInput;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MyAdapter extends BaseAdapter {

	List<String> mlist = null;
	Context mContext = null;		
	LayoutInflater mInflater; 
	ViewHolder holder;
	public MyAdapter(List<String> mlist, Context mContext) {
		super();
		this.mlist = mlist;
		this.mContext = mContext;
		mInflater = LayoutInflater.from(mContext);
	}
	
	/**
	 * 该方法返回不同item_layout的数目
	 */
	@Override
	public int getViewTypeCount() {		
		return 2;
	}
	
	/**
	 * 返回对应position的item_layout类型
	 */
	@Override
	public int getItemViewType(int position) {		
		//这里用奇偶数来区分
		return position%2;
	}
	
	@Override
	public int getCount() {
		return mlist.size();		
	}

	@Override
	public String getItem(int position) {
		return mlist.get(position);
	}

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

	//静态内部类
	static class ViewHolder{
        ImageView img;
        TextView text;
    }  
	
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		View view = convertView;
		int type = getItemViewType(position);
		//缓存机制
		if(view == null){
			//根据不同的type采用不同的布局
			switch(type){
			case 0:
				view = mInflater.inflate(R.layout.vlist, null);
				holder = new ViewHolder();
	        	holder.text = (TextView) view.findViewById(R.id.title);
	        	holder.img = (ImageView) view.findViewById(R.id.img);	        
	        	view.setTag(holder);
	        	break;
			case 1:
				view = mInflater.inflate(R.layout.vlist2, null);
				holder = new ViewHolder();
	        	holder.text = (TextView) view.findViewById(R.id.title);
	        	holder.img = (ImageView) view.findViewById(R.id.img);	        
	        	view.setTag(holder);
	        	break;
			}
		}else{
			//减少了findviewbyid()
			holder = (ViewHolder)view.getTag(); 
		}
		//为控件设置值
		holder.text.setText(mlist.get(position));
		holder.img.setImageResource(R.drawable.ic_launcher);
		return view;
	}	
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值