当前位置: 首页 > news >正文

【Android】ListView和RecyclerView知识总结

文章目录

  • ListView
    • 步骤
    • 适配器Adpter
    • ArrayAdapter
    • SimpleAdapter
    • BaseAdpter
    • 效率问题
  • RecyclerView
    • 具体实现
    • 不同布局形式的设置
      • 横向滚动
      • 瀑布流
      • 网格
    • 点击事件

ListView

ListView 是 Android 中的一种视图组件,用于显示可滚动的垂直列表。每个列表项都是一个视图对象,ListView 会通过适配器(Adapter)将数据绑定到这些视图对象上。它通常用于显示一组相似的数据,比如联系人列表、消息列表等。

步骤

  1. 准备数据:这是数据源,通常是一个数组或一个List
  2. 构建适配器:适配器用于将数据映射到ListView的每一项。你可以使用系统提供的适配器(如ArrayAdapter)或者自定义适配器。
  3. 绑定适配器到ListView:将适配器设置到ListView上。
  4. 处理ListView项的点击事件:添加点击事件监听器来处理每一项的点击事件。

适配器Adpter

作用:充当 ListView 和数据源之间的桥梁,将数据源转换为可以显示在 ListView 中的视图项。

image-20240723093657352

常用适配器类型

  1. ArrayAdapter:适用于简单的数据源,如数组或列表。它将每个数据项转换为一个 TextView 或其他简单视图。
  2. SimpleAdapter:用于将复杂的数据源(如 List<Map<String, Object>>)绑定到多个视图。
  3. Custom Adapter:通过继承 BaseAdapter 或其他适配器类,可以创建自定义适配器以实现复杂的需求。

ArrayAdapter

新建一个ArrayListActivity类及其布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>

添加入一个ListView控件

public class ArrayListActivity extends AppCompatActivity {private ListView mListView;private List<String> mStringList;private ArrayAdapter<String> mArrayAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_array_list);// 初始化 ListView。mListView = findViewById(R.id.lv);// 初始化字符串列表并填充数据。mStringList = new ArrayList<>();for (int i = 0; i < 50; i++) {mStringList.add("这是条目" + i);}// 使用 ArrayAdapter 绑定数据到 ListView。mArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mStringList);mListView.setAdapter(mArrayAdapter);// 设置 ListView 的点击事件。mListView.setOnItemClickListener((parent, view, position, id) -> {Toast.makeText(ArrayListActivity.this, "你点击了" + position, Toast.LENGTH_SHORT).show();});// 设置 ListView 的长按事件。mListView.setOnItemLongClickListener((parent, view, position, id) -> {Toast.makeText(ArrayListActivity.this, "你长按了" + position, Toast.LENGTH_SHORT).show();return true;});}
}
  • mListView = findViewById(R.id.lv);

    获得ListView组件

  • mStringList = new ArrayList<>();

    创建一个list储存信息

  • mArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mStringList);

    1. 创建了一个ArrayAdapter对象,命名为mArrayAdapter。
    2. 通过构造函数传入了三个参数:
      • this:表示当前的上下文对象,即这个ArrayAdapter将被用于当前的Activity或Fragment中。
      • android.R.layout.simple_list_item_1:表示列表项的布局文件,这里使用了Android系统提供的简单列表项布局,该布局只有一个文本视图。
      • mStringList:表示要显示的数据集合,即一个字符串列表。

    通过这个ArrayAdapter对象,可以将mStringList中的字符串显示在列表视图中。

  • mListView.setAdapter(mArrayAdapter);

    mArrayAdapter对象设置为mListView的适配器

实现效果:

Screenshot_2024-07-23-10-28-26-233_com.example.la

SimpleAdapter

SimpleAdapter是Android中用于将数据模型转换成ListView或其他视图组件的适配器。它简化了数据绑定过程,通过映射数据集中的字段到布局文件中的视图

同样新建一个ArrayListActivity类及其布局文件

<?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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent" /></LinearLayout>

xml和刚才一致

