Android中BaseAdapter在ListView中的应用与优化

作为Adapter的基类,BaseAdapter拥有相当自由的使用空间,虽然使用格式较其他Adapter更加繁复,但是套路基本固定,也不算难。我们知道ListView中的各个条目是可以点击的,但是如果通过自定义布局为每个条目加入按钮控件,ListView本身的点击事件会覆盖按钮的点击事件,导致无法为按钮添加事件,如果使用BaseAdapter的话就可以是实现,而如果子条目的控件中加入按钮控件,ListView本身的点击事件就会失效,只能通过按钮点击事件设定功能。

SimpleAdapter的创建中比ArrayAdapter多了几个参数,由于BaseAdapter是一个抽象类,BaseAdapter的创建则需要写一个类继承BaseAdapter来实现,绑定数据和布局都需要在该类中实现。关于BaseAdapter中的方法在代码中有注释,那么BaseAdapter是如何将每条数据导入到每个条目上呢,基本机制是这样的:当App打开的时候,BaseAdapter首先将屏幕中能够显示的条目加载出来,其余项是不加载的,如果上拉,就会加载下面的条目,拉多少加载多少,如果下拉,就会重新加载上面的条目,并不会因为加载过就不再加载。

接下来看一个简单的实现,子条目布局文件list_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"
    android:gravity="center"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/b_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="10"
        android:padding="5dp"
        android:textSize="20sp" />

    <Button
        android:id="@+id/b_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

</LinearLayout>
布局文件如下

<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="com.briup.listview_baseadapter.MainActivity" >

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:divider="#ff0000"
        android:dividerHeight="1dp" />

</RelativeLayout>
MainActivity.class和BaseAdapter类文件如下

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

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {
	private ListView listView;
	private List<Integer> list;
	private MyBaseAdapter adapter;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		listView = (ListView) findViewById(R.id.listview);
		list = new ArrayList<Integer>();
		for(int i=0;i<100;i++)
			list.add(i);
		adapter = new MyBaseAdapter(MainActivity.this, list);
		listView.setAdapter(adapter);
	}
}
class MyBaseAdapter extends BaseAdapter{
	private Context context;
	private List<Integer> list;
	
	public MyBaseAdapter(Context context, List<Integer> list) {
		super();
		this.context = context;
		this.list = list;
	}

	@Override
	//返回ListView中要显示的子View数量
	//我们这里传入List所以可以返回list的条目就可以了
	public int getCount() {
		return this.list!=null?this.list.size():0;
	}

	@Override
	//返回一个子View,即ListView中的一个子条目显示数据的对象
	public Object getItem(int position) {
		return this.list.get(position);
	}

	@Override
	//根据ListView中的位置返回适配器的位置
	//这个方法实在自定义点击事件时调用,所以一般不需要覆写
	//这里适配器和list都是从零开始的,二者位置是一样的
	public long getItemId(int position) {
		return position;
	}

	@Override
	//返回这个条目的整个信息
	public View getView(int position, View convertView, ViewGroup parent) {
		TextView item_tv;
		Button item_btn;
		// 通过View.inflate方法将布局文件转化为View对象,然后依次获取子控件
		convertView = View.inflate(context, R.layout.list_item, null);
		item_tv = (TextView) convertView.findViewById(R.id.b_tv);
		item_btn = (Button) convertView.findViewById(R.id.b_btn);
		item_tv.setText(position+"");
		item_btn.setText(position+"");
		item_btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Button btn = (Button) v;
				btn.setText("点击");//按钮点击事件为点击后改变按钮文字为“点击”
			}
		});
		return convertView;
	}
	
}
效果如下


在BaseAdapter这种默认的加载方式下,如果需要查看的数据较大,反复查看将带来极大的资源负担,这就是需要优化的地方。

上面的代码使用View.inflate()方法获取布局,通过findViewById()方法获取控件,这种方法执行效率较低,降低它们的使用次数就能够达到优化的目的。首先,ListView中每个子布局都是一样的,所以并不是每个子布局都需要重新加载出来,其他子布局加载到的同样可用,所以我们应该将加载出来的布局存起来,以便下一条目使用,其次,BaseAdapter的机制就是加载屏幕能容下的子条目,所以第一次加载到的条目数不可避免的都要重新加载,到拉出新的条目后以后的条目就不需要再加载布局了。以上原理,我们只需要在加载布局时加上判断就可以达到优化效果。

