在学习了 ScrollView 及 Adapter 两节内容之后,大家应该对 ListView 有了一些基本的了解,它是一个列表样式的 ViewGroup,将若干 item 按行排列。ListView 是一个很基本的控件也是 Android 中最重要的控件之一。它可以帮助我们完成多个 View 的垂直排列并支持滚动显示效果,而它比 ScrollView 更灵活也更易扩展,Adapter 作为 UI 控件和数据源之间的桥梁,会帮我们实现 MVC 模式,所以在实际开发中大多数的列表场景我们会优先考虑使用 ListView 来实现(目前 Google 推出了新的更强大的列表控件——RecyclerView,不过基本原理和 ListView 类似)。
ListView 在 Android App 中无处不在,比如最常用的“联系人”就可以通过 ListView 轻松实现。通过 ListView 用户可以上下滑动来浏览列表信息,我们可以在 ListView 中放置各种控件,比如 ImageView、Button、ToggleButton 等来丰富我们的列表样式。
正因为 ListView 通常是用来展示大量的数据集的控件,所以我们不可能挨个的为每个 item 去设置相应的数据,这时候就要借助 Adapter 来帮助我们完成 UI 控件和数据的绑定工作了。
ListView 相比其他控件来讲确实比较特殊,也有很多使用技巧,但是它作为一个 ViewGroup,同样也有自己的布局属性、 API 及事件监听器。
ListView 的样式示意图如下:
ListView 中的每个 item 可以设置成任意样式,可以包含任意的 Android 控件,非常灵活。接下来,我们来一起看看如何使用。
使用 ListView 就一定逃不开 Adapter,在上一节我们介绍了 ArrayAdapter 和 SimpleAdapter 配合 ListView 的使用方法,其实 ArrayAdapter 和 SimpleAdapter 都是继承 BaseAdapter 做的封装,那么这一节我们就来看看 BaseAdapter 究竟是何方神圣。为了让大家更好的看到对比,这一节我们用 BaseAdapter 来实现上一节的水果的列表。
BaseAdapter 是一个接口,我们自定义 Adapter 就需要实现一个 BaseAdapter 接口,首先创建一个 MyAdapter 类实现 BaseAdapter 接口,如下:
package com.emercy.myapplication;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
可以看到,继承自 BaseAdapter 的类有 4 个方法是必须实现的,我们具体看看这四个方法分别表示什么以及如何实现:
@Override
public int getCount() {
int count = arrayList.size(); // 计算数据 ArrayList 的长度
return count; // 返回列表的长度
}
@Override
public Object getItem(int i):
return arrayList.get(i); // item 对应的数据内容
}
@Override
public long getItemId(int i) {
return i;
}
LayoutInflater
类获取布局对象,然后通过findViewById
拿到具体的控件,并将数据内容设置到控件当中,比如我们需要在列表中设置一个图片资源:@Override
public View getView(int i, View view, ViewGroup viewGroup) {
view = inflter.inflate(R.layout.activity_gridview, null); // 获取布局对象
ImageView icon = (ImageView) view.findViewById(R.id.icon); // 通过ID拿到具体的View对象
icon.setImageResource(flags[i]); // 设置ImageView的图片资源
return view;
}
为了对比学习,本例实现一个和上一节中 SimpleAdapter 的水果列表相同的例子,布局文件可直接引用,具体代码可以参考 第 23 节第 2 小节的内容。
在 Adapter 中创建我们需要保存的数据,和上一节的例子一样,我们用两个数组分别保存水果名称和水果图片资源:
String[] mDataName = {"苹果", "梨", "香蕉", "桃子", "西瓜", "荔枝", "橘子"};
int[] mDataImage = {R.drawable.apple, R.drawable.pear, R.drawable.banana, R.drawable.peach,
R.drawable.watermelon, R.drawable.lychee, R.drawable.orange, R.drawable.orange};
在 MyAdapter 中新增数据更新接口,用于初始数据的设置及后续数据的更新:
MyAdapter.java
public void setData(String[] name, int[] resId)
这样一来我们就有了数据源,接着按照 第 24 节 3.1 小节中对 4 个回调接口的描述修改回调方法体。主要是在getCount
中返回数组长度,getView
中通过 layout 对象获取到 TextView 和 ImageView,然后设置水果名称和图片,最终 MyAdapter 类的代码如下:
package com.emercy.myapplication;
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 {
private Context mContext;
private String[] mName;
private int[] mResId;
public MyAdapter(Context context) {
mContext = context;
}
public void setData(String[] name, int[] resId) {
mName = name;
mResId = resId;
}
@Override
public int getCount() {
return mName.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.list_view, null);
TextView name = convertView.findViewById(R.id.textView);
ImageView image = convertView.findViewById(R.id.imageView);
name.setText(mName[position]);
image.setImageResource(mResId[position]);
return convertView;
}
}
ListView 的核心适配逻辑都在 Adapter 中完成,主 Activity 比较简单,主要做以下几件事:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends Activity {
ListView mListView;
String[] mDataName = {"苹果", "梨", "香蕉", "桃子", "西瓜", "荔枝", "橘子"};
int[] mDataImage = {R.drawable.apple, R.drawable.pear, R.drawable.banana, R.drawable.peach,
R.drawable.watermelon, R.drawable.lychee, R.drawable.orange, R.drawable.orange};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = findViewById(R.id.listView);
MyAdapter adapter = new MyAdapter(this);
adapter.setData(mDataName, mDataImage);
mListView.setAdapter(adapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(getApplicationContext(), mDataName[i], Toast.LENGTH_LONG).show();
}
});
}
}
运行效果和上一节一样:
本节主要介绍了 ListView 搭配 BaseAdapter 实现列表功能的方法,BaseAdapter 比 ArrayAdapter 和 SimpleAdapter 拥有更大的可控性,我们可以自己实现很多复杂的功能。目前更推荐使用的是 Google 近年推出的 RecyclerView,不过基本原理和 ListView 类似,本节主要介绍的是基础的用法,在掌握了基本用法及核心思想之后,可以继续学习一些优化手段及高级用法。
0/1000