RecyclerView笔记

本文详细介绍了如何使用RecyclerView实现不同类型的布局,包括线性布局、网格布局及瀑布流布局,并演示了如何处理RecyclerView的点击事件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本文是 郭霖. 第一行代码[M]. 人民邮电出版社, 2014. 第三章第六节的笔记

A flexible view for providing a limited window into a large data set.
为大型数据集提供有限窗口的灵活视图。

首先,在 app/build.gradle 里面的 dependence 里面添加:

compile 'com.android.support:recyclerview-v7:27.0.0'

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycle_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

适配器:

Adapter: A subclass of RecyclerView.Adapter responsible for providing views that represent items in a data set.
适配器: RecyclerView.Adapter 的一个子类,负责提供表示数据集中项目的视图。

RecyclerView.Adapter 的 api:

Base class for an Adapter
Adapters provide a binding from an app-specific data set to views that are displayed within a RecyclerView.
适配器的基类
适配器提供从特定于应用程序的数据集到在 RecyclerView 中显示的视图的绑定

接下来需要为 RecylerView 准备一个适配器 FruitRecyclerAdapter 类,让这个适配器继承自 RecyclerView.Adapter ,并将泛型指定为 ViewHolder ,其中 ViewHolder 代码如下:

public class ViewHolder extends RecyclerView.ViewHolder{

    ImageView fruitImage;
    TextView fruitText;

    public ViewHolder(View view) {
        super(view);
        fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
        fruitText = (TextView)view.findViewById(R.id.fruit_text);
    }
}

RecyclerView.ViewHolder 的 API :

A ViewHolder describes an item view and metadata about its place within the RecyclerView.
ViewHolder 描述了一个项目视图和关于它在 RecyclerView 中的位置的元数据。
RecyclerView.Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive findViewById(int) results.
While RecyclerView.LayoutParams belong to the RecyclerView.LayoutManager, ViewHolders belong to the adapter. Adapters should feel free to use their own custom ViewHolder implementations to store data that makes binding view contents easier. Implementations should assume that individual item views will hold strong references to ViewHolder objects and that RecyclerView instances may hold strong references to extra off-screen item views for caching purposes

RecyclerView.ViewHolder 的构造函数需要传入 view 参数.这个 view 参数通常是 RecyclerView 子项的最外层布局。那么我们就可以通过 view 的 findViewById 找到控件的实例。

public class FruitRecycleAdapter extends RecyclerView.Adapter<ViewHolder> {
    private List<Fruit> mFruitList;

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

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

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImgId());
        holder.fruitText.setText(fruit.getName());
        Log.i("FruitRecycleAdapter", "onBindViewHolder" + "加载数据" + position);
    }

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

利用构造函数将要展示的数据源传入,并赋值给全局变量。因为继承自 RecyclerView.Adapter ,所以必须重写 onCreateViewHolder(),onBindViewHolder(),getItemCount()方法。
onCreateViewHolder():

This method calls onCreateViewHolder(ViewGroup, int) to create a new RecyclerView.ViewHolder and initializes some private fields to be used by RecyclerView.
此方法调用 onCreateViewHolder ( ViewGroup , int )来创建一个新的 RecyclerView.ViewHolder ,并初始化 RecyclerView 要使用的一些专用字段。

我们将 fruit_item 布局文件加载进 view 中,用 view 创建一个 ViewHolder 实例。然后将这个实例返回。这个实例将在 onBindViewHolder()方法中被调用。
onBindViewHolder():

Called by RecyclerView to display the data at the specified position.
由 RecyclerView 调用以在指定位置显示数据。(即对子项的数据进行赋值)

onBindViewHolder()方法会在每个子项被滚动到屏幕内的时候被执行,这里我们通过 position 参数得到当前的 fruit 实例。然后再将数据设置到 ViewHolder 的控件实例中去即可。
getItemCount()

Returns the total number of items in the data set held by the adapter.
返回适配器持有的数据集中的项目总数。
Activity :

public class RecyclerActivity extends Activity {
    List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview);
        initFruits();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
        recyclerView.setAdapter(fruitRecycleAdapter);
    }
    private void initFruits() {
        for (int i = 0; i < 2; i++) {
            Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
            fruitList.add(apple);
            ...
            Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
            fruitList.add(mango);
        }
    }
}

这里初始化数据之后,获取 RecyclerView 实例,然后创建一个 LinearLayoutManager 对象,并将它设置到 RecyclerView 当中.
RecyclerView.LayoutManager 的 API :

A LayoutManager is responsible for measuring and positioning item views within a RecyclerView as well as determining the policy for when to recycle item views that are no longer visible to the user. By changing the LayoutManager a RecyclerView can be used to implement a standard vertically scrolling list, a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock layout managers are provided for general use.
If the LayoutManager specifies a default constructor or one with the signature (Context, AttributeSet, int, int), RecyclerView will instantiate and set the LayoutManager when being inflated. Most used properties can be then obtained from getProperties(Context, AttributeSet, int, int). In case a LayoutManager specifies both constructors, the non-default constructor will take precedence.
LayoutManager 负责测量和定位 RecyclerView 中的项目视图,并确定何时回收不再可见的项目视图的策略。通过更改 LayoutManager , RecyclerView 可用于实现标准垂直滚动列表,统一网格,交错网格,水平滚动集合等等。提供一些股票布局经理供一般使用。 如果 LayoutManager 指定默认构造函数或带有签名( Context , AttributeSet , int , int )的构造函数,则 RecyclerView 将在充气时实例化并设置 LayoutManager 。大多数使用的属性可以从 getProperties ( Context , AttributeSet , int , int )中获取。如果一个 LayoutManager 指定了两个构造函数,那么非默认构造函数将优先。

