ListView的使用用ViewHolder提升效率


public class

ListView

extends AbsListView
 
java.lang.Object
   ↳android.view.View
    ↳android.view.ViewGroup
     ↳android.widget.AdapterView<T extends android.widget.Adapter>
      ↳android.widget.AbsListView
       ↳android.widget.ListView

Class Overview

A view that shows items in a vertically scrolling list. The items come from theListAdapter associated with this view.

 

public class

ArrayAdapter

extends BaseAdapter
implements Filterable

java.lang.Object
   ↳android.widget.BaseAdapter
    ↳android.widget.ArrayAdapter<T>
Class Overview

A concrete BaseAdapter that is backed by an array of arbitrary objects. By default this class expects that the provided resource id references a single TextView. If you want to use a more complex layout, use the constructors that also takes a field id. That field id should reference a TextView in the larger layout resource.

However the TextView is referenced, it will be filled with the toString() of each object in the array. You can add lists or arrays of custom objects. Override the toString() method of your objects to determine what text will be displayed for the item in the list.

To use something other than TextViews for the array display, for instance, ImageViews, or to have some of data besides toString() results fill the views, overridegetView(int, View, ViewGroup) to return the type of view you want.

 

1、ListView的简单使用

Demo:

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="sunny.example.listviewsimple.MainActivity" >

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>

 

MainActivity.java

package sunny.example.listviewsimple;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends ActionBarActivity {
	
	private String[] data = {"Apple","Banana","Orange","Watermelon","Pear",
			"Grape","Pineapple","Strawberry","Cherry","Mango"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, data);
		ListView listView = (ListView)findViewById(R.id.list_view);
		listView.setAdapter(adapter);
	}
}


2、提升ListView的运行效率ViewHolder、自定义ListView的界面(例:包括一个ImageView和一个TextView)

 

上例一个item只能显示一段文本,现在自定义一个ListView的界面。自定义一个Fruit类,作为ListView适配器的适配类型。

Fruit.java

package com.example.listviewtest;

public class Fruit {
	private String name;
	private int imageId;
	public Fruit(String name,int imageId){
		this.name = name;
		this.imageId = imageId;
	}
	public String getName(){
		return name;
	}
	public int getImageId(){
		return imageId;
	}

}

Fruit类有两个字段,name表示水果的名字,iamgeId表示水果对应的图片资源id。

然后为ListView的item自定义一个布局

fruit_item.xml

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

    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="10dip"/>
</LinearLayout>
子item布局中定义一个ImageView用于显示水果图片,TextView用于显示水果名字。
创建一个自定义适配器FruitAdapter继承自ArrayAdapter,将泛型指定为Fruit类。
 
FruitAdapter中用到的类与方法:
public abstract class

LayoutInflater

extends Object
java.lang.Object
   ↳android.view.LayoutInflater

Class Overview

Instantiates a layout XML file into its corresponding View objects. It is never used directly. Instead, usegetLayoutInflater() orgetSystemService(String) to retrieve a standard LayoutInflater instance that is already hooked up to the current context and correctly configured for the device you are running on. For example:

LayoutInflater inflater = (LayoutInflater)context.getSystemService
      (Context.LAYOUT_INFLATER_SERVICE);
public static LayoutInflater from (Context context)
Added in API level 1

Obtains the LayoutInflater from the given context.

public View inflate (int resource,ViewGroup root)
Added in API level 1

Inflate a new view hierarchy from the specified xml resource. Throws InflateException if there is an error.

Parameters
resourceID for an XML layout resource to load (e.g., R.layout.main_page)
rootOptional view to be the parent of the generated hierarchy.
Returns
  • The root View of the inflated hierarchy. If root was supplied, this is the root View; otherwise it is the root of the inflated XML file.
 
FruitAdapter.java
public class FruitAdapter extends ArrayAdapter<Fruit> {
	private int resourceId;
	public  FruitAdapter(Context context,int textViewResourceId,List<Fruit> objects){
		super(context,textViewResourceId,objects);
		resourceId = textViewResourceId;
	}
	@Override
	public View getView(int position,View convertView,ViewGroup parent){
		//得到当前项的Fruit实例
		Fruit fruit = getItem(position);
		//LayoutInflater来为这个子项加载布局
		View view = LayoutInflater.from(getContext()).inflate(resourceId,null);
		ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
		TextView textView = (TextView) view.findViewById(R.id.fruit_name);
		fruitImage.setImageResource(fruit.getImageId());
		fruitName.setText(fruit.getName());
		return view;
		}
}

重写了ArrayAdapter的 getView()方法,这个方法在每个子item被滚动到屏幕内的时候会被调用。

目前ListView的运行效率是很低的,因为每个子item被滚动到屏幕内的时候会调用getView()在getView()方法中每次都将布局加载了一遍。

优化方法一:

public View getView(int position,View convertView,ViewGroup parent)

方法中有一个convertView参数,用于将之前加载好的布局进行缓存,以便以后重用。

修改上述代码:

 

