【Android】ListView与RecyclerView基础运用

【Android】ListView与RecyclerView基础运用

ListView

简单用法

首先在activity_main中加入ListView控件。

xml 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical">
    
    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
</LinearLayout>

而后修改MainActivity中的代码

java 复制代码
public class MainActivity extends AppCompatActivity {

    String[] data = {"apple","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon","banana","orange","watermelon"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1,data);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String fruit = data[position];
                Toast.makeText(MainActivity.this,fruit,Toast.LENGTH_SHORT).show();
            }
        });
    }
}
  1. ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, data);

    1. 这行代码创建了一个ArrayAdapter对象,它是Android中用于将数据绑定到ListViewGridView等组件的适配器之一。
    2. <String>表示这个适配器是用来处理字符串(String)类型的数据。
    3. new ArrayAdapter<>(... 表示实例化一个ArrayAdapter对象。
    4. MainActivity.this是当前MainActivity的上下文(Context),这通常用于获取系统服务或启动其他组件。
    5. android.R.layout.simple_list_item_1是一个系统提供的布局资源,用于定义列表项的样式。这里指定了列表项的布局,该布局显示单个文本项。
    6. data是一个字符串数组,它包含了要显示在列表中的所有数据。
  2. ListView listView = (ListView) findViewById(R.id.list_view);

    1. 这行代码通过调用findViewById方法来获取布局文件中定义的ListView组件的实例。
    2. R.id.list_viewListView组件在布局文件中的ID。
    3. ListView是Android中的一个组件,用于显示垂直滚动的列表项。
  3. listView.setAdapter(adapter);

    1. 这行代码将之前创建的adapter设置为listView的适配器。
    2. setAdapterListView的一个方法,用于将适配器绑定到列表视图上,这样列表视图就可以显示适配器中的数据了。

ListView界面定制

新建类Fruit,并建立布局

java 复制代码
public class Fruit {
    private String name;
    private int imageId;

    public Fruit(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }

    public String getName() {
        return name;
    }

    public int getImageId() {
        return imageId;
    }
}

而后为ListView的子项指定一个自定义的布局,在layout目录下新建一个fruit_item.xml

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">
    
    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="10dp"/>

</LinearLayout>

创建自定义的适配器

接着我们需要创建一个自定义的适配器,这个适配器继承自ArrayAdapter,并将泛型是定位Fruit类

java 复制代码
public class FruitAdapter extends ArrayAdapter<Fruit> {

    private int resourceId;
    public FruitAdapter(@NonNull Context context, int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId,objects);
        resourceId = textViewResourceId;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Fruit fruit = getItem(position);
        View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
        TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
        fruitImage.setImageResource(fruit.getImageId());
        fruitName.setText(fruit.getName());
        return view;
    }
}
  1. public class FruitAdapter extends ArrayAdapter<Fruit> {
    1. 定义了一个名为FruitAdapter的公共类,它继承自ArrayAdapter类,并且指定了泛型参数Fruit,这意味着这个适配器是专门为处理Fruit类型对象的列表而设计的。
  2. private int resourceId;
    1. 声明了一个私有的整型变量resourceId,用于存储列表项的布局资源ID。
  3. public FruitAdapter(@NonNull Context context, int textViewResourceId, List<Fruit> objects) {
    1. 定义了FruitAdapter的构造函数,它接受三个参数:
      • @NonNull Context context:上下文对象,用于访问系统服务和资源。
      • int textViewResourceId:列表项的布局资源ID。
      • List<Fruit> objects:一个包含Fruit对象的列表,这些对象将被显示在列表中。
  4. super(context, textViewResourceId, objects);
    1. 调用父类ArrayAdapter的构造函数,传递上下文、资源ID和对象列表。
  5. public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    1. 重写了ArrayAdapter中的getView方法,它是适配器的核心,用于为列表中的每个项提供视图。
    2. int position:当前项在列表中的位置。
    3. @Nullable View convertView:可重用的视图,用于提高列表滚动的性能。
    4. @NonNull ViewGroup parent:列表视图的父容器。
  6. Fruit fruit = getItem(position);
    1. 调用ArrayAdaptergetItem方法,获取列表中当前位置的Fruit对象。
  7. View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
    1. 使用LayoutInflatergetContext()获取的上下文中实例化一个视图,使用resourceId指定的布局资源,并且parent是列表视图的父容器,false表示不要为parent添加这个视图。
  1. LayoutInflater.from(getContext())
    1. LayoutInflater是一个用于将XML布局文件转换成实际视图对象的系统服务。
    2. from(context)是一个静态方法,它返回一个LayoutInflater的实例,并且已经配置了给定的上下文(context)。
  2. getContext()
    1. 这是ArrayAdapter类的一个方法,返回当前适配器的上下文(Context)。上下文是Android中非常重要的概念,它提供了对系统服务的访问以及对资源的引用。
  3. inflate(resourceId, parent, attachToRoot)
    1. inflateLayoutInflater类的一个方法,用于将指定资源ID的XML布局文件实例化为视图对象。
    2. resourceId:是一个整型值,指定了要实例化的布局资源的ID。
    3. parent:是一个ViewGroup对象,表示新创建的视图将要被添加到的父容器。在这个上下文中,parent参数通常是用来确保新视图的布局参数正确设置的。
    4. attachToRoot:是一个布尔值,指定是否将新创建的视图附加到parent。在这个例子中,false表示不附加到parent,而是返回一个独立的视图对象,稍后可以手动添加到parent

综合来看,LayoutInflater.from(getContext()).inflate(resourceId, parent, false);这行代码的作用是:

  • 从当前适配器的上下文中获取LayoutInflater实例。
  • 使用inflate方法将指定的布局资源ID(resourceId)转换成实际的视图对象。
  • 将这个新创建的视图作为子视图,但不立即附加到提供的父容器(parent)上。
  • 返回这个新创建的视图对象,以便可以在适配器的getView方法中进一步设置和返回。
  1. ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
    1. 通过findViewById获取布局中的ImageView组件,用于显示水果的图片。
  2. TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
    1. 通过findViewById获取布局中的TextView组件,用于显示水果的名称。
  3. fruitImage.setImageResource(fruit.getImageId());
    1. 设置ImageView的图片资源,getImageId()Fruit类的一个方法,返回图片资源的ID。
  4. fruitName.setText(fruit.getName());
    1. 设置TextView的文本,getName()Fruit类的一个方法,返回水果的名称。
  5. return view;
    1. 返回为当前列表项创建的视图。

FruitAdapter类通过重写getView方法,为每个列表项提供了自定义的视图布局,包括水果的图片和名称。这使得列表项不仅显示文本,还可以显示图像,从而提供更丰富的用户界面。

在MainActivity中调用

这一步骤只需要替换上一阶段中的适配器对象即可,此处略过。

ListView性能优化

将FruitAdapter中的代码进行优化。

java 复制代码
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
    Fruit fruit = getItem(position);
    
    View view;
    if(convertView == null){
        view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
    }else{
        view = convertView;
    }

    ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
    TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);

    fruitImage.setImageResource(fruit.getImageId());
    fruitName.setText(fruit.getName());
    return view;
}
  • 通过重用convertView,减少了布局的创建和视图的初始化次数。这对于长列表来说尤其重要,因为它可以显著减少内存使用和提高滚动性能。
