作为Adapter的基类,BaseAdapter拥有相当自由的使用空间,虽然使用格式较其他Adapter更加繁复,但是套路基本固定,也不算难。我们知道ListView中的各个条目是可以点击的,但是如果通过自定义布局为每个条目加入按钮控件,ListView本身的点击事件会覆盖按钮的点击事件,导致无法为按钮添加事件,如果使用BaseAdapter的话就可以是实现,而如果子条目的控件中加入按钮控件,ListView本身的点击事件就会失效,只能通过按钮点击事件设定功能。
SimpleAdapter的创建中比ArrayAdapter多了几个参数,由于BaseAdapter是一个抽象类,BaseAdapter的创建则需要写一个类继承BaseAdapter来实现,绑定数据和布局都需要在该类中实现。关于BaseAdapter中的方法在代码中有注释,那么BaseAdapter是如何将每条数据导入到每个条目上呢,基本机制是这样的:当App打开的时候,BaseAdapter首先将屏幕中能够显示的条目加载出来,其余项是不加载的,如果上拉,就会加载下面的条目,拉多少加载多少,如果下拉,就会重新加载上面的条目,并不会因为加载过就不再加载。
接下来看一个简单的实现,子条目布局文件list_item.xml如下
布局文件如下
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 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();
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 list;
public MyBaseAdapter(Context context, List 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如下
布局文件如下
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 list;
private List jlist;//奇数List
private List olist;//偶数List
public MyBaseAdapter(Context context, List list) {
this.list = list;
this.context = context;
jlist = new ArrayList();
olist = new ArrayList();
//数据填充
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 strs = new ArrayList();
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();
}
});
}
}
效果图如下