24. 【Android教程】适配器 Adapter

news/2024/9/23 9:19:17/

本节将会引入一个全新的概念——适配器,这个名字很形象,和电源适配器的功能类似,从程序设计的角度出发,它可以将不同类型、不同结构的数据适配到一起。
在 Android 中,适配器是 UI 组件和数据之间的桥梁,它帮助我们将数据填充到 UI 组件当中,实现了一个典型的 MVC 模式。我们可以分别编写独立的 UI 样式和数据模型,至于数据如何与 UI 组件绑定都由 Adapter 帮我们完成,这样的好处就是做到 UI 和数据的解耦。Android 系统为我们提供了多种 Adapter,今天就来介绍几种常见同场景下 Adapter 的基本用法。

1. 为什么要用 Adapter

我们首先看看 Android 为什么要引入 Adapter,也就是使用 Adapter 有哪些好处?
在 Android 中Adapter 通常是搭配列表控件使用,我们先看看在没有学习 Adapter 的时候,如何实现一个列表样式,我们可能需要以下几步:

  1. 创建一个 ScrollView(上一节刚学到的,不熟悉的可以参照 22 节);
  2. 在 ScrollView 中放置多个 View / ViewGroup,比如 TextView;
  3. 获取每个 TextView 实例,根据业务需求为 TextView 设置 Text;
  4. 编写额外代码管理所有的 TextView,并且需要分辨点击事件发生在第几行从而定位到相应的 TextView,从而相应列表的点击事件。

读到这里,脑海里已经有实现思路了吗?即使你能捋清思路,代码也很难写的优雅,因为编写 TextView 样式的这些 UI 代码一定会和 TextView 内容的数据代码耦合在一起,这样如何 UI 样式一变,数据也需要做很大的调整,后期的维护成本是相当高的。最好的办法就是能够有一套逻辑专门去管理数据和 UI 代码的绑定关系,用它来将 UI、Data 隔离开,提高代码的简洁性和可维护性。
我们结合一张图来理解一下 Adapter:

电源适配器将电器和电源接口适配到一起,好处是可以让手机等电子产品及家用电器厂商在生产过程中完全不需要考虑用户电源接口的类型,可以是 220V 交流电、也可以是 USB 接口,适配工作只需要交给相应的 Adapter 就可以完成。而 Android 适配器是将数据和 UI 适配到一起,好处同样也是我们在做 UI 的时候,完全不用考虑未来填充的数据是什么样的,只需要针对不同的数据类型提供一个 Adapter 即可。

如果你觉得上面的描述都太抽象,后面可以通过几个简单的例子来直观感受一下 Adapter 的用法。

2. Adapter 的类型

就像电源适配器需要根据不同的电源接口类型提供不同的适配器一样,Android 中我们需要根据不同数据类型提供不同的 Adapter,系统已经为我们实现了几种 Adapter:

  1. BaseAdapter:
    所有 Adapter 的基类,通常我们需要实现自定义 Adapter 时,需要实现此抽象类,在实际开发中使用的最多的类型。
  2. ArrayAdapter:
    适用于一个单项列表,并且数据可以以数据形式存放的场景。
  3. SimpleAdapter:
    适用于一个列表项中有多个数据的场景,它可以将一个 map 里的数据映射到 xml 布局文件中的各个控件上。
  4. SimpleCursorAdapter:
    针对数据库使用的 Adapter,使用场景很少。

3. 常见 Adapter 的用法

其实最常用的是 BaseAdapter,在实际开发中稍微复杂一点的列表都需要通过继承 BaseAdapter 来编写一个自定义的 Adapter 。大多数场景是结合 ListView / GridView 来完成,所以 BaseAdapter 的具体用法我们会放到后面 ListView / GridView 的相关章节做详细介绍,这里主要是让大家对 Adapter 的概念有个基本认识即可。

3.1 ArrayAdapter 的用法

ArrayAdpater 的用法非常简单,如上一小节所说,它适合列表是单项列表并且数据可以存在一个数据当中的场景。首先我们创建布局文件,里面只需要存放一个 ListView 控件即可:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/simpleListView"android:layout_width="match_parent"android:layout_height="wrap_content"android:divider="#000"android:dividerHeight="2dp" />