public class SimpleListActivity extends AppCompatActivity {private ListView mListView;private SimpleAdapter msimpleAdapter;private List<Map<String, Object>> mList;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_simple_list);// 初始化ListViewmListView = findViewById(R.id.lv);// 初始化并填充列表数据mList = new ArrayList<>();for (int i = 0; i < 5; i++) {Map<String, Object> map1 = new HashMap<>();map1.put("image", R.drawable.apple);map1.put("text", "苹果");mList.add(map1);Map<String, Object> map2 = new HashMap<>();map2.put("image", R.drawable.banana);map2.put("text", "香蕉");mList.add(map2);Map<String, Object> map3 = new HashMap<>();map3.put("image", R.drawable.litchi);map3.put("text", "荔枝");mList.add(map3);}// 使用SimpleAdapter绑定数据到ListViewmsimpleAdapter = new SimpleAdapter(this, mList, R.layout.list_item_layout,new String[]{"image", "text"}, new int[]{R.id.image, R.id.tv});mListView.setAdapter(msimpleAdapter);} 
}
  • msimpleAdapter = new SimpleAdapter(this, mList, R.layout.list_item_layout,

    new String[]{"image", "text"}, new int[]{R.id.image, R.id.tv});

    1. 创建了一个SimpleAdapter对象,用于将数据绑定到ListView上。
    2. this:上下文对象,表示当前的Activity或Fragment。
    3. mList:数据源,通常是一个List集合,包含了要展示的数据。
    4. R.layout.list_item_layout:不同于刚才安卓自带的,这里我们使用自己创建的布局,下面的两个参数String[]是刚才map数组中的键,int[]是自定义布局中对应的视图id
    5. new String[]{"image", "text"}:数据源中要展示的字段名数组,这里表示要展示名为"image"和"text"的字段。
    6. new int[]{R.id.image, R.id.tv}:对应字段在布局文件中的控件ID数组,这里表示字段"image"对应ID为R.id.image的ImageView控件,字段"text"对应ID为R.id.tv的TextView控件
  • mListView.setAdapter(msimpleAdapter);

R.layout.list_item_layout:自定义的布局

<?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"><ImageViewandroid:id="@+id/image"android:layout_width="0dp"android:layout_height="100dp"android:layout_weight="1" /><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="3" /></LinearLayout>

实际效果:
在这里插入图片描述

BaseAdpter

新建一个ItemBean类来存储数据

public class ItemBean {private String name;private int imageId;public ItemBean(String name, int imageId) {this.name = name;this.imageId = imageId;}public void setName(String name) {this.name = name;}public void setImageId(int imageId) {this.imageId = imageId;}public String getName() {return name;}public int getImageId() {return imageId;}
}

接下来为这个类构造适配器

继承BaseAdapter类,自动生成四个重写方法

