Android RecyclerView列表流行控件的实现

RecyclerView

RecyclerView是Android中一个强大的控件,不仅可以实现和ListView同样的效果,还有各种特殊的样式,比如网格布局、瀑布流布局,同时在列表布局中也可以实现横向翻滚和数据反向显示

辅助类

RecyclerView的实现需要下面几个类进行辅助:

  • LayoutManager布局管理器,用于管理RecyclerView的展示样式(列表、网格、瀑布)
  • Adapter处理视图和数据之间的关系
  • ViewHolder用于容纳ItemView的实例,如列表或网格中的单独的View

实现效果

引用RecyclerView

RecyclerView并没有被默认引用到项目中,所以在使用之前先配置一下build.gradle(:app) 文件

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    // 引用RecyclerView
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

}

这里的版本号1.1.0要与上面的appcompat的版本号保持一致

线性布局

不管是什么布局都要进行以下三步:

  1. 创建一个类继承RecyclerView.Adapter
  2. 创建一个内部类绑定ViewHolder(继承RecyclerView.ViewHolder,在类内完成控件初始化)
  3. 实现Adapter的相关方法
// 泛型:自定义ViewHolder,需继承RecyclerView.ViewHolder
public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {

    private Context context;
    private List<String> data;
    private RecyclerView recyclerView;

    // 这里传入RecyclerView对象是用于瀑布布局,如果是网格布局和线性布局可以不传
    public MyRecyclerViewAdapter(Context context, RecyclerView recyclerView) {
        this.context = context;
        this.data = new ArrayList<>();
        this.recyclerView = recyclerView;
    }

    public void setData(List<String> data) {
        this.data = data;
        // 刷新Adapter
        notifyDataSetChanged();
    }

    /**
     * 创建并返回ViewHolder
     *
     * @param parent
     * @param viewType
     * @return
     */
    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    	// 这里的R.layout.item_layout是单个子项的布局文件
        return new MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_layout, parent, false));
    }

    /**
     * ViewHolder绑定数据
     *
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
        holder.image.setImageResource(getIcon(position));
        holder.text.setText(data.get(position));
    }

    /**
     * 返回数据数量
     *
     * @return
     */
    @Override
    public int getItemCount() {
        return data.size();
    }

    // 返回对应图片
    private int getIcon(int position) {
        switch (position % 5) {
            case 0:
                return R.mipmap.a;
            case 1:
                return R.mipmap.b;
            case 2:
                return R.mipmap.c;
            case 3:
                return R.mipmap.d;
            case 4:
                return R.mipmap.e;
        }
        return 0;
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        ImageView image;
        TextView text;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            image = itemView.findViewById(R.id.image);
            text = itemView.findViewById(R.id.text);
        }
    }
}

MainActivity中完成实例化操作

    private RecyclerView recyclerView;
    private MyRecyclerViewAdapter adapter;

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

        recyclerView = findViewById(R.id.recycler_view);

        // 线性布局
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
		recyclerView.setLayoutManager(linearLayoutManager);
        adapter = new MyRecyclerViewAdapter(this, recyclerView);
        recyclerView.setAdapter(adapter);
    }

    // 添加数据按钮
    public void onAddDataClick(View v) {
        List<String> data = new ArrayList<>();

        for (int i = 1; i < 21; i++) {
            String s = "第" + i + "条数据";
            data.add(s);
        }

        adapter.setData(data);
    }

运行效果
在这里插入图片描述

横向和反向数据显示

横向和反向数据只需要对LinearLayoutManager的实例化进行设置即可

        // 横向排列ItemView
        linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        // 数据反向显示
        linearLayoutManager.setReverseLayout(true);

横向排列显示
数据可以向右滑动
在这里插入图片描述 在这里插入图片描述
数据反向显示
从底部往上显示,可以向上滑动
在这里插入图片描述

横向+反向显示
数据可以向左滑动
在这里插入图片描述 在这里插入图片描述

网格布局

网格布局使用的是GridLayoutManager,在前面的基础上,只需要增加两行代码即可转变为网格布局

    // 切换布局按钮
    public void onChangeLayoutClick(View v) {
        // 从线型布局 切换为 网格布局
        if (recyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
            // 网格布局
            // 参数1:上下文  参数2:列数
            GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
            recyclerView.setLayoutManager(gridLayoutManager);
        }
    }

网格布局效果
在这里插入图片描述

瀑布流布局

瀑布流布局使用的是StaggeredGridLayoutManager。它每个ItemView的宽度相等,但是高度不同,在实际开发中,每个ItemView的高度都是给定的,这里没有给定直接用随机数来实现
首先在MyRecyclerViewAdapter中增加一个随机数方法

    // 返回随机数,用于瀑布布局下不同ItemView的高度(最少要等于原始高度)
    private int getRandomHeight() {
        return Math.max(ViewGroup.LayoutParams.WRAP_CONTENT, (int) (Math.random() * 1000));
    }

然后在onBindViewHolder()方法中设置随机高度

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
        holder.image.setImageResource(getIcon(position));
        holder.text.setText(data.get(position));

        // 在RecyclerView的LayoutManager为瀑布流布局的时候对TextView使用随机高度
        if (recyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
            holder.text.setLayoutParams(params);
        } else {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            holder.text.setLayoutParams(params);
        }
    }

