【Android】RecyclerView一个更加强大好用的控件,实现横向滚动和瀑布流布局,网格布局滚动

一、RecyclerView

1、LayoutInflater,View,Context的关系

image-20230720193736115

解释这段代码:

View view = LayoutInfalter.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false)

这段代码的作用是将名为 R.layout.fruit_item 的布局文件转换为一个视图对象,并将其添加到 parent 视图组中。

  • parent: 这是一个 ViewGroup 对象,它表示父级视图,即将要将转换后的视图添加到其中的容器。在 RecyclerView 中,每个列表项都有一个父级视图,即 RecyclerView 本身。
  • parent.getContext(): 通过调用 getContext() 方法,我们可以获取 RecyclerView 所在的上下文(Context)对象,这个上下文用于在应用程序中访问资源和进行其他操作。
  • LayoutInflater.from(parent.getContext()): 我们使用 LayoutInflater.from() 方法来获取一个用于在指定上下文中创建视图的 LayoutInflater 实例。
  • inflate(R.layout.fruit_item, parent, false) : inflate方法是 LayoutInflater类的一个重要方法,它接受三个参数:要转换的布局文件的资源 ID,父级视图(容器),以及是否将转换后的视图添加到父级视图中。
    • R.layout.fruit_item: 这是要转换的布局文件的资源 ID,它指定了要转换的布局文件的名称和位置。
    • parent: 这是要添加转换后的视图的父级视图(容器),即 RecyclerView。
    • false: 这个参数表示在将转换后的视图添加到父级视图时是否立即执行这个操作。false 表示在后续手动添加视图,因为在 RecyclerView 中,适配器负责创建和绑定视图。

2、RecyclerView的基本用法

2.1 为RecyclerView准备一个适配器

新建FruitAdapter类,让这个适配器继承自RecyclerView,Adapter,并将泛型指定为FruitAdapter,ViewHolder。其中,ViewHolder是我们在FruitAdapter中定义的一个内部类,代码如下所示:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>  {

    private List<Fruit> mFruitList;


    static class ViewHolder extends RecyclerView.ViewHolder{
        FruitItemBinding binding;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            binding = FruitItemBinding.bind(itemView);
            //binding = FruitItemBinding.inflate(LayoutInflater.from(itemView.getContext()), (ViewGroup) itemView,true);

        }
    }

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

    @NonNull
    @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.binding.fruitImg.setImageResource(fruit.getImageId());
        holder.binding.fruitName.setText(fruit.getName());
    }

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

虽然这段代码看上去好像有点长,但其实它比ListView的适配器要更容易理解。这里我们==1、==首先定义了一个内部类ViewHolderViewHolder要继承自RecyclerView.ViewHolder。然后ViewHolder的构造函数中要传人一个View参数,这个参数通常就是RecyclerView子项的最外层布局。然后我们通过bing获取到这个View的绑定类。

2.1.1 bind与inflate(LayoutInflater.from(itemView.getContext()))有什么区别

这里有个知识点下面两段代码有什么区别?

binding = FruitItemBinding.inflate(LayoutInflater.from(itemView.getContext()));
binding = FruitItemBinding.bind(itemView);

inflate(LayoutInflater.from(itemView.getContext())) :

它会自动处理布局文件中定义的绑定信息,将布局文件中的视图与绑定类中的对应视图关联起来,**注意他是没有绑定父布局。**就相当于创建了一个新的布局绑定,和之前的没有关系。

image-20230720205226190

通过源码我们发现如果使用inflate(LayoutInflater.from(itemView.getContext())) 实际上是1调用2再调用3,所以这两段代码产生的数据类型相同,但是可以看见1中调用inflate但是传入的parent是null就相当于创建了一个新的布局绑定,和之前的没有关系。

改为这样就可以了,和bind是等价的

binding = FruitItemBinding.inflate(LayoutInflater.from(itemView.getContext()), (ViewGroup) itemView,true);

binding = FruitItemBinding.bind(itemView) :

这行代码使用 FruitItemBinding 类的 bind() 方法,将传入的 itemViewRecyclerView 的单个项布局)与 FruitItemBinding 类所对应的布局文件关联起来。bind() 方法的作用是将布局文件 fruit_item.xml 中的视图控件与 FruitItemBinding 绑定类中的对应视图控件进行关联。