public class FruitAdapter extends ArrayAdapter<Fruit> {
	private int resourceId;
	public  FruitAdapter(Context context,int textViewResourceId,List<Fruit> objects){
		super(context,textViewResourceId,objects);
		resourceId = textViewResourceId;
	}
	@Override
	public View getView(int position,View convertView,ViewGroup parent){
		//得到当前项的Fruit实例
		Fruit fruit = getItem(position);
		View view;
		//convertView为空,使用LayoutInflater加载布局
		//不为空,则直接对convertView进行重用
		if(convertView==null){
			view = LayoutInflater.from(getContext()).inflate(resourceId, null);
			}else{
				view = convertView;
			}
		ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
		TextView textView = (TextView) view.findViewById(R.id.fruit_name);
		fruitImage.setImageResource(fruit.getImageId());
		fruitName.setText(fruit.getName());
		return view;
		}
}

 

目前代码还可以优化,虽然现在已经不会再重复去加载布局,但每次getView()方法中还是会调用View的findViewById()来获取一次控件实例。下面使用ViewHolder继续优化:

优化方法二:新增一个内部类ViewHolder:
 
class ViewHolder{
		ImageView fruitImage;
		TextView fruitName;
	}

用于对对象的实例进行缓存。当convertView为空,创建一个ViewHolder对象,并将控件的实例都存放在ViewHolder里,然后调用View的setTag()方法将ViewHolder 对象存储在view中。当convertView不为空时,调用View的getTag()方法把ViewHolder取出。这样所有的控件实例都缓存在ViewHolder里了,就不用每次都通过findViewById()方法获取控件实例了。
 
FruitAdapter.java
package com.example.listviewtest;

import java.util.List;

import android.annotation.SuppressLint;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

@SuppressLint("ViewHolder")
public class FruitAdapter extends ArrayAdapter<Fruit> {
	private int resourceId;
	public  FruitAdapter(Context context,int textViewResourceId,List<Fruit> objects){
		super(context,textViewResourceId,objects);
		resourceId = textViewResourceId;
	}
	@Override
	public View getView(int position,View convertView,ViewGroup parent){
		Fruit fruit = getItem(position);
		View view;
		ViewHolder viewHolder;
		if(convertView==null){
			view = LayoutInflater.from(getContext()).inflate(resourceId, null);
			viewHolder = new ViewHolder();
			viewHolder.fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
			viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
			view.setTag(viewHolder);
		}
		else{
			view = convertView;
			viewHolder = (ViewHolder)view.getTag();
		}
		//View view = LayoutInflater.from(getContext()).inflate(resourceId,null);
		//ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
		//TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
		//fruitImage.setImageResource(fruit.getImageId());
		//fruitName.setText(fruit.getName());
		viewHolder.fruitImage.setImageResource(fruit.getImageId());
		viewHolder.fruitName.setText(fruit.getName());
		return view;
		
	}
	class ViewHolder{
		ImageView fruitImage;
		TextView fruitName;
	}
	
}

activity_main.xml
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        </ListView>

</LinearLayout>
 
MainActivity.java
 
package com.example.listviewtest;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
//import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Toast;

import android.widget.ListView;

public class MainActivity extends Activity {
	
	private String[] data = {"Apple","Banana","Orange","Watermelon","Pear","Grape","Pineapple",
			"Strawberry","cherry","Mango"};
	
	private List<Fruit> fruitList = new ArrayList<Fruit>();
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		initFruits();
//public FruitAdapter(Context context,int textViewResourceId,List<Fruit> objects)	
		FruitAdapter adapter = new FruitAdapter(MainActivity.this,R.layout.fruit_item,fruitList);
		//ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,data);
		ListView listView = (ListView) findViewById(R.id.list_view);
		listView.setAdapter(adapter);
		listView.setOnItemClickListener(new OnItemClickListener(){
		

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				// TODO Auto-generated method stub
				Fruit fruit = fruitList.get(position);
				Toast.makeText(MainActivity.this, fruit.getName(), Toast.LENGTH_SHORT).show();
				
			}
			
		});
	}

	private void initFruits() {
		// TODO Auto-generated method stub
		Fruit apple = new Fruit("Apple",R.drawable.ic_launcher);
		fruitList.add(apple);
		Fruit banana = new Fruit("Banana",R.drawable.ic_launcher1);
		fruitList.add(banana);
		Fruit orange = new Fruit("Orange",R.drawable.ic_launcher2);
		fruitList.add(orange);
		Fruit Watermelon = new Fruit("Watermelon",R.drawable.ic_launcher3);
		fruitList.add(Watermelon);
		Fruit Pear = new Fruit("Pear",R.drawable.ic_launcher4);
		fruitList.add(Pear);
		Fruit Grape = new Fruit("Grape",R.drawable.ic_launcher5);
		fruitList.add(Grape);
		Fruit Pineapple = new Fruit("Pineapple",R.drawable.ic_launcher6);
		fruitList.add(Pineapple);
		Fruit Strawberry = new Fruit("S trawberry",R.drawable.ic_launcher7);
		fruitList.add(Strawberry);
		Fruit cherry = new Fruit("Cherry",R.drawable.ic_launcher8);
		fruitList.add(cherry);
		Fruit mango = new Fruit("Mango",R.drawable.ic_launcher9);
		fruitList.add(mango);
		
	}
}


 

 

 


 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值