其中有两个属性大家可能比较陌生:

 android:divider="#000"android:dividerHeight="2dp"

这两个属性是用来设置列表项之间的分割线样式的,详细的会在 ListView 章节进行介绍。然后还需要编写列表中每个列表项的布局样式,我们只需要一个 TextView 来显示文本,而文本的内容就是数组的数据,列表项布局代码 list_view.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="vertical"><TextViewandroid:id="@+id/textView"android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_gravity="center"android:padding="30dp"android:textColor="#000" />
</LinearLayout>

一个我们非常熟悉的 TextView,然后就可以在 Java 代码中通过 ArrayAdapter进行数据 / UI 的绑定了,Java 代码如下:

package com.emercy.myapplication;import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;import android.app.Activity;public class MainActivity extends Activity {ListView mList;String mNums[] = {"TextView", "EditText", "Button", "ImageButton", "RadioButton", "ToggleButton","ImageView", "ProgressBar", "SeekBar", "RatingBar", "ScrollView", "Adapter"};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mList = findViewById(R.id.simpleListView);ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this, R.layout.list_view, R.id.textView, mNums);mList.setAdapter(arrayAdapter);}}

我们在 OnCreate() 中获取ListView对象,然后创建 ArrayAdapter,传入列表项的布局文件 ID、需要显示内容的 TextView 控件 ID 以及数组形式的数据。最后通过 setAdapter 完成数据及 UI 的绑定,这样系统就会帮我们完成适配工作,效果如下:

我们写在数组中的数据就会按顺序填充到列表中了。

3.2 SimpleAdapter 的用法

SimpleAdapter 相比 ArrayAdapter 会更丰富一点,主要体现在 ArrayAdapter 只能适用于列表中只有一项数据(上一小节中的 TextView)的场景,而如果列表项由多个数据组成,比如文字配图片的形式 ArrayAdapter 就有些力不从心,这时候就需要用到 SimpleAdapter 了。
整个 Activity 的布局文件依旧不变,只需要放置一个 ListView 即可。我们在之前的list_view.xml中增加一个 ImageView,如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/imageView"android:layout_width="50dp"android:layout_height="50dp"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:layout_marginRight="10dp"android:padding="5dp" /><TextViewandroid:id="@+id/textView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:padding="30dp"android:textColor="#000" />
</RelativeLayout>

从上面的布局文件可以看出,我们现在的列表项由两个部分组成:一个图片和一个文本。接着修改 Java 代码,主要是数据格式的变换,现在数据数组需要包含图片资源和文本内容两个部分,如下:

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.SimpleAdapter;
import android.widget.Toast;import java.util.ArrayList;
import java.util.HashMap;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};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = findViewById(R.id.simpleListView);// 将水果图片和水果名称整合到一个map当中,最后将所有的水果都存放到ArrayListArrayList<HashMap<String, String>> arrayList = new ArrayList<>();for (int i = 0; i < mDataName.length; i++) {HashMap<String, String> hashMap = new HashMap<>();hashMap.put("name", mDataName[i]);hashMap.put("image", mDataImage[i] + "");arrayList.add(hashMap);}String[] from = {"name", "image"};int[] to = {R.id.textView, R.id.imageView};SimpleAdapter simpleAdapter = new SimpleAdapter(this, arrayList, R.layout.list_view, from, to);mListView.setAdapter(simpleAdapter);mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {Toast.makeText(getApplicationContext(), mDataName[i], Toast.LENGTH_LONG).show();}});}
}

在这段例子中,我们使用两个数组分别保存水果名称及水果图片,然后再将每个水果的名称和图片存入一个 map,接着把所有的水果 map 都整合到一个 ArrayList 当中,最后创建 SimpleAdapter,这一步也是最关键的。我们来单独看看 SimpleAdapter 的创建语句:

SimpleAdapter simpleAdapter = new SimpleAdapter(this, arrayList, R.layout.list_view, from, to);

SimpleAdapter 构造器参数比较多,我们来仔细分析分析。传入构造器的第二个参数是数据源,也就是存放所有水果 map 的 ArrayList 对象;传入的第三个参数是列表项的布局文件,即 list_view.xml;第四个参数是一个字符串数组,表示水果 map 中的 key,也就是水果名和水果图片的 key,用来与具体的 UI 控件对应;最后一个参数是一个整形数组,用来与第四个参数匹配,告诉系统 map 中的哪些数据需要显示到哪个 View 上。这样一来,就完成了列表、列表项、数据的对应关系,接着直接用setAdapter完成适配,最后通过 ListView 的setOnItemClickListener为每个列表项添加点击事件(具体使用方法会在 ListView 章节详细介绍),效果如下:

