【Android】ListView与RecyclerView基础运用

avatar
作者
猴君
阅读量:0

【Android】ListView与RecyclerView基础运用

ListView

简单用法

首先在activity_main中加入ListView控件。

<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> 

image-20240717110820048

而后修改MainActivity中的代码

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的一个方法,用于将适配器绑定到列表视图上,这样列表视图就可以显示适配器中的数据了。4902796901f83eb61bce378eb85c31df

ListView界面定制

  1. 新建类Fruit,并建立布局

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 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> 
  1. 创建自定义的适配器

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

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方法,为每个列表项提供了自定义的视图布局,包括水果的图片和名称。这使得列表项不仅显示文本,还可以显示图像,从而提供更丰富的用户界面。

  1. 在MainActivity中调用

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

ListView性能优化

将FruitAdapter中的代码进行优化。

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,减少了布局的创建和视图的初始化次数。这对于长列表来说尤其重要,因为它可以显著减少内存使用和提高滚动性能。
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年就被淘汰。

img

RecyclerView

加入RecyclerView组件

<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准备一个适配器

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中的代码

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就有了数据源,并且知道如何显示这些数据。

03a72ce93d1fefabc43061ffda69d190

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

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

横向滚动布局

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

LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); recyclerView.setLayoutManager(layoutManager); 

瀑布流布局(StaggeredGridLayoutManager)

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

  1. 创建 StaggeredGridLayoutManager 实例:
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(spanCount, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); 

网格布局

网格布局使用 GridLayoutManager 来实现。

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

注册点击事件

@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; } 

866b450ecab378fa55c08b1b70c7f9e1

1c163ee961dc9e8964a2edff6de1d1a4

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!