LayoutManager 用于指定 RecyclerView 的布局方式,这里使用线性布局。最后调用 RecyclerView 的 setAdapter 方法来完成适配器设置,这样 RecyclerView 和数据之间的关联就建立完成了。
运行结果:

控制台输出:
04-10 17:10:06.989 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:06.993 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:06.995 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:06.998 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 1
04-10 17:10:06.999 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.001 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 2
04-10 17:10:07.003 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.005 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 3
04-10 17:10:07.006 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.008 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 4
04-10 17:10:07.009 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.012 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 5
04-10 17:10:07.013 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.015 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 6
04-10 17:10:07.017 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.019 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 7
04-10 17:10:18.510 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:18.516 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 8
04-10 17:10:18.580 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:18.585 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 9
04-10 17:10:19.021 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:19.022 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 10
04-10 17:10:19.514 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:19.514 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:26.093 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:36.262 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:36.365 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 12
04-10 17:10:41.062 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 13
04-10 17:10:42.420 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 2
04-10 17:10:42.489 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 1
04-10 17:10:42.543 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:44.798 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:45.902 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 12
04-10 17:10:46.261 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 13
04-10 17:10:47.125 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 2
04-10 17:10:47.600 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 1
04-10 17:10:47.687 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:48.552 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:48.620 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 12

可见在加载数据的时候 onCreateViewHolder 不需要每次都调用。

横屏滚动

  1. 修改布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="wrap_content"
    android:layout_height="100dp">

    <ImageView
        android:id="@+id/fruit_image"
        android:layout_gravity="center_horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/fruit_text"
        android:layout_gravity="center_horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
在 Activity 中加入
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

使得布局横向排列。

public class RecyclerActivity extends Activity {
    List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview);
        initFruits();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        recyclerView.setLayoutManager(layoutManager);
        FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
        recyclerView.setAdapter(fruitRecycleAdapter);
    }
    ...
}

网格布局

public class RecyclerActivity extends Activity {
    List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview);
        initFruits();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
        GridLayoutManager layoutManager = new GridLayoutManager(this,4);
//        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        recyclerView.setLayoutManager(layoutManager);
        FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
        recyclerView.setAdapter(fruitRecycleAdapter);
    }
    ...
}

将 LinearLayoutManager 改为,设置每行 4 格

将列数改为 7 之后:
可见, ``android:layout_width="wrap_content"`` 的时候这个宽度是可以自动适配的。 # 瀑布流布局 布局文件: ```xml <?xml version="1.0" encoding="utf-8"?> ``` 瀑布流布局为了使宽度可以自动适配,所以设置
android:layout_width="match_parent"

使用 layout_margin 是的子项之间互留一点间距( layout_margin 设置 view 的上下左右边框的额外空间)。设置 TextView 居左对齐。

public class RecyclerActivity extends Activity {
    List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recyclerview);
        initFruits();
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,
                StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
        recyclerView.setAdapter(fruitRecycleAdapter);
    }
    private void initFruits() {
        for (int i = 0; i < 2; i++) {
            Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
            fruitList.add(watermelon);
            Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
            fruitList.add(pear);
            Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
            fruitList.add(grape);
            Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
            fruitList.add(strawberry);
            Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
            fruitList.add(cherry);
            Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
            fruitList.add(mango);
        }
    }

    private String getRandomLengthName(String name) {
        Random random = new Random();
        int length = random.nextInt(20) + 1;
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            builder.append(name);
        }
        return builder.toString();
    }
}

设置 StaggeredGridLayoutManager 排列方向为垂直方向排列,即为每行 3 个,一行排列完成再排列另外一行。
效果:

如果设置为水平方向排列,效果:

即每列 3 个,排完一列再排下一列,而且由于布局文件设置
android:layout_width="match_parent" android:layout_height="wrap_content"

所以每列 3 个没有平均分配,每行一个也布满整个行。

RecyclerView 的点击事件

RecyclerView 所有的点击事件都是由具体的 view 注册.
首先修改 ViewHolder :

public class ViewHolder extends RecyclerView.ViewHolder{
    View fruitView;
    ImageView fruitImage;
    TextView fruitText;

    public ViewHolder(View view) {
        super(view);
        fruitView = view;
        fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
        fruitText = (TextView)view.findViewById(R.id.fruit_text);
    }
}

在 ViewHolder 中添加 fruitView 变量,保存子项最外层布局的实例.然后在 onCreateViewHolder 中注册点击事件,这里为最外层布局和 ImageView 都注册了点击事件。

public class FruitRecycleAdapter extends RecyclerView.Adapter<ViewHolder> {

    private List<Fruit> mFruitList;

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

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.fruit_item, parent, false);
        final ViewHolder holder = new ViewHolder(view);
        holder.fruitView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(), "you click view " + fruit.getName(),
                        Toast.LENGTH_SHORT).show();

            }
        });
        holder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(v.getContext(), "you click image " + fruit.getName(),
                        Toast.LENGTH_SHORT).show();
            }
        });
        Log.i("FruitRecycleAdapter", "onCreateViewHolder");
        return holder;
    }
    ...
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值