总结:inflate(LayoutInflater.from(itemView.getContext()))绑定的是整个布局,bind绑定的是单独控件。

==2、==往下看,FruitAdapter中也有一个构造函数,这个方法用于把要展示的数据源传进来,并赋值给一个全局变量mFru1 tList,我们后续的操作都将在这个数据源的基础上进行。

==3、==由于FruitAdapter是继承自RecyclerView.Adapter的,那么就必须重写onCreateviewHolder()、onBindViewHolder()和getItemCount()这3个方法。

**onCreateViewHolder()**方法是用于创建ViewHolder实例的,我们在这个方法中将fruititem布局加载进来,然后创建一个ViewHolder实例,并把加载出来的布局传人到构造函数当中,最后将ViewHolder的实例返回。**onBindViewHolder()**方法是用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行,这里我们通过position参数得到当前项的Fruit实例,然后再将数据设置到ViewHolder的ImageView和TextView当中即可。**getItemCount()**方法就非常简单了,它用于告诉RecyclerView一共有多少子项,直接返回数据源的长度就可以了。

2.2 使用准备好的适配器

public class MainActivity extends AppCompatActivity {
    private List<Fruit> fruitList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initFruits(); //初始化水果数据

        ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        //制作布局方式
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);

       //设置布局方式
        binding.recycler1.setLayoutManager(linearLayoutManager);

        //传递数据给适配器
        FruitAdapter adapter = new FruitAdapter(fruitList);

        binding.recycler1.setAdapter(adapter);

    }

    //初始化水果数据
    private void initFruits() {
        for (int i = 0; i < 2; i++) {
            Fruit ariplane = new Fruit("0", R.drawable.img10);
            fruitList.add(ariplane);
            Fruit Banana = new Fruit("1", R.drawable.img1);
            fruitList.add(Banana);
            Fruit TheEarth = new Fruit("2", R.drawable.img2);
            fruitList.add(TheEarth);
            Fruit bourn = new Fruit("3", R.drawable.img3);
            fruitList.add(bourn);
            Fruit emitter = new Fruit("4", R.drawable.img4);
            fruitList.add(emitter);
            Fruit cati = new Fruit("5", R.drawable.img5);
            fruitList.add(cati);
            Fruit SatelliteVehicle = new Fruit("6", R.drawable.img6);
            fruitList.add(SatelliteVehicle);
            Fruit warplane = new Fruit("7", R.drawable.img7);
            fruitList.add(warplane);
            Fruit Saturn = new Fruit("8", R.drawable.img8);
            fruitList.add(SatelliteVehicle);
            Fruit Jupiter = new Fruit("9", R.drawable.img9);
            fruitList.add(Jupiter);
            Fruit Apple = new Fruit("10", R.drawable.img10);
            fruitList.add(Apple);
            Fruit Tomato = new Fruit("11", R.drawable.img11);
            fruitList.add(Tomato);
            Fruit Onion = new Fruit("12", R.drawable.img12);
            fruitList.add(Onion);
            Fruit watermelon = new Fruit("13", R.drawable.img13);
            fruitList.add(watermelon);
            Fruit sun = new Fruit("14", R.drawable.img14);
            fruitList.add(sun);
        }
    }
}

实现效果如下:

image-20230720203945193

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

3.1 横向滚动

在MainActivity中加上这样一句话:linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initFruits(); //初始化水果数据

    ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());

    //制作布局方式
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

    //设置布局方式
    binding.recycler1.setLayoutManager(linearLayoutManager);

    //传递数据给适配器
    FruitAdapter adapter = new FruitAdapter(fruitList);

    binding.recycler1.setAdapter(adapter);

}

意思是设置为水平方向,因为默认的是竖值方向所有不用设置。

MainActivity中只加入了一行代码,调用LinearLayoutManager的setOrientation()方法来设置布局的排列方向,默认是纵向排列的,我们传人LinearLayoutManager.HORIZONTAL表示让布局横行排列,这样Recycler View就可以横向滚动了。