/*** 自定义适配器,用于在ListView中显示物品列表。* 该适配器负责将数据集中的每个项目绑定到相应的视图上。*/
public class MyAdapter extends BaseAdapter {// 存储物品数据的列表private List<ItemBean> mlist;// LayoutInflater用于从XML文件中加载布局private LayoutInflater mLayoutInflater;// 上下文对象,用于获取资源和进行其他上下文相关的操作private Context mcontext;/*** 构造函数初始化适配器。* * @param mlist 物品数据的列表* @param mcontext 上下文对象,用于初始化LayoutInflater*/public MyAdapter(List<ItemBean> mlist, Context mcontext) {this.mlist = mlist;this.mcontext = mcontext;this.mLayoutInflater = LayoutInflater.from(mcontext);}//返回数据集的大小@Overridepublic int getCount() {return mlist.size();}//根据位置获取数据集中的物品。@Overridepublic Object getItem(int position) {return mlist.get(position);}// 获取物品在ListView中的唯一标识符@Overridepublic long getItemId(int position) {return position;}/*** 为ListView的每个项目创建并返回一个视图。* * @param position 物品的位置* @param convertView 当前被重用的视图* @param parent 视图的父容器* @return 绑定到数据集中的视图*/@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// 使用LayoutInflater加载列表项的布局convertView = mLayoutInflater.inflate(R.layout.list_item_layout, parent, false);// 查找并初始化列表项中的ImageView和TextViewImageView img = convertView.findViewById(R.id.image);TextView tv = convertView.findViewById(R.id.tv);// 获取当前位置的物品数据,设置ImageView的图片资源ItemBean itemBean = mlist.get(position);img.setImageResource(itemBean.getImageId());tv.setText(itemBean.getName());return convertView;}
}
  • convertView = mLayoutInflater.inflate(R.layout.list_item_layout, parent, false);

    mLayoutInflater通过构造方法处的LayoutInflater.from(mcontext)赋值

    调用其inflate方法:

    1. convertView: 这个变量通常用来存储从布局文件转换而来的View对象。在ListViewRecyclerViewgetView()方法中,它会被检查是否为空。如果非空,那么会直接使用这个View对象来展示数据,以避免频繁创建新的View,从而提高性能。
    2. mLayoutInflater: 这是一个LayoutInflater实例,它是Android框架提供的一个类,用于将XML布局文件转换为对应的View对象。
    3. R.layout.list_item_layout: 这是一个资源ID,指向了XML布局文件list_item_layout.xml。这个布局文件定义了一个ListViewRecyclerView中单个item的外观和结构,包括控件的类型、大小、位置以及样式等。
    4. parent: 这是一个ViewGroup类型的参数,代表了新创建的View最终将被添加到的父容器。在ListViewRecyclerView的情况下,parent就是ListViewRecyclerView本身。虽然在这个调用中,我们并没有立即把新创建的View添加到parent中(因为false参数),但这个参数还是需要的,因为它会影响LayoutInflater如何计算View的尺寸和位置。
    5. false: 这个布尔值参数告诉LayoutInflater不要将生成的View添加到parent中。这是因为ListViewRecyclerView有自己的逻辑来管理子View的添加和移除,它们会在适当的时候将View添加到自己内部的ViewGroup中。

    下面是BaseAdpterActivity类,和前两个步骤相似

    public class BaseAdpterActivity extends AppCompatActivity {private ListView mListView;private BaseAdapter mBaseAdapter;private List<ItemBean> mlist;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_base_adpter);// 初始化数据列表,这里使用了循环来添加多个数据项。mlist = new ArrayList<>();for (int i = 0; i < 20; i++) {ItemBean itemBean1 = new ItemBean("apple", R.drawable.apple);ItemBean itemBean2 = new ItemBean("banana", R.drawable.banana);ItemBean itemBean3 = new ItemBean("litchi", R.drawable.litchi);mlist.add(itemBean1);mlist.add(itemBean2);mlist.add(itemBean3);}// 初始化ListView和其适配器mListView = findViewById(R.id.lv);mBaseAdapter = new MyAdapter(mlist, this);mListView.setAdapter(mBaseAdapter);}
    }
    

    效果:

    Screenshot_2024-07-23-12-24-11-021_com.example.la

效率问题

列表中信息的出现需要频繁调用getView方法,效率很低。

public class MyAdapter extends BaseAdapter {//省略这里代码.......@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder;if (convertView == null) {// 如果convertView为空,说明该视图是第一次被创建convertView = mLayoutInflater.inflate(R.layout.list_item_layout, parent, false);viewHolder = new ViewHolder();viewHolder.img = convertView.findViewById(R.id.image);viewHolder.tv = convertView.findViewById(R.id.tv);convertView.setTag(viewHolder);} else {// 如果convertView不为空,说明该视图可以被复用,直接从标签中获取ViewHolder对象viewHolder = (ViewHolder) convertView.getTag();}// 获取当前位置的物品数据ItemBean itemBean = mlist.get(position);// 绑定数据到视图viewHolder.img.setImageResource(itemBean.getImageId());viewHolder.tv.setText(itemBean.getName());return convertView;}/*** ViewHolder类用于缓存ListView项的视图引用,* 以减少在getView方法中每次都需要通过findViewById查找视图的开销。*/class ViewHolder {ImageView img;TextView tv;}
}
  1. convertView为空:创建一个新的视图并初始化它,然后使用 setTag() 方法将一个包含子控件引用的对象与 convertView 关联起来。
  2. convertView不为null,则调用 getTag() 方法来获取之前存储的 ViewHolder 对象,这样就可以直接访问子控件而不需要再次查找它们。
  3. 使用setTag()getTag()方法来缓存这些子控件的引用
  4. ViewHolder:每次getView()方法被调用时,如果直接在视图中使用findViewById()方法来获取子视图,这会导致性能下降,因为findViewById()是一个耗时的操作。ViewHolder模式通过在视图首次创建时保存对所有子视图的引用,避免了后续滚动时的多次查找

RecyclerView

RecyclerView 是 Android 提供的一个更高级和灵活的列表视图控件,相对于 ListViewRecyclerView 提供了更多的功能和更好的性能。它引入了一些新的概念,如 ViewHolder 模式,更高效的滚动和动画支持,以及更灵活的布局管理器(LayoutManager)。

具体实现