MainActivity的切换布局按钮点击方法onChangeLayoutClick()方法中完成瀑布流布局的设置

    // 切换布局按钮
    public void onChangeLayoutClick(View v) {
        // 从线型布局 切换为 网格布局
        if (recyclerView.getLayoutManager().getClass() == LinearLayoutManager.class) {
            // 网格布局
            // 参数1:上下文  参数2:列数
            GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
            recyclerView.setLayoutManager(gridLayoutManager);
        } else if (recyclerView.getLayoutManager().getClass() == GridLayoutManager.class) {
            // 瀑布流布局
            // 参数1:列数   参数2:排列方向(竖向、横向)
            StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
            recyclerView.setLayoutManager(staggeredGridLayoutManager);
        } else {
            // 线性布局
            LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
            recyclerView.setLayoutManager(linearLayoutManager);
        }
    }

运行效果
在这里插入图片描述

ItemView监听点击事件

RecyclerView是默认不支持点击事件,可以通过自定义接口回调ItemView的点击事件
MyRecyclerViewAdapter类中,创建回调接口,并在自定义的MyViewHolder类里实例化一个View对象

    // ItemView点击事件回调接口
    interface OnItemClickListener {
        void onItemClick(int position);
    }
    
    class MyViewHolder extends RecyclerView.ViewHolder {

        ImageView image;
        TextView text;
        
        View mItemView;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            image = itemView.findViewById(R.id.image);
            text = itemView.findViewById(R.id.text);
            
            mItemView = itemView;
        }
    }

同时在onBindViewHolder()方法中为ItemView设置点击事件

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
        holder.image.setImageResource(getIcon(position));
        holder.text.setText(data.get(position));

        // 在RecyclerView的LayoutManager为瀑布流布局的时候使用随机高度
        if (recyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
            holder.text.setLayoutParams(params);
        } else {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            holder.text.setLayoutParams(params);
        }

        // 为ItemView设置点击事件
        holder.mItemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 调用接口的回调方法
                if (onItemClickListener != null) {
                    onItemClickListener.onItemClick(position);
                }
            }
        });
    }

并在MyRecyclerViewAdapter类中创建一个setOnItemClickListener()的方法

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

最后在MainActivity中实现接口为ItemView设置监听事件

        // ItemView点击事件监听
        adapter.setOnItemClickListener(new MyRecyclerViewAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(int position) {
                Toast.makeText(MainActivity.this, "第" + (position + 1) + "条数据被点击", Toast.LENGTH_SHORT).show();
            }
        });

运行效果
点击第三条数据
在这里插入图片描述

插入和删除数据

MyRecyclerViewAdapter类中增加数据的添加和删除方法
这里还要完成插入之后更新position值的操作

    // 添加一条数据,参数:添加的位置
    public void addData(int position) {
        addDataPosition = position;
        data.add(position, "插入的数据");
        // 设置插入动画(RecyclerView自带的动画显示效果,可省略)
        notifyItemInserted(position);
        // 刷新ItemView(刷新数据的position值)
        notifyItemRangeChanged(position,data.size() - position);
    }

    //删除一条数据,参数:删除的位置
    public void removeData(int position) {
        addDataPosition = -1;
        data.remove(position);
        // 设置删除动画(RecyclerView自带的动画显示效果,可省略)
        notifyItemRemoved(position);
        // 刷新ItemView(刷新数据的position值)
        notifyItemRangeChanged(position,data.size() - position);
    }

为了清楚显示,在onBindViewHolder()方法中设置一下插入的数据背景色

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, final int position) {
        holder.image.setImageResource(getIcon(position));
        holder.text.setText(data.get(position));

        // 在RecyclerView的LayoutManager为瀑布流布局的时候使用随机高度
        if (recyclerView.getLayoutManager().getClass() == StaggeredGridLayoutManager.class) {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getRandomHeight());
            holder.text.setLayoutParams(params);
        } else {
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            holder.text.setLayoutParams(params);
        }

        // 改变插入的ItemView背景色
        if (addDataPosition == position) {
            holder.mItemView.setBackgroundColor(Color.RED);
        }

        // 为ItemView设置点击事件
        holder.mItemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 调用接口的回调方法
                if (onItemClickListener != null) {
                    onItemClickListener.onItemClick(position);
                }
            }
        });
    }

MainActivity中增加两个按钮的点击事件,设定为在position = 1处插入数据

    // 添加一条数据,position = 1
    public void onInsertDataClick(View v){
        adapter.addData(1);
    }

    // 删除一条数据,position = 1
    public void onRemoveDataClick(View v){
        adapter.removeData(1);
    }

RecyclerView对象声明处,增加Animato方法来实现增加或删除动画效果

    recyclerView.setItemAnimator(new DefaultItemAnimator());

运行效果
点击插入数据,这时候插入数据的position值为1,后面的position值往后顺延
在这里插入图片描述
点击删除数据,这时后面的所有数据的position值自动修正
在这里插入图片描述

总结

RecyclerView的作用是在有限的空间里展示大批量的数据,它本身只负责回收以及复用View,需要与其它类进行搭配来完成展示效果, 如通过与Adapter的配合来实现数据和视图的连接,与LayoutManager的配合来确定数据的展示形式
RecyclerView并没有提供Item点击事件响应方法,需要自己实现
如果想要增加或删除数据,可以使用ItemAnimator来设置增加或删除动画效果
最后注意要通过notifyItemRangeChanged()方法来刷新ItemView(数据的position值)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值