Android 自定义 Adapter
在上一章节中我们知道了啥是 Adapter
,也知道了 Adapter
的家族体系,也用过了几个 XxxAdapter
本章节,我们就来自己实现一个 Adapter
Adapter
要实现的方法很多,我们并不打算从头定义,而是继承 BaseAdapter
然后重写几个方法
首先,我们要做的就是要了解我们到底要重写哪些方法,因为 BaseAdapter
实现了 ListAdapter
和 SpinnerAdapter
接口,而这两个接口又继承自 Adapter
所以,我们先来看看接口 Adapter
有几个方法
Adapter 接口
打开 Adapter API 文档,我们可以看到它有以下几个方法要实现
方法 | 说明 |
---|---|
int getCount() | 数据集中有多少项目 |
Object getItem(int position) | 获取与数据集中指定位置关联的数据项 |
long getItemId(int position) | 获取与列表中指定位置关联的行 ID |
int getItemViewType(int position) | 获取将由 getView(int, View, ViewGroup) 指定项目创建的视图类型 |
View getView(int position, View convertView, ViewGroup parent) | 获取显示数据集中指定位置的数据的视图 |
int getViewTypeCount() | 返回将由其创建的视图类型的数量 getView(int, View, ViewGroup) |
boolean hasStableIds() | 项目 id 在基础数据更改期间是否稳定 |
boolean isEmpty() | 是否为空 |
void registerDataSetObserver(DataSetObserver observer) | 注册一个在此适配器使用的数据发生更改时调用的观察者 |
unregisterDataSetObserver(DataSetObserver observer) | 取消注册先前已通过此适配器注册的观察者 registerDataSetObserver(DataSetObserver) |
方法说明简单明了,主要就是获取数据项的数量和获取数据项,获取数据项的类型和视图
SpinnerAdapter
只有一个公开方法
方法 | 说明 |
---|---|
View getDropDownView(int position, View convertView, ViewGroup parent) | 根据下拉式弹出窗口中显示数据集中指定的位置获取数据的视图 |
ListAdapter
只有两个方法
方法 | 说明 |
---|---|
boolean areAllItemsEnabled() 设置是否启用此适配器中的所有项目 | |
boolean isEnabled(int position) | 判断指定位置上的项目是否启用 |
好了,简单明了,也没几个方法,我们再来看看 BaseAdapter
BaseAdapter
方法 | 说明 |
---|---|
boolean areAllItemsEnabled() | 判断是否启用此适配器中的所有项目 |
CharSequence[] getAutofillOptions() | 获取可帮助 AutofillService 自动填充适配器支持的视图的适配器数据的字符串表示形式 |
View getDropDownView(int position, View convertView, ViewGroup parent) | 根据下拉式弹出窗口中显示数据集中指定的位置获取数据的视图 |
int getItemViewType(int position) | 获取将由 getView(int, View, ViewGroup) 指定项目创建的视图类型 |
View getView(int position, View convertView, ViewGroup parent) | 获取显示数据集中指定位置的数据的视图 |
boolean hasStableIds() | 项目 id 在基础数据更改期间是否稳定 |
boolean isEmpty() | 是否为空 |
boolean isEnabled(int position) | 判断指定位置上的项目是否启用 |
void notifyDataSetChanged() | 通知附属的观察者,底层数据已被更改,任何反映数据集的视图都应该自行刷新 |
void notifyDataSetInvalidated() | 通知所附的观察员,底层数据不再有效或可用 |
void setAutofillOptions(CharSequence... options) | 设置返回的值 getAutofillOptions() |
void registerDataSetObserver(DataSetObserver observer) | 注册一个在此适配器使用的数据发生更改时调用的观察者 |
void registerDataSetObserver(DataSetObserver observer) | 注册一个在此适配器使用的数据发生更改时调用的观察者 |
unregisterDataSetObserver(DataSetObserver observer) | 取消注册先前已通过此适配器注册的观察者 registerDataSetObserver(DataSetObserver) |
方法说明简单明了,主要就是获取数据项的数量和获取数据项,获取数据项的类型和视图
现在我们看看我们要重写哪些方法
-
首先和监听器相关的都不用重写,因为暂时用不着,于是去掉
- notifyDataSetChanged()
- notifyDataSetInvalidated()
- registerDataSetObserver(DataSetObserver observer)
- registerDataSetObserver(DataSetObserver observer)
-
跟 自动填充 相关的也暂时用不着 (以后有机会介绍吧),于是去掉
- setAutofillOptions(CharSequence... options)
- getAutofillOptions()
-
有些方法可以有选择性的实现
- getDropDownView()
- areAllItemsEnabled()
- isEnabled()
- isEmpty()
- getItemViewType()
- getItemViewTypeCount()
好了,还剩下 4 个方法
方法 | 说明 |
---|---|
View getView(int position, View convertView, ViewGroup parent) | 获取显示数据集中指定位置的数据的视图 |
int getCount() | 数据集中有多少项目 |
Object getItem(int position) | 获取与数据集中指定位置关联的数据项 |
long getItemId(int position) | 获取与列表中指定位置关联的行 ID |
知道了要重写几个方法就好办了,现在我们拿 Android Adapter 适配器 的 SimpleAdapter 的范例来实现自己的 YetAdapter
自定义 BaseAdapter
我们先来看看效果图
对的,跟那个范例长得一模一样
-
创建一个 空的 Android 项目
cn.twle.android.YetAdapter
-
下载 /static/i/android/it_language_icon.zip 解压并把所有的图片拖到
res/drawable
目录 -
修改
activity_main.xml
添加一个 ListView<?xml version="1.0" encoding="utf-8" ?> <LinearLayout 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:padding="8dp" android:orientation="vertical" > <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
-
定义列表中每一行的布局,在
res/layout
目录下新建一个文件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:orientation="horizontal"> <!-- 定义一个用于显示头像的 ImageView --> <ImageView android:id="@+id/icon" android:layout_width="32dp" android:layout_height="32dp" android:baselineAlignBottom="true" android:paddingLeft="8dp" /> <!-- 定义一个竖直方向的 LinearLayout,把 语言 与 简介 的文本框设置出来 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" android:textColor="#1D1D1C" android:textSize="20sp" /> <TextView android:id="@+id/desc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8px" android:textColor="#B4B4B9" android:textSize="14sp" /> </LinearLayout> </LinearLayout>
-
创建一个
Bean
,在MainActivity.java
同目录下创建一个类YetLanguage.java
package cn.twle.android.yetadapter; public class YetLanguage { private String aName; private String aDesc; private int aIcon; public YetLanguage () { } public YetLanguage (String aName, String aDesc, int aIcon) { this.aName = aName; this.aDesc = aDesc; this.aIcon = aIcon; } public String getaName() { return aName; } public String getaDesc() { return aDesc; } public int getaIcon() { return aIcon; } public void setaName(String aName) { this.aName = aName; } public void setaDesc(String aSpeak) { this.aDesc = aDesc; } public void setaIcon(int aIcon) { this.aIcon = aIcon; } }
-
在
MainActivity.java
同目录下创建一个适配器YetAdapter
继承自BaseAdapter
package cn.twle.android.yetadapter; import android.content.Context; import android.widget.BaseAdapter; import android.widget.TextView; import android.widget.ImageView; import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater; import java.util.LinkedList; public class YetAdapter extends BaseAdapter { private LinkedList<YetLanguage> mData; private Context mContext; public YetAdapter(LinkedList<YetLanguage> mData, Context mContext) { this.mData = mData; this.mContext = mContext; } @Override public int getCount() { return mData.size(); } @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_item,parent,false); ImageView img_icon = (ImageView) convertView.findViewById(R.id.icon); TextView txt_aName = (TextView) convertView.findViewById(R.id.name); TextView txt_aDesc = (TextView) convertView.findViewById(R.id.desc); img_icon.setBackgroundResource(mData.get(position).getaIcon()); txt_aName.setText(mData.get(position).getaName()); txt_aDesc.setText(mData.get(position).getaDesc()); return convertView; } }
-
修改
MainActivity.java
package cn.twle.android.yetadapter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ListView; import java.util.LinkedList; import java.util.List; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); List<YetLanguage> mData = new LinkedList<YetLanguage>(); mData.add(new YetLanguage("Kotlin", "Kotlin 是运行在 Java 虚拟机上的静态语言,被称之为 Android 世界的 Swift", R.drawable.kotlin)); mData.add(new YetLanguage("Scala", "Scala 是一门多范式(multi-paradigm)的编程语言", R.drawable.scala)); mData.add(new YetLanguage("Swift", "Swift 是开发 Mac APP 和 iOS APP 的语言", R.drawable.swift)); mData.add(new YetLanguage("TypeScript", "TypeScript 是一种由微软开发的自由和开源的编程语言", R.drawable.typescript)); //创建一个 YetAdapter YetAdapter yetAdapter = new YetAdapter((LinkedList<YetLanguage>) mData,getApplicationContext()); ListView listView = (ListView) findViewById(R.id.listview); listView.setAdapter(yetAdapter); } }
对,就这么简单