3.2 瀑布流布局(StaggerredGridLayoutManager)

修改MainActivity中的onCreate代码如下:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initFruits(); //初始化水果数据

    ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());

    //制作布局方式
    StaggeredGridLayoutManager staggeredGridLayoutManager = new
            StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);

   //设置布局方式
    binding.recycler1.setLayoutManager(staggeredGridLayoutManager);

    //传递数据给适配器
    FruitAdapter adapter = new FruitAdapter(fruitList);

    binding.recycler1.setAdapter(adapter);

}

构造StaggeredGridLayoutManager对象,构造函数的两个参数,第一个参数表示指定布局的列数,第二个参数指定布局的排列方式。

注意fruit_item.xml中,LinearLayout的宽度应该是macth_parent,因为瀑布流的宽度应该是根据布局的列数自动适配的。

显示效果如下:

image-20230721095709284

3.3 网格布局

同理在MainActivity中的onCreate中的之中布局方式代码改为:

//网格布局
        GridLayoutManager gridLayoutManager = new GridLayoutManager(this,4);

第一个参数是Activitycontext。第二个参数是布局的列数。

显示效果如下:

image-20230721095348796

4、RecycleView点击事件

RecycleView需要我们为子项具体的View注册点击事件,修改FruitAdapter中代码如下:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>  {

    private List<Fruit> mFruitList;


    static class ViewHolder extends RecyclerView.ViewHolder{
        FruitItemBinding binding;

        //最外层的View
        View public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>  {

    private List<Fruit> mFruitList;


    static class ViewHolder extends RecyclerView.ViewHolder{
        FruitItemBinding binding;

        //最外层的View
        View fruitView;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            binding = FruitItemBinding.bind(itemView);
            fruitView = itemView;
        }


    }

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

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

        //设置为Final防止不小心被修改
        final ViewHolder holder = new ViewHolder(view);

        //注册外层监听事件
        holder.fruitView.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.binding.fruitImg.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;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.binding.fruitImg.setImageResource(fruit.getImageId());
        holder.binding.fruitName.setText(fruit.getName());
        Log.d("TAG",fruit.getName());
    }

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

我们先是修改了ViewHolder,在ViewHolder中添加了fruitView变量来保存子项最外层布局的实例,然后在onCreateviewHolder()方法中注册点击事件就可以了。这里分别为最外层布局和ImageView都注册了点击事件,RecyclerView的强大之处也在这里,它可以轻松实现子项中任意控件或布局的点击事件。我们在两个点击事件中先获取了用户点击的positio,然后通过position拿到相应的Fruit实例,再使用Toast分别弹出两种不同的内容以示区别。

由于TextView并没有注册点击事件,因此点击文字这个事件会被子项的最外层布局捕获到。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android中,RecyclerView一个强大控件,可以用于显示大量数据,并且支持各种布局和交互效果。要实现RecyclerView横向滚动定位,可以按照以下步骤进行操作: 1. 创建RecyclerView对象:在XML布局文件中定义RecyclerView,并在代码中使用findViewById方法找到它。 2. 设置布局管理器:使用LinearLayoutManager或GridLayoutManager等布局管理器,将RecyclerView设置为水平滚动模式。例如,对于横向滚动,可以使用以下代码: LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); recyclerView.setLayoutManager(layoutManager); 3. 创建数据适配器:根据需求创建RecyclerView.Adapter的子类,并实现其抽象方法。适配器负责将数据绑定到RecyclerView的视图项上,并管理视图项的点击事件等操作。 4. 绑定适配器:将适配器与RecyclerView进行关联,通过调用RecyclerView的setAdapter方法来实现。例如: recyclerView.setAdapter(adapter); 5. 实现横向定位功能:可以利用RecyclerView的smoothScrollToPosition方法实现横向滚动定位。根据需要,可以在适配器中设置点击事件,当点击RecyclerView的某个视图项时,获取其位置position,并通过以下代码实现横向定位: recyclerView.smoothScrollToPosition(position); 通过上述步骤,我们可以实现RecyclerView横向滚动定位功能。在实际开发中,还可以根据需求进行样式、数据加载等其他操作来定制RecyclerView的外观和功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摸鱼小小虫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值