4 小结

本节介绍了一个比较新鲜的概念——适配器,大家初期理解它可以当成电源适配器来理解就好。然后介绍了几种常用的使用方法,系统也为我们提供了几种封装好的 Adapter 可以应付一些简单的场景。但是在大家实际的开发过程中能够直接使用系统提供的 Adapter 的场景比较少,大多数情况还是要继承 BaseAdapter 来自己实现一套 Adapter,这个内容会在 ListView / GridView 相关章节做具体的介绍。另外,大家可以思考一下本章节的例子如果使用 ScrollView 要怎么实现,优劣势在哪里?


http://www.ppmy.cn/news/1426113.html

相关文章

算法题解记录18+++搜索二维矩阵Ⅱ

本题可以说是运用二分查找的典例&#xff0c;即使是面对矩阵&#xff0c;只要是它保持“排序好”这样的结构&#xff0c;就一定能采用二分查找法。【你知道的&#xff0c;对于排序好的数组&#xff0c;二分查找几乎是最优秀的算法】 当然&#xff0c;答案提供的是“Z字形查找法…

详解IIC通信协议以及FPGA实现

一、IIC简介 IIC也称为I2C&#xff08;Inter-Integrated Circuit&#xff09;由飞利浦公司&#xff08;现在的恩智浦半导体&#xff09;开发&#xff0c;是一种用于短距离数字通信的串行&#xff0c;同步&#xff0c;半双工通信接口协议&#xff1b;传输在标准模式下可以达到10…

16篇 hdfs中篇

36. **更改文件权限** (hdfs fs -chmod): 更改HDFS中文件或目录的权限模式。 - 示例&#xff1a;hdfs fs -chmod 666 hdfs_file&#xff08;设置文件的权限为可读写&#xff09; 37. **更改文件所有者** (hdfs fs -chown): 更改HDFS中文件或目录的所有者和组。 - 示例…

安装kafka需优先安装 zookeeper ,scala

安装kafka需优先安装 zookeeper ,scala scala安装&#xff08;如安装略过&#xff09; 现在 wget https://downloads.lightbend.com/scala/2.12.11/scala-2.12.11.tgz解压 tar -zxvf scala-2.12.11.tgz编辑环境变量 vim /etc/profile添加环境变量 &#xff08;export SCALA_H…

微服务中Dubbo通俗易懂讲解及代码实现

当你在微服务架构中需要不同服务之间进行远程通信时&#xff0c;Dubbo是一个优秀的选择。Dubbo是一个高性能的Java RPC框架&#xff0c;它提供了服务注册、发现、调用、负载均衡等功能&#xff0c;使得微服务之间的通信变得简单而高效。 让我们来看一下Dubbo的通俗易懂的解释和…

[mysql]binlog监听main方法简化版

一、检测是否开启binlog 1.正常开启状态 show variables like log_bin; ---------------------- | Variable_name | Value | ---------------------- | log_bin | ON | ---------------------- 1.2 show binary logs; ----------------------------- | Log_name …

MediaStream使用webRtc多窗口传递

最近在做音视频通话&#xff0c;有个需求是把当前会话弄到另一个窗口单独展示&#xff0c;但是会话是属于主窗口的&#xff0c;多窗口通信目前不能直接传递对象&#xff0c;所以想着使用webRtc在主窗口和兄弟窗口建立连接&#xff0c;把主窗口建立会话得到的MediaStream传递给兄…

(51单片机)第十章-定时器/计时器应用提高

10.1 方式0应用 通过设置TMOD寄存器中的M1M0位为00选择定时器方式0&#xff0c;方式0的计数位数是13位&#xff0c;对TO来说&#xff0c;由TL0寄存器的低5位(高3位未用)和TH0的8位组成。TL0的低5位溢出时向 TH0 进位&#xff0c;TH0 溢出时&#xff0c;置位 TCON 中的 TF0 标志…