适配器视图无处不在,以至于您很难找到不使用它们的流行Android应用程序。 该名称听起来可能不熟悉,但是如果您认为从未见过适配器视图,则可能是错误的。 每次您看到Android应用以列表,网格或堆栈的形式显示用户界面元素时,您都会看到一个适配器视图在起作用。
顾名思义, 适配器视图是一个View
对象。 这意味着,您可以将其添加到活动中,就像添加任何其他用户界面小部件一样。 但是,它无法自行显示任何数据。 它的内容始终由另一个对象( 适配器)确定。 在本教程中,我将向您展示如何创建适配器以及如何使用它们来提供不同类型的适配器视图,例如ListView
和GridView
。
1.什么是适配器?
适配器是实现Adapter
接口的类的对象。 它充当数据集和适配器视图之间的链接,适配器视图是扩展抽象AdapterView
类的类的对象。 数据集可以是任何以结构化方式表示数据的数据。 数组, List
对象和Cursor
对象是常用的数据集。
适配器负责从数据集中检索数据,并根据该数据生成View
对象。 然后,使用生成的View
对象填充绑定到适配器的任何适配器视图。
您可以从头开始创建自己的适配器类,但是大多数开发人员选择使用或扩展Android SDK提供的适配器类,例如ArrayAdapter
和SimpleCursorAdapter
。 在本教程中,我们重点介绍ArrayAdapter
类。
2.适配器视图如何工作?
适配器视图可以非常有效地显示大型数据集。 例如, ListView
和GridView
小部件可以显示数百万个项目,而没有任何明显的滞后,同时保持内存和CPU使用率非常低。 他们是如何做到的? 不同的适配器视图遵循不同的策略。 但是,这通常是大多数人的工作。
- 它们仅渲染那些已经在屏幕上或将要在屏幕上移动的
View
对象。 这样,适配器视图消耗的内存可以是恒定的,并且与数据集的大小无关。
- 它们还使开发人员可以最大程度地减少昂贵的布局膨胀操作,并回收已经移出屏幕的现有
View
对象。 这样可以保持较低的CPU消耗。
3.创建一个ArrayAdapter
要创建适配器,您需要以下内容:
- 数据集
- 资源文件,其中包含生成的
View
对象的布局
此外,由于ArrayAdapter
类只能使用字符串,因此需要确保所生成的View
对象的布局至少包含一个TextView
小部件。
步骤1:建立资料集
ArrayAdapter
类可以同时使用数组和List
对象作为数据集。 现在,让我们使用数组作为数据集。
String[] cheeses = {
"Parmesan",
"Ricotta",
"Fontina",
"Mozzarella",
"Cheddar"
};
步骤2:创建资源文件
创建一个新的布局XML文件,其根元素为LinearLayout
并将其命名为item.xml 。 将其中的大文本小部件拖放到其中,并将其id
属性的值设置为cheese_name 。 布局XML文件应如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="@+id/cheese_name" />
</LinearLayout>
步骤3:创建适配器
在您的活动中,使用其构造函数创建ArrayAdapter
类的新实例。 作为参数,传递资源文件的名称, TextView
的标识符以及对该数组的引用。 现在适配器已准备就绪。
ArrayAdapter<String> cheeseAdapter =
new ArrayAdapter<String>(this,
R.layout.item,
R.id.cheese_name,
cheeses
);
4.创建一个列表
要显示垂直滚动项目列表,可以使用ListView
小部件。 要将小部件添加到活动中,可以将其拖放到活动的布局XML文件中,也可以使用Java代码中的其构造函数来创建。 现在,让我们做后者。
ListView cheeseList = new ListView(this);
通常,没有其他用户界面窗口小部件放置在包含ListView
的布局内。 因此,将ListView
传递给您活动的setContentView()
方法,以使其占据整个屏幕。
setContentView(cheeseList);
要将ListView
绑定到上一步中创建的适配器,请调用setAdapter()
方法,如下所示。
cheeseList.setAdapter(cheeseAdapter);
如果立即运行应用程序,则应该能够以列表的形式查看数组的内容。
5.创建一个网格
要显示垂直滚动的二维项目网格,可以使用GridView
小部件。 ListView
和GridView
都是抽象AbsListView
类的子类,它们具有许多相似之处。 因此,如果您知道如何使用另一种,那么您也会知道如何使用另一种。
使用GridView
类的构造函数创建一个新实例,并将其传递给您活动的setContentView()
方法。
GridView cheeseGrid = new GridView(this);
setContentView(cheeseGrid);
要设置网格中的列数,请调用其setNumColumns()
方法。 我将使它成为一个两列的网格。
cheeseGrid.setNumColumns(2);
通常,您需要使用setColumnWidth()
, setVerticalSpacing()
和setHorizontalSpacing()
方法来调整列的宽度和它们之间的间距。 请注意,这些方法使用像素作为其单位。
cheeseGrid.setColumnWidth(60);
cheeseGrid.setVerticalSpacing(20);
cheeseGrid.setHorizontalSpacing(20);
现在,您可以使用setAdapter()
方法将GridView
绑定到我们先前创建的适配器。
cheeseGrid.setAdapter(cheeseAdapter);
再次运行您的应用程序以查看GridView
外观。
6.添加事件监听器
可以在适配器视图内的项目上侦听单击和长按事件。 作为示例,让我们向GridView
添加一个click事件侦听器。
创建实现AdapterView.OnItemClickListener
接口的匿名类的新实例,并将其传递给GridView
对象的setOnItemClickListener()
方法。 Android Studio会自动为接口的onItemClick()
方法生成一个存根。 您会注意到,该方法的参数包括一个整数,用于指定列表项的位置。 您可以使用该整数来找出用户单击了数据集中的哪个项目。
下面的代码说明了每次单击GridView
的项目时如何将简单消息显示为小吃条。
cheeseGrid.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView,
View view, int position, long rowId) {
// Generate a message based on the position
String message = "You clicked on " + cheeses[position];
// Use the message to create a Snackbar
Snackbar.make(adapterView, message, Snackbar.LENGTH_LONG)
.show(); // Show the Snackbar
}
});
如果您运行该应用程序并单击网格中的任何项目,则屏幕底部将显示一条消息。 请注意,您可以使用相同的代码来侦听ListView
内项目的单击事件。
7.扩展ArrayAdapter
ArrayAdapter
只能处理其生成的View
对象的布局内的一个TextView
小部件。 要扩展其功能,必须对其进行扩展。 但是,在执行此操作之前,让我们创建一个稍微复杂一些的数据集。
假设我们的数据集包含以下类别的对象,而不是字符串:
static class Cheese {
String name;
String description;
public Cheese(String name, String description) {
this.name = name;
this.description = description;
}
}
这是我们将使用的数据集:
Cheese[] cheeses = {
new Cheese("Parmesan", "Hard, granular cheese"),
new Cheese("Ricotta", "Italian whey cheese"),
new Cheese("Fontina", "Italian cow's milk cheese"),
new Cheese("Mozzarella", "Southern Italian buffalo milk cheese"),
new Cheese("Cheddar", "Firm, cow's milk cheese"),
};
如您所见, Cheese
类包含两个字段, name
和description
。 要在列表或网格中显示两个字段,项目的布局必须包含两个TextView
小部件。
创建一个新的布局XML文件,并将其命名为custom_item.xml 。 向其中添加一个大文本和一个小文本小部件。 将第一个小部件的id
属性设置为cheese_name ,将第二个小部件的id
属性设置为cheese_description 。 现在,布局XML文件的内容应如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Large Text"
android:id="@+id/cheese_name" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Small Text"
android:id="@+id/cheese_description" />
</LinearLayout>
ArrayAdapter
还必须能够处理两个TextView
小部件。 重新访问您的活动,创建一个扩展ArrayAdapter
类的新匿名类,并重写其getView()
方法。 确保将数组作为参数传递给其构造函数。
ArrayAdapter<Cheese> cheeseAdapter =
new ArrayAdapter<Cheese>(this, 0, cheeses) {
@Override
public View getView(int position,
View convertView,
ViewGroup parent) {
}
};
在getView()
方法内部,必须使用position
参数作为数组的索引,并从该索引处获取项目。
Cheese currentCheese = cheeses[position];
getView()
方法的第二个参数使我们能够重用View
对象。 如果您忽略它,则适配器视图的性能将很差。 首次调用getView()
方法时, convertView
为null
。 您必须通过膨胀指定列表项布局的资源文件来初始化它。 为此,请使用getLayoutInflater()
方法获取对LayoutInflater
的引用,然后调用其LayoutInflater
inflate()
方法。
// Inflate only once
if(convertView == null) {
convertView = getLayoutInflater()
.inflate(R.layout.custom_item, null, false);
}
此时,您可以使用findViewById()
获取对布局内的TextView
小部件的引用,并调用它们的setText()
方法以使用数组中的数据对其进行初始化。
TextView cheeseName =
(TextView)convertView.findViewById(R.id.cheese_name);
TextView cheeseDescription =
(TextView)convertView.findViewById(R.id.cheese_description);
cheeseName.setText(currentCheese.name);
cheeseDescription.setText(currentCheese.description);
最后,返回convertView
以便可以将其用于填充与适配器关联的任何适配器视图。
return convertView;
8.使用视图架
适配器视图反复调用getView()
方法以填充自身。 因此,您必须尝试减少在其中执行的操作数量。
在上一步中,您可能已经注意到,即使我们确保列表项的布局仅膨胀一次,每次getView()
方法被调用时,都会调用消耗大量CPU周期的findViewById()
方法。叫。
为了避免这种情况并提高适配器视图的性能,我们需要将findViewById()
方法的结果存储在convertView
对象中。 为此,我们可以使用视图持有者对象,该对象仅是可以存储布局中存在的小部件的类的对象。
因为该布局具有两个TextView
小部件,所以视图持有者类还必须具有两个TextView
小部件。 我已将类命名为ViewHolder 。
static class ViewHolder{
TextView cheeseName;
TextView cheeseDescription;
}
在getView()
方法中,为布局充气后,现在可以使用findViewById()
方法初始化视图持有者对象。
ViewHolder viewHolder = new ViewHolder();
viewHolder.cheeseName =
(TextView)convertView.findViewById(R.id.cheese_name);
viewHolder.cheeseDescription =
(TextView)convertView.findViewById(R.id.cheese_description);
要将视图持有者对象存储在convertView
,请使用其setTag()
方法。
// Store results of findViewById
convertView.setTag(viewHolder);
现在,每次调用getView()
,都可以使用getTag()
方法从convertView
检索视图持有者对象,并使用其setText()
方法来更新其中的TextView
小部件。
TextView cheeseName =
((ViewHolder)convertView.getTag()).cheeseName;
TextView cheeseDescription =
((ViewHolder)convertView.getTag()).cheeseDescription;
cheeseName.setText(currentCheese.name);
cheeseDescription.setText(currentCheese.description);
如果现在运行应用程序,则可以看到GridView
在每个单元格中显示两行文本。
结论
在本教程中,您学习了如何创建适配器并使用它来填充各种适配器视图。 您还学习了如何创建自己的自定义适配器。 尽管我们只关注ArrayAdapter
, ListView
和GridView
类,但是您可以对Android SDK提供的其他适配器和适配器视图使用相同的技术。
Android支持库包含RecyclerView
类。 它的行为很像适配器视图,但是它不是AdapterView
类的子类。 如果要创建更复杂的列表,尤其是那些使用多个布局文件作为项目的列表,则应考虑使用它。 要了解更多信息,可以参考此Envato Tuts +教程 。
要了解有关AdapterView
类及其子类的更多信息,可以参考其文档 。