java 复制代码
public class FruitAdapter extends ArrayAdapter<Fruit> {

    private int resourceId;
    public FruitAdapter(@NonNull Context context, int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId,objects);
        resourceId = textViewResourceId;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        Fruit fruit = getItem(position);

        View view;
        ViewHolder viewHolder;

        if(convertView == null){
            view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
            viewHolder = new ViewHolder();
            viewHolder.fruitImage = view.findViewById(R.id.fruit_image);
            viewHolder.fruitName = view.findViewById(R.id.fruit_name);
            view.setTag(viewHolder);
        }else{
            view = convertView;
            viewHolder = (ViewHolder) view.getTag();
        }

        viewHolder.fruitImage.setImageResource(fruit.getImageId());
        viewHolder.fruitName.setText(fruit.getName());
        return view;
    }

    class ViewHolder {
        ImageView fruitImage;
        TextView fruitName;
    }
}

白学警告

此控件早在远古时代的16年就被淘汰。

RecyclerView

加入RecyclerView组件

xml 复制代码
<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.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

由于该组件不是内置在系统SDK中,所以需要把完整的包路径写出来。

为RecyclerView准备一个适配器

java 复制代码
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>  {

    private List<Fruit> mFruitList;


    static class ViewHolder extends RecyclerView.ViewHolder{
        ImageView fruitImage;
        TextView fruitName;
        public ViewHolder(View itemView) {
            super(itemView);
            fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image);
            fruitName = (TextView) itemView.findViewById(R.id.fruit_name);
        }
    }

    public FruitAdapter(List<Fruit> mFruitList) {
        this.mFruitList = mFruitList;
    }

    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,
                parent,false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}

这段代码定义了一个名为FruitAdapter的类,它扩展了RecyclerView.Adapter,用于在RecyclerView中显示水果列表。

  1. 构造函数
    1. public FruitAdapter(List<Fruit> mFruitList)
    2. 初始化适配器,并接收一个包含Fruit对象的列表。
  2. ViewHolder****静态内部类
    1. static class ViewHolder extends RecyclerView.ViewHolder
    2. 定义了一个静态内部类ViewHolder,用于缓存视图组件的引用,减少重复调用findViewById
  3. onCreateViewHolder
    1. @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    2. 创建并返回一个新的ViewHolder实例。这个方法负责为每个列表项提供视图。
  4. onBindViewHolder
    1. @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position)
    2. 将数据绑定到ViewHolder上。这个方法负责将每个列表项的数据填充到视图中。
  5. getItemCount
    1. @Override public int getItemCount()
    2. 返回列表中项目的数量。这是RecyclerView用来确定显示多少项的方法。
  • 构造函数
    • 接收一个Fruit对象的列表,并将其存储在适配器中。
  • ViewHolder
    • ViewHolder的构造函数中,通过调用itemView.findViewById获取视图中的组件(如ImageViewTextView),并将它们存储在ViewHolder实例中。
  • onCreateViewHolder
    • 使用LayoutInflaterparent.getContext()获取的上下文中加载布局资源(R.layout.fruit_item),并将其实例化为视图。
    • 创建一个新的ViewHolder实例,并将新创建的视图传递给ViewHolder的构造函数。
    • 返回这个新的ViewHolder实例。
  • onBindViewHolder
    • 根据position参数获取列表中对应位置的Fruit对象。
    • 使用ViewHolder中的组件引用(fruitImagefruitName)来设置视图的图片资源和文本。
  • getItemCount
    • 返回适配器中Fruit对象列表的大小,即列表项的总数。

