赞
踩
目录
欢迎来到本篇博客!在移动应用开发中,列表视图(ListView)是一个不可或缺的组件,它为我们提供了一种简洁而强大的方式来展示和管理大量的数据。无论是显示联系人列表、商品信息还是消息记录,列表视图都能够让我们以清晰、有序的方式将数据呈现给用户。
在今天的移动应用开发中,Android Studio 是我们的得力助手。它提供了丰富的开发工具和功能,使我们能够轻松地构建符合用户期望的应用程序。而在 Android Studio 中,使用列表视图是非常常见的需求,因此掌握列表视图的使用技巧是每个开发者的必备技能之一。
在本篇博客中,我们将深入探讨列表视图在 Android Studio 中的应用。我们将了解
无论您是初学者还是有经验的开发者,本篇博客都将为您提供全面且实用的知识。让我们一起开始探索 Android Studio 中列表视图的奇妙世界吧!
列表视图在Android应用开发中扮演着重要的角色,它为我们提供了一种直观、高效的方式来展示和管理大量的数据。无论是展示联系人列表、商品信息还是消息记录,列表视图都能够以清晰而有序的方式将数据呈现给用户,并提供丰富的交互性。
以下是列表视图的一些常见应用场景:
显示联系人列表:当应用需要展示联系人列表时,列表视图是理想的选择。通过列表视图,我们可以在每个列表项中显示联系人的名称、头像等信息,并可以为每个列表项设置点击事件,例如拨打电话或发送短信。
商品列表:电子商务应用通常需要展示大量的商品信息。通过列表视图,我们可以将商品按照清晰的布局展示出来,包括商品名称、价格、图片等。用户可以通过滚动列表浏览商品,并选择感兴趣的商品进行购买或查看详情。
消息列表:聊天应用或通知应用通常需要展示大量的消息记录。通过列表视图,我们可以将消息按照时间顺序展示,并在每个列表项中显示发送者、内容、时间等信息。用户可以通过滚动列表查看历史消息,并进行回复或其他操作。
设置界面:设置界面常常包含多个选项或功能项。通过列表视图,我们可以以列表的形式展示各个设置项,使用户能够方便地浏览和更改应用的各种设置。
除了以上场景,列表视图还可以用于显示音乐播放列表、图书目录、新闻列表等各种类型的数据。通过适当的布局和交互设计,列表视图能够提供良好的用户体验,帮助用户快速准确地找到所需信息。
列表视图是Android开发中常用的UI组件,用于展示大量的数据列表。它的原理和工作方式主要包括以下几个方面:
数据源:列表视图需要一个数据源来提供显示的数据。数据源可以是数组、集合或数据库等。每个数据项代表着列表中的一个条目。
适配器(Adapter):适配器是连接数据源和列表视图之间的桥梁。它负责将数据项与列表项视图进行关联,以便正确地显示数据。适配器可以继承自BaseAdapter类,也可以使用ArrayAdapter、CursorAdapter等预置的适配器类。
列表项视图:每个数据项都会对应一个列表项视图,用于展示具体的数据内容。列表项视图可以通过布局文件来定义,也可以使用代码动态创建。它通常包含了多个控件(如TextView、ImageView等),用于显示数据项的各个属性。
列表项复用:为了提高性能和减少内存占用,Android系统采用了列表项的复用机制。当列表滚动时,超出屏幕范围的列表项会被回收并重复利用,而不是每次都创建新的列表项。这个机制需要在适配器的getView()方法中进行实现。
列表项点击事件:列表视图可以监听用户对列表项的点击事件。当用户点击某个列表项时,可以通过设置监听器来执行相应的操作,例如打开新界面、显示详细信息等。
列表视图的更新:当数据源发生变化时,需要及时通知列表视图进行更新。可以通过调用适配器的notifyDataSetChanged()方法来实现,该方法会触发列表视图重新加载数据并刷新界面。
【图1 Adapter 适配器的原理图】
总体而言,列表视图的工作流程如下:
通过理解和掌握列表视图的原理和工作方式,开发者可以更好地使用和定制列表视图,实现各种复杂的数据展示需求。
定义数据模型类:创建一个数据模型类,用于表示列表中的每个项目。该类应包含与项目相关的属性和方法。
- public class Item {
- private String title; // 标题
- private String description; // 描述
-
- // 构造函数、Getter 和 Setter 方法等
-
- // 可以根据需要扩展其他属性和方法
- }
适配器在列表视图中起着非常重要的作用,它负责将数据源与列表视图进行关联,并决定如何将数据显示在列表项中。以下是一个基本的适配器使用示例:
- ArrayList<String> dataList = new ArrayList<>();
- dataList.add("Item 1");
- dataList.add("Item 2");
- dataList.add("Item 3");
- public class MyAdapter extends BaseAdapter {
- private ArrayList<String> mDataList;
- public MyAdapter(ArrayList<String> dataList) {
- mDataList = dataList;
- }
-
- @Override
- public int getCount() {
- return mDataList.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mDataList.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) { // 创建或复用列表项视图
- if (convertView == null) {
- convertView=LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_layout, parent, false);
- }
- // 获取当前位置的数据项
- String item = (String) getItem(position); // 在列表项视图中显示数据
- TextView textView = convertView.findViewById(R.id.text_view);
- textView.setText(item);
- return convertView;
- }
- }
MyAdapter
,其中包含了必要的方法:
getCount()
:返回数据项的数量。getItem()
:返回对应位置的数据项。getItemId()
:返回数据项的ID,这个ID可以用于处理点击事件等需求,通常使用数据项的位置作为ID。getView()
:创建或复用列表项视图,并在其中显示数据。- ListView listView = findViewById(R.id.list_view);
- MyAdapter adapter = new MyAdapter(dataList); // dataList是前面创建的数据源 listView.setAdapter(adapter);
- // 动态更改数据源
- itemList.add(new Item("标题4", "描述4"));
- adapter.notifyDataSetChanged();
在Android的布局文件中,可以使用ListView元素来定义和配置列表视图。下面是一个简单的示例来演示如何在布局文件中编写列表视图:
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <!-- 其他布局元素 -->
- <ListView
- android:id="@+id/listView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
上述示例中,使用LinearLayout作为根布局,内部包含了其他的布局元素(可根据实际需求添加)和一个ListView元素。在ListView元素中,可以根据需要设置相应的属性。
常用的ListView属性包括:
android:id
:给ListView分配一个唯一的标识符。android:layout_width
和android:layout_height
:用于设置ListView的宽度和高度。android:divider
和android:dividerHeight
:可用于设置列表项之间的分隔线及其高度。android:listSelector
:用于设置列表项被选中时的背景颜色或样式。android:scrollbars
:可用于设置滚动条的显示方式。除了ListView元素本身,还需要通过适配器(Adapter)来为列表提供数据。适配器负责将数据与列表视图进行关联,使得数据能够正确地显示在列表中。具体的适配器配置及数据绑定操作通常是在Java代码中完成。
需要注意的是,从Android 10(API级别29)开始,Google推荐使用 RecyclerView 作为更加灵活和高效的列表视图组件。RecyclerView提供了更多的功能和优化选项,可以替代传统的ListView。如果你的应用目标设备的最低支持版本高于Android 10,推荐使用RecyclerView代替ListView。
自定义列表项布局是通过创建一个XML文件来定义列表项视图的外观和布局。以下是一个简单的示例,演示如何创建一个自定义的列表项布局:
创建一个新的XML文件:在Android项目的res/layout
目录下创建一个新的XML文件,命名为list_item_layout.xml
(可以根据实际需求进行命名)。
定义列表项布局:在list_item_layout.xml
文件中,使用各种布局和控件来定义列表项的外观。下面是一个例子,包含一个TextView作为列表项的显示内容:
- <!-- list_item_layout.xml -->
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="8dp">
- <TextView
- android:id="@+id/text_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="16sp"
- android:textColor="@android:color/black" />
- </LinearLayout>
在上面的示例中,我们使用了一个LinearLayout作为容器,内部包含一个TextView用于显示列表项的文本内容。你可以根据需要添加更多的布局和控件来实现不同的样式和交互效果。
3.在适配器中使用自定义布局:在适配器的getView()
方法中,将自定义的布局文件加载为视图,并根据需要进行填充和设置数据。
- @Override public View getView(int position, View convertView, ViewGroup parent) {
- // 创建或复用列表项视图
- if (convertView == null) {
- convertView=
- LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_layout, parent, false);
- }
- // 获取当前位置的数据项
- String item = (String) getItem(position);
- // 在列表项视图中显示数据
- TextView textView = convertView.findViewById(R.id.text_view);
- textView.setText(item);
- return convertView;
- }
在上述代码中,我们使用LayoutInflater
的inflate()
方法来将自定义的列表项布局文件加载为一个视图,并通过findViewById()
方法找到其中的TextView控件。然后,我们可以通过setText()
方法将适配器中的数据设置到TextView中。
完成以上步骤后,你就成功创建了一个自定义的列表项布局,并在适配器中使用它来展示列表项的内容。
要处理列表项的点击事件,你可以在适配器中为列表项设置点击监听器。下面是一个示例,展示了如何在适配器中处理列表项的点击事件:
1. 在适配器的getView()
方法中为列表项设置点击监听器:
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) { // 创建或复用列表项视图
- if (convertView == null) {
- convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_layout, parent, false);
- }
- // 获取当前位置的数据项
- String item = (String) getItem(position);
- // 在列表项视图中显示数据
- TextView textView = convertView.findViewById(R.id.text_view); textView.setText(item);
- // 设置点击监听器
- convertView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) { // 处理列表项点击事件
- // 在这里编写点击事件处理的代码
- // 可以使用position参数获取当前点击的列表项位置
- }
- });
- return convertView;
- }
在上述代码中,我们在convertView
(列表项视图)上设置了一个点击监听器,并在onClick()
方法中编写了处理点击事件的代码。你可以根据需要在此处编写适配器中列表项的点击逻辑。
注意,我们使用了position
参数来获取当前点击的列表项位置。这可以帮助你识别用户点击的是哪个列表项,并进行相应的操作。
2.添加点击事件处理逻辑:将具体的点击事件处理逻辑添加到适配器中的onClick()
方法中。例如,可以打开新的Activity或Fragment来显示详细信息,或执行其他操作。
- convertView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) { // 处理列表项点击事件
- // 在这里编写点击事件处理的代码
- // 可以使用position参数获取当前点击的列表项位置
- // 例如,打开新的Activity并传递数据
- String selectedItem = (String) getItem(position);
- Intent intent = new Intent(v.getContext(), DetailActivity.class);
- intent.putExtra("selectedItem", selectedItem);
- v.getContext().startActivity(intent);
- }
- });
在上面的示例中,我们根据用户点击的列表项位置获取了选中的数据项,然后创建了一个包含数据的Intent,并将其作为参数传递给新的Activity(DetailActivity)。你可以根据自己的需求进行修改和扩展。
优化列表视图可以提升应用的性能和用户体验。以下是一些常见的列表视图相关功能,包括搜索过滤、下拉刷新和侧滑菜单的实现方式:
搜索过滤功能:
下拉刷新功能:
setRefreshing(false)
方法结束刷新状态。侧滑菜单功能:
除了上述功能,还可以考虑以下优化措施提升列表视图的性能和用户体验:
这里主要解析三个列表:
- 简单列表--ArrayAdapter
- 图文列表--SimpleAdapter
- 图文复杂列表--BaseAdapter (重点)
【演示效果】:
ListView应用
【核心代码】:
1. ItemBean 类
创建一个bena包,在此包目录下创建一个ItemBean类,定义文章标题、内容、图片
- public class ItemBean implements Serializable {
-
- private String title; // 标题
- private String content; // 内容
- private int imgResId; // 图片
-
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this.content = content;
- }
-
- public int getImgResId() {
- return imgResId;
- }
-
- public void setImgResId(int imgResId) {
- this.imgResId = imgResId;
- }
-
- @Override
- public String toString() {
- return "ItemBean{" +
- "title='" + title + '\'' +
- ", content='" + content + '\'' +
- ", imgResId=" + imgResId +
- '}';
- }
- }
2.主页Button按钮
在 activity_main.xml文件编辑如下代码:
- <?xml version="1.0" encoding="utf-8"?>
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context=".MainActivity">
-
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginRight="10dp"
- android:orientation="vertical"
- android:paddingLeft="10dp">
-
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:text="Array列表"
- android:onClick="toArrayListTest"
- android:textAllCaps="false" />
-
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:text="simple列表"
- android:onClick="toSimpleListTest"
- android:textAllCaps="false" />
-
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:text="BaseAdapter列表"
- android:onClick="toBaseListTest"
- android:textAllCaps="false" />
-
- <Button
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:text="FensiList列表"
- android:onClick="toFrensiListActivity"
- android:textAllCaps="false" />
- </LinearLayout>
- </ScrollView>
【页面效果】:
【图2 activity_main.xml效果图】
3.在主程序类添加点击事件跳转至相应的列表视图
(1)创建5个空的活动,分别命名为:
【图3 活动类目录图】
(2)在 MainActivty 主程序类中添加点击事件,跳转至相应的列表视图
- public void toArrayListTest(View view) {
- Intent intent = new Intent(this, ArrayListActivity.class);
- startActivity(intent);
- }
-
- public void toSimpleListTest(View view) {
- Intent intent = new Intent(this, SimpleListActivity.class);
- startActivity(intent);
- }
-
- public void toBaseListTest(View view) {
- Intent intent = new Intent(this, BaseAdapterActivity.class);
- startActivity(intent);
- }
-
- public void toFrensiListActivity(View view) {
- Intent intent = new Intent(this, FensiListActivity.class);
- startActivity(intent);
- }
(3)在res/layout目录下的 activity_array_list.xml 、activity_base_adapter.xml、activity_simple_list.xml 这三个文件中定义 ListView 视图
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context=".BaseAdapterActivity">
-
- <ListView
- android:id="@+id/lv"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
-
- </LinearLayout>
(4)在res/layout目录下新建一个xml文件,将其命名为 list_item_layout.xml
- <?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="match_parent"
- android:paddingLeft="10dp"
- android:paddingTop="5dp"
- android:paddingRight="10dp"
- android:paddingBottom="5dp">
-
- <ImageView
- android:id="@+id/iv_img"
- android:layout_width="100dp"
- android:layout_height="100dp"
- android:scaleType="centerCrop"
- android:src="@drawable/ic_launcher_background" />
-
- <TextView
- android:id="@+id/tv_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="10dp"
- android:layout_toRightOf="@id/iv_img"
- android:ellipsize="end"
- android:maxLines="1"
- android:text="雨中漫步"
- android:textSize="20sp"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/tv_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/tv_title"
- android:layout_marginLeft="10dp"
- android:layout_toRightOf="@id/iv_img"
- android:ellipsize="end"
- android:maxLines="3"
- android:text="人生就像一场旅行,不必在乎目的地,在乎的是沿途的风景以及看风景的心情,让心灵去旅行"
- android:textSize="16sp" />
- </RelativeLayout>
【效果图】:
【图4 自定义列表项】
4. 简单列表--ArrayAdapter
(1) 新建一个 adapter 包,在此包目录下 新建 MyAdapter 适配器,用于 ArrayListActivity 活动的 适配器。
- public class MyAdapter extends BaseAdapter {
-
- private List<ItemBean> mBeanList;
- private LayoutInflater mLayoutInflater;
- private Context mContext;
-
- public MyAdapter(Context context,List<ItemBean> beanList){
- this.mContext = context;
- this.mBeanList = beanList;
- mLayoutInflater = LayoutInflater.from(mContext);
- }
-
- @Override
- public int getCount() {
- return mBeanList.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mBeanList.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // 创建视图控件
- convertView = mLayoutInflater.inflate(R.layout.list_item_layout, parent, false);
- ImageView imageView = convertView.findViewById(R.id.iv_img);
- TextView tvTitle = convertView.findViewById(R.id.tv_title);
- TextView tvContent = convertView.findViewById(R.id.tv_content);
-
- // 为控件填充数据
- ItemBean itemBean = mBeanList.get(position);
- imageView.setImageResource(itemBean.getImgResId());
- tvTitle.setText(itemBean.getTitle());
- tvContent.setText(itemBean.getContent());
-
- return convertView;
- }
- }
代码解析:
这里的 MyAdapter 类继承自 BaseAdapter,并实现了其中的几个方法。通过在 getView() 方法中为每个列表项填充子视图的数据,我们可以自定义列表项的布局和样式。
在 getView() 方法中,首先检查 convertView 是否为空,如果为空则使用 LayoutInflater 从布局文件
R.layout.list_item_layout
中创建一个新的视图。然后,通过 findViewById() 方法获取子视图布局中的控件,并将这些控件与数据源中的数据进行绑定。其中,ItemBean 是一个自定义的数据模型类,用于存储每个列表项的数据。通过调用 getItem() 方法获取对应位置的 ItemBean 对象,然后使用该对象的方法获取相应的数据,并将其设置到子视图控件中。
最后,返回填充好数据的视图对象供 ListView 进行显示。
(2).编辑 ArrayListActivity 类
- public class ArrayListActivity extends AppCompatActivity {
-
- private ListView mListView;
- private List<String> mStringList;
- private ArrayAdapter<String> mArrayAdapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_array_list);
- mListView = findViewById(R.id.lv);
- mStringList = new ArrayList<>();
-
- for (int i = 0; i < 50; i++) {
- mStringList.add("这是条目" + i);
- }
- mArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mStringList);
-
- mListView.setAdapter(mArrayAdapter);
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Toast.makeText(ArrayListActivity.this,"你点击了"+position,Toast.LENGTH_SHORT).show();
- }
- });
-
-
- mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
- Toast.makeText(ArrayListActivity.this,"你长按了"+position,Toast.LENGTH_SHORT).show();
-
- return true;
- }
- });
- }
- }
代码解析:
在 onCreate() 方法中,首先通过 setContentView() 方法设置布局文件为 activity_array_list.xml。然后,使用 findViewById() 方法获取到 ListView 控件的实例。
接下来,我们创建一个 ArrayList 对象作为数据源,并使用一个 for 循环向其中添加了 50 个条目字符串。然后,创建 ArrayAdapter 对象,将它与数据源关联,并将该适配器设置到 ListView 控件上,以便显示数据。
同时,我们还设置了列表项的点击事件和长按事件监听器。当用户点击列表项时,会弹出一个 Toast 提示框显示当前点击的位置。当用户长按列表项时,同样会弹出一个 Toast 提示框显示当前长按的位置。
这样,ArrayListActivity 类就完成了一个简单的基于 ArrayList 的列表显示,并为列表项的点击事件和长按事件提供了相应的处理逻辑。
5.图文列表--SimpleAdapter
(1) SimplListActivity 活动的适配器 MyAdapter2
因为在使用 ListView 的适配器时,通常会使用 convertView 进行视图的重用,会有 convertView 为空的情况,表示当前没有可重用的视图。
所以,我们在MyAdapter类的基础上,在getView()方法中,更改创建视图控件的部分代码,
进行判断convertView是否为空,如果 convertView 为空,则表示当前没有可重用的视图,需要创建新的视图。
- // 创建视图控件
- if (convertView == null) {
- convertView = mLayoutInflater.inflate(R.layout.list_item_layout, parent, false);
- }
(2) SimpleListActivity 类
- public class SimpleListActivity extends AppCompatActivity {
-
- private ListView mListView;
- private SimpleAdapter mSimpleAdapter;
- private List<Map<String, Object>> mList;
- private int[] imgs = {
- R.drawable.test1,
- R.drawable.test2,
- R.drawable.test3,
- R.drawable.test4,
- R.drawable.test5,
- R.drawable.test6,
- R.drawable.test7,
- R.drawable.test8,
- R.drawable.test9
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_simple_list);
- mListView = findViewById(R.id.lv);
-
- mList = new ArrayList<>();
-
- for (int i = 0; i < 50; i++) {
- Map<String, Object> map = new HashMap();
- map.put("img", imgs[i % imgs.length]);
- map.put("title", "这是标题" + i);
- map.put("content", "这是内容" + i);
-
- mList.add(map);
- }
-
- mSimpleAdapter = new SimpleAdapter(this,
- mList,
- R.layout.list_item_layout,
- new String[]{"img", "title", "content"},
- new int[]{R.id.iv_img, R.id.tv_title, R.id.tv_content}
- );
-
- mListView.setAdapter(mSimpleAdapter);
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Map<String, Object> map = mList.get(position);
- String title = (String) map.get("title");
- Toast.makeText(SimpleListActivity.this, "你点击了" + position + title, Toast.LENGTH_SHORT).show();
-
- }
- });
-
- }
- }
代码解析:
在 onCreate() 方法中,首先通过 setContentView() 方法设置布局文件为 activity_simple_list.xml。然后使用 findViewById() 方法获取到 ListView 控件的实例。
接下来,我们创建一个 ArrayList 对象作为数据源 mList,并使用循环为其添加50个条目。每个条目都是一个 HashMap 对象,其中包含了图片、标题和内容等信息。
然后,创建了一个 SimpleAdapter 对象 mSimpleAdapter,并将其与数据源 mList 关联。在构造 SimpleAdapter 对象时,我们指定了列表项的布局文件 list_item_layout.xml,以及数据源 mList 中各项数据对应的键和列表项布局中的控件 ID。
最后,将 SimpleAdapter 设置到 ListView 控件上,以便显示数据。同时,我们还设置了列表项的点击事件监听器。当用户点击列表项时,会通过 getItem() 方法获取当前点击位置的数据项,然后从中取出标题信息并弹出一个 Toast 提示框显示当前点击的位置和标题。
这样,SimpleListActivity 类就完成了一个基于 SimpleAdapter 的简单列表显示,并为列表项的点击事件提供了相应的处理逻辑。
6. 图文复杂列表--BaseAdapter (重点)
(1)BaseAdapterActivity活动的适配器 MyAdapter3
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // 创建视图控件
- ViewHolder viewHolder = new ViewHolder();
- if (convertView == null) {
- convertView = mLayoutInflater.inflate(R.layout.list_item_layout, parent, false);
- ImageView imageView = convertView.findViewById(R.id.iv_img);
- TextView tvTitle = convertView.findViewById(R.id.tv_title);
- TextView tvContent = convertView.findViewById(R.id.tv_content);
- viewHolder.imageView = imageView;
- viewHolder.tvTitle = tvTitle;
- viewHolder.tvContent = tvContent;
- convertView.setTag(viewHolder);
- } else {
- viewHolder = (ViewHolder) convertView.getTag();
- }
-
- // 为控件填充数据
- ItemBean itemBean = mBeanList.get(position);
- viewHolder.imageView.setImageResource(itemBean.getImgResId());
- viewHolder.tvTitle.setText(itemBean.getTitle());
- viewHolder.tvContent.setText(itemBean.getContent());
-
- return convertView;
- }
-
- class ViewHolder {
- ImageView imageView;
- TextView tvTitle;
- TextView tvContent;
- }
代码解析:
在 getView() 方法中,首先创建了一个名为 ViewHolder 的内部类,用于存储列表项布局中的各个控件实例。
然后,在判断 convertView 是否为空的条件下,进行了两个分支操作:
- 当 convertView 为空时,表示当前列表项需要被创建。通过 LayoutInflater 的 inflate() 方法将布局文件 list_item_layout.xml 转换为 View 对象,并找到其中的 ImageView、TextView 控件。将它们的实例赋值给 ViewHolder 对应的成员变量,并将 ViewHolder 对象通过 setTag() 方法附加到 convertView 上,以便稍后利用 getTag() 方法取出。
- 当 convertView 非空时,表示当前列表项可以复用。直接使用 getTag() 方法从 convertView 中取出之前存储的 ViewHolder 对象。
将数据填充到 ViewHolder 中的控件时,通过 getItem() 方法获取当前位置的 ItemBean 对象,并从中取出图片资源 ID、标题和内容的信息。然后,分别将这些信息设置到 ViewHolder 中相应的控件上,完成了数据的填充。
最后,返回填充好数据的 convertView,用于显示在列表中。
这样,在 SimpleAdapter 中重写了 getView() 方法,实现了对列表项布局的动态创建和复用,并将数据正确地填充到控件中,使得列表能够正确显示数据。同时,使用 ViewHolder 模式可以提高列表的滚动性能。
(2) 编辑 activity_news_detail.xml 文件
- <?xml version="1.0" encoding="utf-8"?>
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- tools:context=".NewsDetailActivity">
-
- <TextView
- android:id="@+id/tv_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:gravity="center"
- android:text="林间漫步"
- android:textSize="25sp"
- android:textStyle="bold" />
-
- <ImageView
- android:id="@+id/iv_img"
- android:layout_width="400dp"
- android:layout_height="300dp"
- android:layout_marginTop="10dp"
- android:scaleType="centerCrop"
- android:src="@color/colorPrimaryDark" />
-
- <TextView
- android:id="@+id/tv_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="40dp"
- android:gravity="start|left"
- android:text="一根线如果绷得太紧,总有一天会断裂,一颗心若是禁锢得太久,总有一天会失去平衡,我们需要放飞心灵,让心翱翔在自由的太空。"
- android:textSize="16sp"
-
- />
-
- </LinearLayout>
- </ScrollView>
【效果图】:
【图5 activity_news_detail.xml 效果图】
(3) NewsDetailActivity 类
- package com.sziiit.listview_demo1;
-
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.AdapterView;
- import android.widget.ListView;
- import android.widget.Toast;
-
- import androidx.appcompat.app.AppCompatActivity;
-
- import com.sziiit.listview_demo1.adapter.MyAdapter3;
- import com.sziiit.listview_demo1.bean.ItemBean;
-
- import java.util.ArrayList;
- import java.util.List;
-
- public class BaseAdapterActivity extends AppCompatActivity {
-
- private ListView mListView;
- private List<ItemBean> mBeanList;
- private MyAdapter3 mMyAdapter;
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_base_adapter);
-
- initView();
- initData();
- initEvent();
- }
-
- private void initEvent() {
- mMyAdapter = new MyAdapter3(this,mBeanList);
- mListView.setAdapter(mMyAdapter);
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- ItemBean itemBean = mBeanList.get(position);
- String title = itemBean.getTitle();
- Intent intent = new Intent(BaseAdapterActivity.this, NewsDetailActivity.class);
- intent.putExtra("item",itemBean);
- startActivity(intent);
-
- Toast.makeText(BaseAdapterActivity.this, "你点击了" + position + title, Toast.LENGTH_SHORT).show();
- }
- });
- mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
-
- return false;
- }
- });
- }
-
- private void initData() {
- mBeanList = new ArrayList<>();
-
- ItemBean newsBean1 = new ItemBean();
- newsBean1.setTitle("雨中漫步");
- newsBean1.setContent("人生就像一场旅行,不必在乎目的地,在乎的是沿途的风景以及看风景的心情,让心灵去旅行。\n只想进行一场漫无目的的旅行,在一个有花有海、安静缓慢的地方晒着太阳无所事事。\n背着背包的路上,看过许多人,听过许多故事,见过旅行风景,就这样,慢慢学会了长大。");
- newsBean1.setImgResId(R.drawable.test1);
-
- ItemBean newsBean2 = new ItemBean();
- newsBean2.setTitle("林间穿梭");
- newsBean2.setContent("只想进行一场漫无目的的旅行,在一个有花有海、安静缓慢的地方晒着太阳无所事事。\n路旁边浪似地滚着高高低低的黄土。太阳给埋在黄土里,发着肉红色。可是太阳还烧得怪起劲的,把他们的皮肉烧得变成紫黑色,似乎还闻得到一股焦味儿。");
- newsBean2.setImgResId(R.drawable.test2);
-
- ItemBean newsBean3 = new ItemBean();
- newsBean3.setTitle("旅行花海");
- newsBean3.setContent("只想进行一场漫无目的的旅行,在一个有花有海、安静缓慢的地方晒着太阳无所事事。\n背着背包的路上,看过许多人,听过许多故事,见过旅行风景,就这样,慢慢学会了长大。\n" +
- "\n" +
- "想呼吸着每座城市的空气,想感受着每座城市的人儿,想看着每座城市的风景。");
- newsBean3.setImgResId(R.drawable.test3);
-
-
- ItemBean newsBean4 = new ItemBean();
- newsBean4.setTitle("非平衡的线");
- newsBean4.setContent("一根线如果绷得太紧,总有一天会断裂,一颗心若是禁锢得太久,总有一天会失去平衡,我们需要放飞心灵,让心翱翔在自由的太空。每个人在他的人生发轫之初,总有一段时光,没有什么可留恋,只有抑制不住的梦想,没有什么可凭仗,只有他的好身体,没有地方可去,只想到处流浪。");
- newsBean4.setImgResId(R.drawable.test4);
-
- ItemBean newsBean5 = new ItemBean();
- newsBean5.setTitle("说走就走");
- newsBean5.setContent("说走就走的旅行,要么缘由幸福稳定和宽裕,要么祸起无力无奈和逃避。\n在旅途中,我遇见了你,你我相识是缘分!看着你手中的戒指,你说,你可以把它取下来吗?当我要取的时候,你淘气的躲开了,你说只有有缘人才可以取下,我看着你手中的戒指,想做你的有缘人,可是我知道结果是惨淡的,但还是心存希望!");
- newsBean5.setImgResId(R.drawable.test5);
-
- ItemBean newsBean6 = new ItemBean();
- newsBean6.setTitle("美好的记忆");
- newsBean6.setContent("旅行不在乎终点,而是在意途中的人和事还有那些美好的记忆和景色。人生就是一次充满未知的旅行,在乎的是沿途的风景,在乎的是看风景的心情,旅行不会因为美丽的风景终止。走过的路成为背后的风景,不能回头不能停留,若此刻停留,将会错过更好的风景,保持一份平和,保持一份清醒。享受每一刻的感觉,欣赏每一处的风景,这就是人生。(作者:《遇到另一种生活》");
- newsBean6.setImgResId(R.drawable.test6);
-
- ItemBean newsBean7 = new ItemBean();
- newsBean7.setTitle("久违的感动");
- newsBean7.setContent("人生最好的旅行,就是你在一个陌生的地方,发现一种久违的感动。人生最好的旅行,就是你在一个陌生的地方,发现一种久违的感动");
- newsBean7.setImgResId(R.drawable.test7);
-
- ItemBean newsBean8 = new ItemBean();
- newsBean8.setTitle("流浪日记");
- newsBean8.setContent("着背包的路上,看过许多人,听过许多故事,见过旅行风景,就这样,慢慢学会了长大。\n每个人在他的人生发轫之初,总有一段时光,没有什么可留恋,只有抑制不住的梦想,没有什么可凭仗,只有他的好身体,没有地方可去,只想到处流浪。");
- newsBean8.setImgResId(R.drawable.test8);
-
- ItemBean newsBean9 = new ItemBean();
- newsBean9.setTitle("山的尽头");
- newsBean9.setContent("在向山靠近一点,才发现这座山,好象一位诗人遥望远方,等待故人的归来。山上的树,大多数是松树比较突出。松树亭亭玉立的耸立在周围小草小花的中间,仿佛松树就是一位威风的将军,守护着国家的国民。\n在向山靠近一点,才发现这座山,好象一位诗人遥望远方,等待故人的归来。山上的树,大多数是松树比较突出。松树亭亭玉立的耸立在周围小草小花的中间,仿佛松树就是一位威风的将军,守护着国家的国民。\n在向山靠近一点,才发现这座山,好象一位诗人遥望远方,等待故人的归来。山上的树,大多数是松树比较突出。松树亭亭玉立的耸立在周围小草小花的中间,仿佛松树就是一位威风的将军,守护着国家的国民。");
- newsBean9.setImgResId(R.drawable.test9);
-
- ItemBean newsBean10 = new ItemBean();
- newsBean10.setTitle("秦河魅影");
- newsBean10.setContent("秦淮河、夫子庙,繁华依旧,只少了些当年桨声灯影的风味。喝一碗鸭血粉丝,吃一客蟹黄汤包,坐一次龙舟游船,览一河别样风景,发一回思古幽情,我想,这也许就是所谓夜游秦淮的小资情调了。独游,却只有看客的心情。\n秦淮河、夫子庙,繁华依旧,只少了些当年桨声灯影的风味。喝一碗鸭血粉丝,吃一客蟹黄汤包,坐一次龙舟游船,览一河别样风景,发一回思古幽情,我想,这也许就是所谓夜游秦淮的小资情调了。独游,却只有看客的心情。\n秦淮河、夫子庙,繁华依旧,只少了些当年桨声灯影的风味。喝一碗鸭血粉丝,吃一客蟹黄汤包,坐一次龙舟游船,览一河别样风景,发一回思古幽情,我想,这也许就是所谓夜游秦淮的小资情调了。独游,却只有看客的心情。");
- newsBean10.setImgResId(R.drawable.test10);
-
- mBeanList.add(newsBean1);
- mBeanList.add(newsBean2);
- mBeanList.add(newsBean3);
- mBeanList.add(newsBean4);
- mBeanList.add(newsBean5);
- mBeanList.add(newsBean6);
- mBeanList.add(newsBean7);
- mBeanList.add(newsBean8);
- mBeanList.add(newsBean9);
- mBeanList.add(newsBean10);
- mBeanList.add(newsBean1);
- mBeanList.add(newsBean2);
- mBeanList.add(newsBean3);
- mBeanList.add(newsBean4);
- mBeanList.add(newsBean5);
- mBeanList.add(newsBean6);
- mBeanList.add(newsBean7);
- mBeanList.add(newsBean8);
- mBeanList.add(newsBean9);
- mBeanList.add(newsBean10);
- }
-
- private void initView() {
- mListView = findViewById(R.id.lv);
- }
- }
(4) BaseAdapterActivity 类
- public class BaseAdapterActivity extends AppCompatActivity {
-
- private ListView mListView;
- private List<ItemBean> mBeanList;
- private MyAdapter3 mMyAdapter;
-
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_base_adapter);
-
- initView();
- initData();
- initEvent();
- }
-
- private void initEvent() {
- mMyAdapter = new MyAdapter3(this,mBeanList);
- mListView.setAdapter(mMyAdapter);
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- ItemBean itemBean = mBeanList.get(position);
- String title = itemBean.getTitle();
- Intent intent = new Intent(BaseAdapterActivity.this, NewsDetailActivity.class);
- intent.putExtra("item",itemBean);
- startActivity(intent);
-
- Toast.makeText(BaseAdapterActivity.this, "你点击了" + position + title, Toast.LENGTH_SHORT).show();
- }
- });
- mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
-
- return false;
- }
- });
- }
-
- private void initData() {
- mBeanList = new ArrayList<>();
-
- ItemBean newsBean1 = new ItemBean();
- newsBean1.setTitle("雨中漫步");
- newsBean1.setContent("人生就像一场旅行,不必在乎目的地,在乎的是沿途的风景以及看风景的心情,让心灵去旅行。\n只想进行一场漫无目的的旅行,在一个有花有海、安静缓慢的地方晒着太阳无所事事。\n背着背包的路上,看过许多人,听过许多故事,见过旅行风景,就这样,慢慢学会了长大。");
- newsBean1.setImgResId(R.drawable.test1);
-
- ItemBean newsBean2 = new ItemBean();
- newsBean2.setTitle("林间穿梭");
- newsBean2.setContent("只想进行一场漫无目的的旅行,在一个有花有海、安静缓慢的地方晒着太阳无所事事。\n路旁边浪似地滚着高高低低的黄土。太阳给埋在黄土里,发着肉红色。可是太阳还烧得怪起劲的,把他们的皮肉烧得变成紫黑色,似乎还闻得到一股焦味儿。");
- newsBean2.setImgResId(R.drawable.test2);
-
- ItemBean newsBean3 = new ItemBean();
- newsBean3.setTitle("旅行花海");
- newsBean3.setContent("只想进行一场漫无目的的旅行,在一个有花有海、安静缓慢的地方晒着太阳无所事事。\n背着背包的路上,看过许多人,听过许多故事,见过旅行风景,就这样,慢慢学会了长大。\n" +
- "\n" +
- "想呼吸着每座城市的空气,想感受着每座城市的人儿,想看着每座城市的风景。");
- newsBean3.setImgResId(R.drawable.test3);
-
-
- ItemBean newsBean4 = new ItemBean();
- newsBean4.setTitle("非平衡的线");
- newsBean4.setContent("一根线如果绷得太紧,总有一天会断裂,一颗心若是禁锢得太久,总有一天会失去平衡,我们需要放飞心灵,让心翱翔在自由的太空。每个人在他的人生发轫之初,总有一段时光,没有什么可留恋,只有抑制不住的梦想,没有什么可凭仗,只有他的好身体,没有地方可去,只想到处流浪。");
- newsBean4.setImgResId(R.drawable.test4);
-
- ItemBean newsBean5 = new ItemBean();
- newsBean5.setTitle("说走就走");
- newsBean5.setContent("说走就走的旅行,要么缘由幸福稳定和宽裕,要么祸起无力无奈和逃避。\n在旅途中,我遇见了你,你我相识是缘分!看着你手中的戒指,你说,你可以把它取下来吗?当我要取的时候,你淘气的躲开了,你说只有有缘人才可以取下,我看着你手中的戒指,想做你的有缘人,可是我知道结果是惨淡的,但还是心存希望!");
- newsBean5.setImgResId(R.drawable.test5);
-
- ItemBean newsBean6 = new ItemBean();
- newsBean6.setTitle("美好的记忆");
- newsBean6.setContent("旅行不在乎终点,而是在意途中的人和事还有那些美好的记忆和景色。人生就是一次充满未知的旅行,在乎的是沿途的风景,在乎的是看风景的心情,旅行不会因为美丽的风景终止。走过的路成为背后的风景,不能回头不能停留,若此刻停留,将会错过更好的风景,保持一份平和,保持一份清醒。享受每一刻的感觉,欣赏每一处的风景,这就是人生。(作者:《遇到另一种生活》");
- newsBean6.setImgResId(R.drawable.test6);
-
- ItemBean newsBean7 = new ItemBean();
- newsBean7.setTitle("久违的感动");
- newsBean7.setContent("人生最好的旅行,就是你在一个陌生的地方,发现一种久违的感动。人生最好的旅行,就是你在一个陌生的地方,发现一种久违的感动");
- newsBean7.setImgResId(R.drawable.test7);
-
- ItemBean newsBean8 = new ItemBean();
- newsBean8.setTitle("流浪日记");
- newsBean8.setContent("着背包的路上,看过许多人,听过许多故事,见过旅行风景,就这样,慢慢学会了长大。\n每个人在他的人生发轫之初,总有一段时光,没有什么可留恋,只有抑制不住的梦想,没有什么可凭仗,只有他的好身体,没有地方可去,只想到处流浪。");
- newsBean8.setImgResId(R.drawable.test8);
-
- ItemBean newsBean9 = new ItemBean();
- newsBean9.setTitle("山的尽头");
- newsBean9.setContent("在向山靠近一点,才发现这座山,好象一位诗人遥望远方,等待故人的归来。山上的树,大多数是松树比较突出。松树亭亭玉立的耸立在周围小草小花的中间,仿佛松树就是一位威风的将军,守护着国家的国民。\n在向山靠近一点,才发现这座山,好象一位诗人遥望远方,等待故人的归来。山上的树,大多数是松树比较突出。松树亭亭玉立的耸立在周围小草小花的中间,仿佛松树就是一位威风的将军,守护着国家的国民。\n在向山靠近一点,才发现这座山,好象一位诗人遥望远方,等待故人的归来。山上的树,大多数是松树比较突出。松树亭亭玉立的耸立在周围小草小花的中间,仿佛松树就是一位威风的将军,守护着国家的国民。");
- newsBean9.setImgResId(R.drawable.test9);
-
- ItemBean newsBean10 = new ItemBean();
- newsBean10.setTitle("秦河魅影");
- newsBean10.setContent("秦淮河、夫子庙,繁华依旧,只少了些当年桨声灯影的风味。喝一碗鸭血粉丝,吃一客蟹黄汤包,坐一次龙舟游船,览一河别样风景,发一回思古幽情,我想,这也许就是所谓夜游秦淮的小资情调了。独游,却只有看客的心情。\n秦淮河、夫子庙,繁华依旧,只少了些当年桨声灯影的风味。喝一碗鸭血粉丝,吃一客蟹黄汤包,坐一次龙舟游船,览一河别样风景,发一回思古幽情,我想,这也许就是所谓夜游秦淮的小资情调了。独游,却只有看客的心情。\n秦淮河、夫子庙,繁华依旧,只少了些当年桨声灯影的风味。喝一碗鸭血粉丝,吃一客蟹黄汤包,坐一次龙舟游船,览一河别样风景,发一回思古幽情,我想,这也许就是所谓夜游秦淮的小资情调了。独游,却只有看客的心情。");
- newsBean10.setImgResId(R.drawable.test10);
-
- mBeanList.add(newsBean1);
- mBeanList.add(newsBean2);
- mBeanList.add(newsBean3);
- mBeanList.add(newsBean4);
- mBeanList.add(newsBean5);
- mBeanList.add(newsBean6);
- mBeanList.add(newsBean7);
- mBeanList.add(newsBean8);
- mBeanList.add(newsBean9);
- mBeanList.add(newsBean10);
- mBeanList.add(newsBean1);
- mBeanList.add(newsBean2);
- mBeanList.add(newsBean3);
- mBeanList.add(newsBean4);
- mBeanList.add(newsBean5);
- mBeanList.add(newsBean6);
- mBeanList.add(newsBean7);
- mBeanList.add(newsBean8);
- mBeanList.add(newsBean9);
- mBeanList.add(newsBean10);
- }
-
- private void initView() {
- mListView = findViewById(R.id.lv);
- }
- }
代码解析:
在
onCreate
方法中,设置布局文件并初始化视图、数据和事件监听器。然后,在initEvent
方法中,创建适配器MyAdapter3
并将其设置给ListView,并为ListView设置点击和长按事件监听器。适配器
MyAdapter3
继承自BaseAdapter,通过重写相应方法,可以自定义ListView的展示效果。在构造方法中传入Context和数据List<ItemBean>,供后续使用。在getView方法中,根据position获取对应位置的ItemBean对象,并通过LayoutInflater加载布局文件,并将数据填充到相应的控件中,最后返回该View。
在学习 Android 的 ListView 视图列表之后,我对列表的使用和管理有了更深入的了解。通过实践和研究,我成功地创建了一个包含多个数据项的列表,并且能够在列表中显示相关的数据。我学会了使用适配器来连接数据和视图,并将其绑定到 ListView 控件上。我还学会了处理列表项的点击事件,并可以根据用户的操作做出相应的响应。此外,我还掌握了如何优化列表的性能,避免因为数据量过大导致的卡顿问题。通过使用 ViewHolder 模式和合理的内存管理,我能够有效地提高列表的效率。
在未来,我计划进一步探索和应用更高级的功能和技术来扩展我对 ListView 视图列表的认识。首先,我打算学习如何自定义列表项的布局和样式,以满足不同的设计需求。通过使用自定义的布局文件和样式,我可以为每个列表项添加更多的信息和交互元素,提供更好的用户体验。其次,我将进一步研究和实践分页加载和异步加载,以优化大数据量下的列表显示和性能。这将使得列表能够快速加载和显示大量的数据,并且可以实现平滑的滚动体验。另外,我还计划深入理解 RecyclerView 的使用,这是一个更灵活和高效的列表控件,能够满足更复杂的数据展示需求。最终,我希望能够将所学到的知识和技术应用到实际的 Android 应用开发中,为用户提供更好的列表展示和交互体验。
[1] 赖红 ,Android 应用开发基础 [M.] 北京:电子工业出版社,2020.3
[2] http://www.android-studio,org/, Android Stuiod 安卓开发者社区.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。