可以实现和ListView一致的效果

Screenshot_2024-07-23-12-24-11-021_com.example.la

示例目录:

image-20240723154019488

  1. 主布局文件 activity_main.xml

加入RecyclerView控件,宽高设置match_parent占满布局。

<?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:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rlv"android:layout_width="match_parent"android:layout_height="match_parent"android:textAllCaps="false"/></LinearLayout>
  1. 数据模型 FruitBean.java,和刚刚的一致
public class FruitBean implements Serializable {private String name;private int imageId;public FruitBean() {}public FruitBean(String name, int imageId) {this.name = name;this.imageId = imageId;}public void setName(String name) {this.name = name;}public void setImageId(int imageId) {this.imageId = imageId;}public String getName() {return name;}public int getImageId() {return imageId;}
}
  1. 自定义适配器MyAdapter
// RecyclerView的适配器类,用于将数据集中的数据绑定到RecyclerView的各个Item上。
//继承RecyclerView.Adapter,这里的泛型是下面的内部类
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {// 数据源列表private List<FruitBean> mList;// 布局填充器,用于将XML布局文件转换为View对象private LayoutInflater mLayoutInflater;// 上下文对象private Context mContext;//构造函数,初始化适配器。public MyAdapter(List<FruitBean> mList, Context mContext) {this.mList = mList;this.mContext = mContext;this.mLayoutInflater = LayoutInflater.from(mContext);}@NonNull@Overridepublic MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {// 将布局文件转换为View对象View view = mLayoutInflater.inflate(R.layout.list_item_layout, parent, false);// 创建并返回ViewHolder实例MyViewHolder myViewHolder = new MyViewHolder(view);return myViewHolder;}// 将数据绑定到ViewHolder,在RecyclerView滚动时会不断调用@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {// 获取当前项的数据FruitBean fruitBean = mList.get(position);// 将数据绑定到ViewHolder中的TextView和ImageViewholder.tv.setText(fruitBean.getName());holder.img.setImageResource(fruitBean.getImageId());}@Overridepublic int getItemCount() {return mList.size();}// ViewHolder类,用于持有每个列表项的视图class MyViewHolder extends RecyclerView.ViewHolder {// 列表项中的ImageView和TextViewprivate ImageView img;private TextView tv;public MyViewHolder(View view) {super(view);// 通过itemView获取布局中的控件,并设置参数this.img = view.findViewById(R.id.image);this.tv = view.findViewById(R.id.tv);}}
}
  1. MainActivity
public class MainActivity extends AppCompatActivity {// 声明RecyclerView、数据列表和适配器private RecyclerView mrecyclerView;private List<FruitBean> mlist;private MyAdapter myAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 初始化数据initData();// 绑定RecyclerView,设置适配器mrecyclerView = findViewById(R.id.rlv);myAdapter = new MyAdapter(mlist, this);mrecyclerView.setAdapter(myAdapter);// 设置布局管理器,这里是竖向滚动RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);mrecyclerView.setLayoutManager(layoutManager);}// 初始化数据列表,添加多个水果项private void initData() {mlist = new ArrayList<>();for (int i = 0; i < 20; i++) {FruitBean itemBean1 = new FruitBean("apple", R.drawable.apple);FruitBean itemBean2 = new FruitBean("banana", R.drawable.banana);FruitBean itemBean3 = new FruitBean("litchi", R.drawable.litchi);mlist.add(itemBean1);mlist.add(itemBean2);mlist.add(itemBean3);}}
}

不同布局形式的设置

横向滚动

只用改一行代码

 RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);mrecyclerView.setLayoutManager(layoutManager);

new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)

  • 这里创建了一个LinearLayoutManager实例
  • this:传入的是当前Activity的上下文(Context)。
  • LinearLayoutManager.HORIZONTAL:表示RecyclerView的布局方向为水平(HORIZONTAL),即列表项将水平排列。
  • false:表示不反转布局。false意味着列表项从起点到终点正常排列,如果设置为true,列表项将从终点到起点反向排列。

瀑布流

RecyclerView.LayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);

new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.HORIZONTAL)

  • 2:表示布局中有2个跨度(列或行,取决于方向)。在水平布局中,这表示有2行。
  • StaggeredGridLayoutManager.HORIZONTAL:表示RecyclerView的布局方向为垂直,即列表项将垂直排列,并且每一列包含多个项目。

网格

RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this,2);