那么通过什么样的办法存储布局呢,首先布局拿出来说到底是一个View,这样我们可以直接写一个类,其中定义多个属性来存放布局中的控件就可以达到目的,为了使用方便,我们将这个类定义为静态。另外,这个类的名字一般都取为ViewHolder,也可以取别的。

此外,作为原始的adpter,自然还有其他拓展控件,ListView的特点就是每一个条目都是一样的,那么当有需要在其中插入不一样的条目时该如何操作呢?其他adpter都做不到的,由于BaseAdapter加载布局的方法是自己写的,所以是可以实现的。以下就BaseAdapter的优化和插入不同条目做一个简单测试。

同上,我们依然让它简单的显示100个数字(去掉了按钮),然后先让它显示奇数,再显示偶数,并在奇数偶数的前面插入一条TextView写上奇数和偶数的个数。

子条目布局文件b_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"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/b_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="5dp"
        android:textSize="20sp" />

</LinearLayout>
布局文件如下

<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="com.briup.baseadapter.MainActivity" >

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

</RelativeLayout>

MyBaseAdapter.class类文件如下

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

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

public class MyBaseAdapter extends BaseAdapter {
	private Context context;
	private List<Integer> list;
	private List<Integer> jlist;//奇数List
	private List<Integer> olist;//偶数List

	public MyBaseAdapter(Context context, List<Integer> list) {
		this.list = list;
		this.context = context;
		jlist = new ArrayList<Integer>();
		olist = new ArrayList<Integer>();
		//数据填充
		for (Integer num : list) {
			if (num % 2 != 0)
				jlist.add(num);
			else
				olist.add(num);
		}
	}

	@Override
	public int getCount() {
		// 显示奇偶数个数的TextView也占据条目
		return this.list != null ? this.jlist.size() + 1 + this.olist.size() + 1 : 0;
	}

	@Override
	// 返回对应位置的View
	public Object getItem(int arg0) {
		return list.get(arg0);
	}

	@Override
	// 根据ListView中的位置返回适配器的位置
	public long getItemId(int position) {
		return position;
	}

	// arg0(position),适配器当前操作的位置
	// arg1(convertView)参数刚开始为空,只有当第一个item完全被挤出屏幕之后,新的item进入屏幕时,该参数有了值
	@Override
	public View getView(int arg0, View arg1, ViewGroup arg2) {
		ViewHolder viewHolder;
		int num;// 每个item中显示的内容,我这里是整型数字
		if (arg0 == 0) {//在第一条子条目中显示奇数个数
			// 通过JAVA代码插入一个TextView来显示数目
			TextView tv = new TextView(context);
			tv.setText("奇数: " + jlist.size() + " 个");
			return tv;
		} else if (arg0 <= jlist.size()) {//1-50
			num = jlist.get(arg0 - 1);//0-49
		} else if (arg0 == jlist.size() + 1) {//第51条应显示偶数数目
			TextView tv = new TextView(context);
			tv.setText("偶数: " + jlist.size() + " 个");
			return tv;
		} else {
			num = olist.get(arg0 - jlist.size() - 2);
		}
		if (arg1 != null && arg1 instanceof ViewGroup) {
			viewHolder = (ViewHolder) arg1.getTag();//经过拉动后新进入屏幕或出去又返回屏幕的条目从ViewHolder中获取布局
		} else {//第一屏的所有条目都需要加载布局
			// 通过View.inflate方法将自定义布局生成一个View对象,作为item的视图
			arg1 = View.inflate(context, R.layout.b_item, null);
			viewHolder = new ViewHolder();
			viewHolder.textView = (TextView) arg1.findViewById(R.id.b_tv);// 找到相应的控件
			arg1.setTag(viewHolder);//通过tag存放布局
		}
		viewHolder.textView.setText(num + "");
		return arg1;
	}

	// 用来储存相应控件的缓存类
	static class ViewHolder {
		TextView textView;
	}
}

MainActivity.class类文件如下
import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {
	ListView listView;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		listView = (ListView) findViewById(R.id.b_listview);
		ArrayList<Integer> strs = new ArrayList<Integer>();
		for(int i=1;i<=100;i++){
			strs.add(i);
		}
		MyBaseAdapter adpter = new MyBaseAdapter(MainActivity.this,strs);
		listView.setAdapter(adpter);
		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
					long arg3) {
				Toast.makeText(MainActivity.this, arg2+"", Toast.LENGTH_SHORT).show();
			}
		});
	}

}
效果图如下


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值