修改MainActivity中的代码

java 复制代码
private List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    EdgeToEdge.enable(this);
    setContentView(R.layout.activity_main);
    ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
        Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
        v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
        return insets;
    });

    initFruits();
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);

    recyclerView.setLayoutManager(layoutManager);
    FruitAdapter adapter = new FruitAdapter(fruitList);
    recyclerView.setAdapter(adapter);
}
initFruits();
  • 这行代码调用了一个名为initFruits的方法,这个方法可能是用来初始化一个包含Fruit对象的列表,这些对象将被用于填充RecyclerView

    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

  • 通过调用findViewById方法,获取布局文件中定义的RecyclerView组件的实例,并将其转换为RecyclerView类型。

    LinearLayoutManager layoutManager = new LinearLayoutManager(this);

  • 创建了一个LinearLayoutManager对象,用于管理RecyclerView中的项如何布局。this关键字指的是当前的上下文(可能是一个ActivityFragment)。

    recyclerView.setLayoutManager(layoutManager);

  • 将创建的LinearLayoutManager设置为RecyclerView的布局管理器,这样RecyclerView就知道如何对其子项进行布局。

    FruitAdapter adapter = new FruitAdapter(fruitList);

  • 创建了一个FruitAdapter对象,并将之前初始化的fruitList(包含Fruit对象的列表)传递给它。这个适配器将用于将数据绑定到RecyclerView

    recyclerView.setAdapter(adapter);

  • 将创建的FruitAdapter设置为RecyclerView的适配器。这样RecyclerView就有了数据源,并且知道如何显示这些数据。

实现横向滚动和瀑布流布局,网格布局

RecyclerView 是 Android 中一个灵活的组件,用于展示大量数据集,它可以配合不同的布局管理器(LayoutManager)来实现横向滚动、瀑布流布局和网格布局。以下是实现这些布局的基本步骤:

横向滚动布局

横向滚动通常使用 LinearLayoutManager 并设置其方向为横向。

java 复制代码
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);

瀑布流布局(StaggeredGridLayoutManager)

瀑布流布局是一种不规则的网格布局,可以实现不同高度和宽度的项。

  1. 创建 StaggeredGridLayoutManager 实例:
java 复制代码
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);

网格布局

网格布局使用 GridLayoutManager 来实现。

  1. 创建 GridLayoutManager 实例:
java 复制代码
GridLayoutManager layoutManager = new GridLayoutManager(this, spanCount);
recyclerView.setLayoutManager(layoutManager);
  1. 设置 RecyclerView 的布局管理器:
java 复制代码
recyclerView.setLayoutManager(layoutManager);

注册点击事件

java 复制代码
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,
            parent,false);
    ViewHolder holder = new ViewHolder(view);

    //注册文字监听事件
    holder.fruitName.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            int position = holder.getAdapterPosition();
            Fruit fruit = mFruitList.get(position);
            Toast.makeText(view.getContext(),"外层", Toast.LENGTH_SHORT).show();
        }
    });

    //注册图片按钮监听事件
    holder.fruitImage.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            int position = holder.getAdapterPosition();
            Fruit fruit = mFruitList.get(position);
            Toast.makeText(view.getContext(),"图片", Toast.LENGTH_SHORT).show();
        }
    });

    return holder;
}
相关推荐
众拾达人几秒前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
吃着火锅x唱着歌1 小时前
PHP7内核剖析 学习笔记 第四章 内存管理(1)
android·笔记·学习
_Shirley2 小时前
鸿蒙设置app更新跳转华为市场
android·华为·kotlin·harmonyos·鸿蒙
hedalei4 小时前
RK3576 Android14编译OTA包提示java.lang.UnsupportedClassVersionError问题
android·android14·rk3576
锋风Fengfeng4 小时前
安卓多渠道apk配置不同签名
android
枫_feng4 小时前
AOSP开发环境配置
android·安卓
叶羽西5 小时前
Android Studio打开一个外部的Android app程序
android·ide·android studio
qq_171538856 小时前
利用Spring Cloud Gateway Predicate优化微服务路由策略
android·javascript·微服务
Vincent(朱志强)8 小时前
设计模式详解(十二):单例模式——Singleton
android·单例模式·设计模式
mmsx8 小时前
android 登录界面编写
android·登录界面