new GridLayoutManager(this, 2)

  • this:传入的是当前Activity的上下文(Context)。
  • 2:表示布局中有2个跨度(列)。这意味着RecyclerView的每一行将包含2个项目。

点击事件

ViewHolder中添加rlContainer变量来保存最外层布局的实例,再在onBindViewHolder方法中为他注册点击事件

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
//省略@Overridepublic void onBindViewHolder(@NonNull MyViewHolder holder, int position) {FruitBean fruitBean = mList.get(position);holder.tv.setText(fruitBean.getName());holder.img.setImageResource(fruitBean.getImageId());// 设置容器布局的点击事件holder.rlContainer.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(mContext, "你点击了" + position, Toast.LENGTH_SHORT).show();}});}class MyViewHolder extends RecyclerView.ViewHolder {private ImageView img;private TextView tv;//添加最外层的布局属性RelativeLayout rlContainer;public MyViewHolder(View view) {super(view);this.img = view.findViewById(R.id.image);this.tv = view.findViewById(R.id.tv);this.rlContainer = view.findViewById(R.id.rl_item_container);}}
}


感谢您的阅读
如有错误烦请指正


参考:

  1. 26-Android中的列表-RecyclerView_哔哩哔哩_bilibili
  2. 【Android】Fragment的静态动态创建以及两种创建方式的生命周期-CSDN博客
  3. 《第一行代码》

相关文章:

  • 初识c++:string类(2)
  • JavaScript(17)——事件监听
  • google 浏览器插件开发简单学习案例:TodoList;打包成crx离线包
  • 2024年钉钉杯大数据竞赛A题超详细解题思路+python代码手把手保姆级运行讲解视频+问题一代码分享
  • Vue3+Element Plus 实现table表格中input的验证
  • 堆的相关知识点
  • 【数据结构】二叉树链式结构——感受递归的暴力美学
  • 十大排序的稳定性和时间复杂度
  • 【proteus经典项目实战】51单片机用计数器中断实现100以内的按键计数并播放音乐
  • SAP MM学习笔记46 - 购买中的出力管理(消息管理)
  • IntelliJ IDEA 直接在软件中更新为最新版
  • 论文快过(图像配准|Coarse_LoFTR_TRT)|适用于移动端的LoFTR算法的改进分析 1060显卡上45fps
  • mysql1055报错解决方法
  • git报错403,git项目拉取不下来
  • VSCode | 修改编辑器注释的颜色
  • AngularJS指令开发(1)——参数详解
  • Asm.js的简单介绍
  • django开发-定时任务的使用
  • docker-consul
  • export和import的用法总结
  • HTTP请求重发
  • Java教程_软件开发基础
  • MYSQL 的 IF 函数
  • Python利用正则抓取网页内容保存到本地
  • QQ浏览器x5内核的兼容性问题
  • redis学习笔记(三):列表、集合、有序集合
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • Vue2.0 实现互斥
  • 基于Mobx的多页面小程序的全局共享状态管理实践
  • 来,膜拜下android roadmap,强大的执行力
  • 腾讯优测优分享 | 你是否体验过Android手机插入耳机后仍外放的尴尬?
  • 突破自己的技术思维
  • 一个JAVA程序员成长之路分享
  • Mac 上flink的安装与启动
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​14:00面试,14:06就出来了,问的问题有点变态。。。
  • (1)(1.11) SiK Radio v2(一)
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (备忘)Java Map 遍历
  • (附源码)php新闻发布平台 毕业设计 141646
  • (转)程序员疫苗:代码注入
  • (状压dp)uva 10817 Headmaster's Headache
  • .net 程序发生了一个不可捕获的异常
  • .Net程序帮助文档制作
  • .NET序列化 serializable,反序列化
  • @JSONField或@JsonProperty注解使用
  • @SuppressLint(NewApi)和@TargetApi()的区别
  • @Transactional 参数详解
  • [ 物联网 ]拟合模型解决传感器数据获取中数据与实际值的误差的补偿方法
  • [2019.2.28]BZOJ4033 [HAOI2015]树上染色
  • [ABC275A] Find Takahashi 题解
  • [Apio2012]dispatching 左偏树
  • [APIO2015]巴厘岛的雕塑
  • [C#]使用深度学习算法opencvsharp部署RecRecNet广角图像畸变矫正校正摄像广角镜头畸变图像
  • [c#基础]DataTable的Select方法