• 可添加头尾的RecycleView的实现


    界面编码设计实现中,我们肯定会用到列表展示控件,大家肯定用过ListView。后来google推出了RecycleView,帮我们去做了很多优化(内置viewholder增加复用率、可以支持局部刷新、布局可以通过外层指定layout等),正常的使用,如下:

     	 MyRecycleViewAdapter adapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_decorator);
            Component component = new ConCreateComponent();
            ComponentImplA impl1 = new ComponentImplA(component);
            impl1.operation();
            List<String> list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                list.add("position " + i);
            }
            adapter = new MyRecycleViewAdapter(this);
            adapter.setData(list);
        }
    
        /**
         * 原始的yRecycleViewAdapter v1
         */
        public void buttonv1(View view) {
            findViewById(R.id.recycleview).setVisibility(View.VISIBLE);
            findViewById(R.id.wrapperR).setVisibility(View.GONE);
    
            RecyclerView recyclerView = findViewById(R.id.recycleview);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            recyclerView.setAdapter(adapter);
        }
    

    在这里插入图片描述

    但是RecycleView大家发现有一个问题,我们如果想要为这个RecycleView添加自定义的头部view、尾部view的话,官方这个明显做不到,那这时我们可以考虑用装饰者模式或者继承去扩展一下。

    设计UML图

    首先我们通过UML图,来设计一下,设计之前想一下,我们是想要扩展RecyclerView.Adapter和RecyclerView,从而可以实现addHeadView、addFootView的功能,那么需要以下几步骤。

    1)首先,由于RecyclerView.Adapter已经是一个抽象类接口,我们自己继承与它,然后进行包装定义为WrapperRecyclerAdapter类
    2)WrapperRecyclerAdapter肯定要持有RecyclerView.Adapter的引用,所以需要有一个构造方法,将RecyclerView.Adapter的引用传递进来
    3)由于WrapperRecyclerAdapter继承与RecyclerView.Adapter,肯定要去实现关键的方法,onCreateViewHolder(创建viewitem的holder)、onBindViewHolder(viewholder数据绑定)、getItemCount(获取列表item的数量)
    4)关键的一步来了,就是使用RecyclerView.Adapter、footviews、headviews,这三者组合,重写上面的三个重要方法,给列表相应位置创建对应的item

    在这里插入图片描述

    代码实现1

    WrapperRecyclerAdapter

    package com.itbird.design.decorator.recycleview;
    
    import android.view.View;
    import android.view.ViewGroup;
    
    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.RecyclerView;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * RecyclerView.Adapter包装类,扩展实现headView、footView的添加
     * Created by itbird on 2022/6/10
     */
    public class WrapperRecyclerAdapter extends RecyclerView.Adapter {
        RecyclerView.Adapter adapter;
        List<View> headViews = new ArrayList<>();
        List<View> footViews = new ArrayList<>();
    
        public WrapperRecyclerAdapter(RecyclerView.Adapter adapter) {
            this.adapter = adapter;
            adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
                @Override
                public void onChanged() {
                    notifyDataSetChanged();
                }
            });
        }
    
        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
            //头部的,返回头部的viewholder
            if (position < headViews.size()) {
                return new WrapperViewHolder(headViews.get(position));
            }
            //adapter返回中间数据holder
            if (position >= headViews.size() && position < headViews.size() + adapter.getItemCount()) {
                return adapter.onCreateViewHolder(parent, adapter.getItemViewType(position - headViews.size()));
            }
    
            //尾部的,返回尾部的viewholder
            return new WrapperViewHolder(footViews.get(position - headViews.size() - adapter.getItemCount()));
        }
    
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            if (position < headViews.size() || position >= adapter.getItemCount() + headViews.size()) {
                return;
            }
            //头部和底部不需要做处理,只需要真实的adapter需要处理
            adapter.onBindViewHolder(holder, position - headViews.size());
        }
    
        @Override
        public int getItemViewType(int position) {
            return position;
        }
    
        @Override
        public int getItemCount() {
            return headViews.size() + footViews.size() + adapter.getItemCount();
        }
    
        public void addHeadView(View view) {
            if (!headViews.contains(view)) {
                headViews.add(view);
                notifyDataSetChanged();
            }
        }
    
        public void addFootView(View view) {
            if (!footViews.contains(view)) {
                footViews.add(view);
                notifyDataSetChanged();
            }
        }
    
        public void removeHeadView(View view) {
            if (headViews.contains(view)) {
                headViews.add(view);
                notifyDataSetChanged();
            }
        }
    
        public void removeFootView(View view) {
            if (footViews.contains(view)) {
                footViews.remove(view);
                notifyDataSetChanged();
            }
        }
    
        static class WrapperViewHolder extends RecyclerView.ViewHolder {
            public WrapperViewHolder(@NonNull View itemView) {
                super(itemView);
            }
        }
    }
    
    

    这时再去调用,发现就可以如下调用

        /**
         * 扩展的,可以增加头尾的recycleview v2
         */
        public void buttonv2(View view) {
            findViewById(R.id.recycleview).setVisibility(View.VISIBLE);
            findViewById(R.id.wrapperR).setVisibility(View.GONE);
    
            RecyclerView recyclerView = findViewById(R.id.recycleview);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            WrapperRecyclerAdapter wrapperRecyclerAdapter = new WrapperRecyclerAdapter(adapter);
            //这里head为什么不会全屏,因为LayoutInflater需要parent才会全屏
            wrapperRecyclerAdapter.addHeadView(LayoutInflater.from(this).inflate(R.layout.layout_header_view, recyclerView, false));
            wrapperRecyclerAdapter.addFootView(new Button(this));
            recyclerView.setAdapter(wrapperRecyclerAdapter);
    //        面向对象的六大基本原则,好像不符合最小知道原则,每次调用需要去new WrapperRecyclerAdapter这样的一个包装者,这肯定是不对的,所以需要封装自己的recycleview
        }
    

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

    代码实现2

    这里我们发现一个问题,这样岂不是让开发者,每每次去使用的时候,new原始的adapter,还需要去new WrapperRecyclerAdapter,然后才能给recyclerView去setAdapter,面向对象的六大基本原则,好像不符合最小知道原则,每次调用需要去new WrapperRecyclerAdapter这样的一个包装者,这肯定是不对的,所以需要封装自己的recycleview。

    所以我们做如下优化,将WrapperRecyclerAdapter的new操作,我们可以放入recyclerView中,这样外界开发者只需要去关心WrapperRecycleView和RecyclerView.Adapter就可以了,对于开发者来讲,只需关心RecyclerView自定义就可以了。

    自定义WrapperRecycleView,重写方法setAdapter,用于封装new WrapperRecyclerAdapter的操作

    package com.itbird.design.decorator.recycleview;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.View;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.recyclerview.widget.RecyclerView;
    
    /**
     * 自定义WrapperRecycleView,重写方法setAdapter,用于封装new WrapperRecyclerAdapter的操作
     * Created by itbird on 2022/6/10
     */
    public class WrapperRecycleView extends RecyclerView {
        WrapperRecyclerAdapter wrapperRecyclerAdapter;
    
        public WrapperRecycleView(@NonNull Context context) {
            super(context);
        }
    
        public WrapperRecycleView(@NonNull Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public WrapperRecycleView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
    
        @Override
        public void setAdapter(@Nullable Adapter adapter) {
            wrapperRecyclerAdapter = new WrapperRecyclerAdapter(adapter);
            super.setAdapter(wrapperRecyclerAdapter);
        }
    
        @Nullable
        @Override
        public Adapter getAdapter() {
            return wrapperRecyclerAdapter;
        }
    
        public void addHeadView(View view) {
            wrapperRecyclerAdapter.addHeadView(view);
        }
    
        public void addFootView(View view) {
            wrapperRecyclerAdapter.addFootView(view);
        }
    
        public void removeHeadView(View view) {
            wrapperRecyclerAdapter.removeHeadView(view);
        }
    
        public void removeFootView(View view) {
            wrapperRecyclerAdapter.removeFootView(view);
        }
    }
    
    

    调用一下

      /**
         * 将wrapperadapter的new操作,内部实现 v3
         * 封装的必要性,这样的话,只需要关注WrapperRecycleView,不再需要关注WrapperRecyclerAdapter
         */
        public void buttonv3(View view) {
            findViewById(R.id.wrapperR).setVisibility(View.VISIBLE);
            findViewById(R.id.recycleview).setVisibility(View.GONE);
    
            WrapperRecycleView wrapperRecycleView = findViewById(R.id.wrapperR);
            wrapperRecycleView.setLayoutManager(new LinearLayoutManager(this));
            wrapperRecycleView.setAdapter(adapter);
            wrapperRecycleView.addHeadView(LayoutInflater.from(this).inflate(R.layout.layout_header_view, wrapperRecycleView, false));
            wrapperRecycleView.addFootView(new Button(this));
    
            //这时再去考虑一个事情,我们通过装饰者模式把adapter封装了一层,如果adpater有数据更新,导致变动,这时会有问题吗?
            //这时会发现,并未更新,原因是装饰类,并未做事件响应
        }
    

    是不是简单了很多。

  • 相关阅读:
    ChatGPT将引发网络安全三大革命
    分布事务和分布式锁
    液位检测仪在线监测系统解决方案
    2020年MathorCup数学建模B题养老服务床位需求预测与运营模式研究全过程解题程序及多篇论文
    【Qt】modbus之串口模式写操作
    新版edge浏览器读取谷歌浏览器上的历史记录
    60岁首席工程师被SpaceX边缘化,主管:我怕他退休或死了
    SystemVerilog学习-06-类的封装
    curl、openssl、mbedtls的交叉编译过程
    出现 nested exception is java.sql.SQLException: 无效的列类型 的解决方法
  • 原文地址:https://blog.csdn.net/baobei0921/article/details/127089334