这里介绍的Adapter
的基本应用主要来自于《第一行代码》的第三章第5节内容,而在此处主要介绍的是ArrayAdapter
,其中ArrayAdapter
是BaseAdapter
的子类
Adapter基本理解
public class MainActivity extends Activity {
String[] s = {"第1个列表项","第1个列表项","第1个列表项"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.listView);
ArrayAdapter adapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_expandable_list_item_1,s);
listView.setAdapter(adapter);
}
}
其中ArrayAdapter
的声明是
new ArrayAdapter(MainActivity.this,android.R.layout.simple_expandable_list_item_1,s)
这里有三个参数,分别表示:
- 当前上下文(
context
) - 子项布局的id(
textViewResourceId
):这里的布局是使用了系统自带的 - 要适配的数据(
objects
)
可以将其理解为视图 + 布局 —— 桥梁 —— 数据,其中Adapter
就是桥梁
如同:
书籍要整理回到书架上,整个大书架就是视图,书架上的格子就是布局,许许多多的书就是数据,而我就是它们之间的桥梁,也就是Adapter
Adapter进阶
核心代码如下:
public class FruitArrayAdapter extends ArrayAdapter {
private int resourceId;
public FruitArrayAdapter(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 = LayoutInflater.from(getContext()).inflate(resourceId,null);
ImageView fruitIamge = (ImageView) view.findViewById(R.id.fruitImage);
TextView fruitText = (TextView) view.findViewById(R.id.fruitText);
fruitIamge.setImageResource(fruit.getImageId());
fruitText.setText(fruit.getName());
return view;
}
}
其中ArrayAdapter
的声明是
fruitArrayAdapter = new FruitArrayAdapter(MainActivity.this,R.layout.fruit_listview,fruitList);
可以看到,因为要显示的内容复杂了,ListView里面还需要给每一个item定义显示的格式,这个显示的格式就是布局R.layout.fruit_item
,它包含了两个组件,一个是<ImageView/>
用来显示一张图片,一个是<TextView/>
用来显示文字
然而为了能够将R.layout.fruit_item
显示出来,就必须要重写ArrayAdapter
中的getView()
方法,下面详细解释为什么
为何要重写getView()方法
根据官方的描述:
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, override getView(int, View, ViewGroup) to return the type of view you want.
也就是说,默认情况下ArrayAdapter只能让你传一个TextView
控件,例如R.layout.simple_expandable_list_item_1
,如果想要显示<TextView/>
以外的控件,像是<ImageView/>
,就必须重写getView()
方法
如何定义新的ArrayAdapter
public class FruitAdapter extends ArrayAdapter<Fruit>{
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@SuppressLint("ViewHolder")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);//获取当前项的Fruit实例
View view;
ViewHolder viewHolder;
if(convertView == null){//convertView用于将之前加载好的布局进行缓存
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);//将viewHolder存储在View中
}
else{
view = convertView;
viewHolder = (ViewHolder)view.getTag();
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
}
}
在实际的工作中,为了更为方便的理解和管理代码,最好的是直接定义一个新的ArrayAdapter
,比如FruitAdapter
,这样更容易理解,然后在FruitAdapter中
重写getView()
方法
getView(int position, View convertView, ViewGroup parent)
方法的中的参数:
- position:表示我们现在正在绘制
ListView
中第几个item - converview:view控件的缓存装置
所以为了给当前正在绘制的item设置要显示的数据,首先需要拿到当前item条目对应在数据库中的数据
//因为这个程序中每个item的数据为Fruit
Fruit fruit = getItem(position);
为何在
Adapter
的函数中可以拿到数据库中的数据,这是因为在实现Adapter
时,MainActivity
首先会初始化Adapter
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
初始化时就会传入数据库
fruitList
,如此就能够通过getItem(position)
方法知道当前item正在使用数据库fruitList
中的哪一条
当然仅仅知道要用数据库中的哪一条数据是不够的,同时还要将这条数据按照一定的格式填入到item视图中,这个格式就是R.layout.fruit_item
所以在程序中还定义FruitAdapter
的构造方法public FruitArrayAdapter(Context context, int textViewResourceId, List<Fruit> objects) {……
,就是为了获得布局R.layout.fruit_item
的id(类型为int
),从而利用LayoutInflater
为当前item视图加载我们自定义布局
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
这之后再把数据按照布局填入
最后返回填好数据的视图