1、主要概念
ListView
用于将大数据集以列表的形式展示。
ListView
可以看成一个容器,它有如下继承链:
View
<- ViewGroup
<- AdapterView
<- AbsListView
<- ListView
可见 ListView
继承自AdapterView
, 而AdapterView
的作用就是为ListView
提供数据。
主要的API:
listView.setAdapter(adapter实例)
我们通常用的Adapter
有如下几个:BaseAdapter
, CursorAdapter
, ArrayAdapter
, SimpleAdapter
.它们有如下的继承关系:
Adapter
<- ListAdapter
<- BaseAdapter
SimpleAdapter
、CursorAdapter
和ArrayAdapter
都是BaseAdapter
的子类。
2、使用方法
- 在
Activity
布局文件中加入一个ListView
组件
<ListView android:layout_width="match_parent" android:layout_height="wrap_content" android:entries="@array/listItems"></ListView>
这里entries
用于指定一个数组来渲染ListView
,它定义在strings.xml
中,内容如下:
<resources> <string-array name="listItems"> <item>orange</item> <item>apple</item> <item>banana</item> <item>pear</item> <item>watermelon</item> <item>lemon</item> <item>peach</item> <item>strawberry</item> </string-array> </resources>
到这里,运行项目已经可以看到列表项了。但这种方式只是静态的给ListView
添加数据,有很大的局限性。基本不用。下面通过给ListView
设置Adapter
提供数据。
- 删除上面的
entries
属性, 修改Activity
的内容
public class ArrayAdapterActivity extends AppCompatActivity { private String[] items = new String[]{"monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_array_adapter); ListView lv = findViewById(R.id.array_adapter_list_view); //可以这里为ListView设置了一个Adapter //并且指定了一个array_adapter_list_item.xml的布局文件,因此要新建一个这样的布局文件 //这个布局文件限制了一个数据项的显示形式,即一行数据如何展示 //该布局文件的根标签为TextView //然后用上面的定义的字符串数组填充每一个TextView lv.setAdapter(new ArrayAdapter<>(this, R.layout.array_adapter_list_item, items)); } }
这样,运行程序ListView
显示的便是数组中定义的值了。
- 不同的
Adapter
使用方式基本一致
下面是SimpleAdapter的使用:
lv.setAdapter(new SimpleAdapter(context, data, resource, from, to));
1.context: 当前上下文
2.data: List<Map<String, ?>>类型的数据集
3.resource:单个数据项的布局文件
5.from:data中map的各个key
6.to: 将key对应的值映射到数据项布局文件的某个组件
看个例子:
public class SimpleAdapterActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_simple_adapter); ListView lv = findViewById(R.id.simple_adapter_list_view); Map<String, Object> student1 = new HashMap<>(); student1.put("username", "jack"); student1.put("gender", "male"); student1.put("age", 33); Map<String, Object> student2 = new HashMap<>(); student2.put("username", "nacy"); student2.put("gender", "female"); student2.put("age", 13); List<Map<String, Object>> students = new ArrayList<>(); students.add(student1); students.add(student2); //from对应map的key String[] from = new String[]{"username", "gender", "age"}; //to对应单行数据项布局文件中组件的id int[] to = new int[]{R.id.simple_adapter_username, R.id.simple_adapter_gender, R.id.simple_adapter_age}; lv.setAdapter(new SimpleAdapter(this, students, R.layout.simple_adapter_list_item, from, to)); } }
单行数据项布局文件:
<?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/simple_adapter_username" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"></TextView> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="3" android:orientation="vertical"> <TextView android:id="@+id/simple_adapter_gender" android:layout_width="match_parent" android:layout_height="wrap_content"></TextView> <TextView android:id="@+id/simple_adapter_age" android:layout_width="match_parent" android:layout_height="wrap_content"></TextView> </LinearLayout> </LinearLayout>
3、BaseAdapter的使用
BaseAdapter
作为一个抽象类,比前几种Adapter
使用起来更灵活。
下面是一个例子:
创建一个类继承BaseAdapter
public class MyBaseAdapter extends BaseAdapter { //根据当前上下文将一个布局文件加载到内存,实例化为对象的一个类 private LayoutInflater layoutInflater; public MyBaseAdapter(Context context) { layoutInflater = LayoutInflater.from(context); } //决定数据项的个数 @Override public int getCount() { return 20; } //指定位置的数据项 @Override public Object getItem(int position) { return null; } //指定位置数据项的索引 @Override public long getItemId(int position) { return 0; } //容纳当前数据项布局文件的一个类 private class ViewHolder { public ImageView header; public TextView username; } //手机在滚动屏幕时,超出屏幕之外的ViewItem要被移除 //如果有10000个ViewItem,那么getView方法就要调用10000次,加载布局文件10000次,然后创建ViewItem,这是相当耗资源的 //为了提高性能,就需要对移除的ViewItem进行复用,每次滚动屏幕移除的ViewItem就是convertView //因此每次调用方法时,都要对convertView判空,如果为空才去加载布局文件,创建新的View,否则进行复用。 //这里的ViewHolder相当于一个容器,承载了一个ViewItem中所有的组件,避免了每次都要findViewById //一次findViewById之后,通过setTag方法将其设置到convertView中,下次就不用再查找了 @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = layoutInflater.inflate(R.layout.base_adapter_list_item, null); holder = new ViewHolder(); holder.header = convertView.findViewById(R.id.iv_header); holder.username = convertView.findViewById(R.id.tv_username); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.header.setImageResource(R.drawable.ic_launcher_foreground); holder.username.setText("这是第" + position + "项"); return convertView; } }
数据项布局文件:
<?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"> <ImageView android:id="@+id/iv_header" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="#008080"></ImageView> <TextView android:id="@+id/tv_username" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"></TextView> </LinearLayout>
Activity文件
public class BaseAdapterActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base_adapter); ListView lv = findViewById(R.id.base_adapter_list_view); lv.setAdapter(new MyBaseAdapter(this)); lv.setOnItemClickListener(new ListView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(BaseAdapterActivity.this, "点击" + position, Toast.LENGTH_SHORT).show(); } }); lv.setOnItemLongClickListener(new ListView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(BaseAdapterActivity.this, "长按" + position, Toast.LENGTH_SHORT).show(); return true; } }); } }