RecyclerView03:装饰者模式,添加头部和底部

RecyclerView 不支持头部和底部(仿造ListView);
查看ListView源码是如何支持头部和底部的(参考HeaderViewListAdapter.java的源码,用了装饰设计模式)。
让ListAdapter变得更加强大,让其支持头部Header和底部Footer的添加。

装饰者模式,添加RecyclerView的Header和Footer。
源码中用到的装饰者模式;
UML内部类怎么画。StarUML。
模板设计模式,手写OkHttp的Dispatcher。

一. 装饰者模式

装饰设计模式也称包装设计模式,用来动态的扩展对象的功能,也是继承关系的的一种替代方案之一。
说个大白话就是,在不使用的继承的方式下,采用装饰设计模式可以扩展一个对象的功能,可以使一个对象变得越来越强大
源码中用到的装饰者设计模式 Eg:
ListView支持头部、底部。
ContextWrapper ;
IO的输入输出流;

private void testIO() {
    try {
        FileReader fr = new FileReader("xx.file");
        // is.read();
        BufferedReader br = new BufferedReader(fr);
        br.readLine(); // 拓展FileReader,让其支持读取一行。
    } catch (Exception e) {
        e.printStackTrace();
    }
}
    // 普通的写法:new一个对象,然后调用对象的方法
    // TeacherEat teacherEat = new TeacherEat();
    // teacherEat.eat();
    // 装饰者模式,一般是把类对象作为构造参数传递。
    PersonEat personEat = new PersonEat();
    TeacherEat teacherEat = new TeacherEat(personEat);
    teacherEat.eat();

二. 研究ListView源码。

 public void addHeaderView(View v, Object data, boolean isSelectable) {
        if (v.getParent() != null && v.getParent() != this) {
            if (Log.isLoggable(TAG, Log.WARN)) {
                Log.w(TAG, "The specified child already has a parent. "
                           + "You must call removeView() on the child's parent first.");
            }
        }
        final FixedViewInfo info = new FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
        // 添加到头部集合中
        mHeaderViewInfos.add(info);
        mAreAllItemsSelectable &= isSelectable;

        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) { // adapter不为空的时候,采取包裹(才能添加Header)
            // 判断当前Adapter是否为空。在原来的Adapter上,进行一层包装。让它支持Header和Footer.
            if (!(mAdapter instanceof HeaderViewListAdapter)) {
                wrapHeaderListAdapterInternal();
            }
            // In the case of re-adding a header view, or adding one later on,
            // we need to notify the observer.
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }
    }
    /** @hide */
    protected void wrapHeaderListAdapterInternal() {
        mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
    }
    /** @hide */
    protected HeaderViewListAdapter wrapHeaderListAdapterInternal(
            ArrayList headerViewInfos,
            ArrayList footerViewInfos,
            ListAdapter adapter) {
        return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter);
    }

ListView本身并不支持添加头部和底部,系统在原来的基础上包装了一层。系统帮我们写好了。
最重要的,要知道HeaderViewListAdapter是怎么写的。
HeaderViewListAdapter 中,
getCount() 、getItem(int position) 、getView(int position, View convertView, ViewGroup parent)、getItemViewType(int position).
在原来的基础上,包一层。形成新的Adapter,WrapperAdapter,里面的是原始的适配器(mAdapter)。

三. 基本使用,每次都包装一下. 相对麻烦。

    RecyclerView recyclerView = findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    mAdapter = new RecyclerAdapter(this, mData);
    // 加一层数据, 每次都要加一层。
    final WrapRecyclerAdapter adapter = new WrapRecyclerAdapter(mAdapter);
    recyclerView.setAdapter(adapter);
    View header = LayoutInflater.from(this).inflate(R.layout.layout_header_footer, recyclerView,false);
    adapter.addHeaderView(header);
    header.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            adapter.removeHeaderView(header);
        }
    });

四. 自定义RecyclerView的实现

package com.example.recyclerview01.wrapper;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
 * 自定义,包裹效果的RV:
 * 每次使用的时候,不需要再包装一下。
 * 观察者模式:
 */
public class WrapRecyclerView extends RecyclerView {

    private WrapRecyclerAdapter mWrapRecyclerAdapter;
    // 带注册的观察者.
    private AdapterDataObserver mAdapterObserver = new AdapterDataObserver() {
        // 观察者的回调.
        @Override
        public void onChanged() {
            mWrapRecyclerAdapter.notifyDataSetChanged();
            Log.d("TAG", "onChanged ");
        }
        @Override
        public void onItemRangeChanged(int positionStart, int itemCount) {
            mWrapRecyclerAdapter.notifyItemRangeChanged(positionStart, itemCount);
        }
        @Override
        public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
            mWrapRecyclerAdapter.notifyItemRangeChanged(positionStart, itemCount, payload);
        }

        @Override
        public void onItemRangeInserted(int positionStart, int itemCount) {
            mWrapRecyclerAdapter.notifyItemRangeInserted(positionStart, itemCount);
        }
        @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            Log.d("TAG", "onItemRangeRemoved " + positionStart + " " + itemCount);
            mWrapRecyclerAdapter.notifyItemRangeRemoved(positionStart, itemCount);
        }
        @Override
        public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
            mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
        }
    };

    public WrapRecyclerView(Context context) {
        this(context, null);
    }
    public WrapRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public WrapRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setAdapter(RecyclerView.Adapter adapter) {
        if (adapter instanceof WrapRecyclerAdapter) {
            mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
        } else {
            // 删除的bug: adapter中数据已经修改了。但是mWrapRecyclerAdapter中的数据没修改。
            // 所以需要关联---观察者.
            mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
            // 注册观察者
            adapter.registerAdapterDataObserver(mAdapterObserver);
        }
        super.setAdapter(mWrapRecyclerAdapter);
    }

    // 添加header、footer的方法
    public void addHeaderView(View view) {
        // 要先setAdapter,mWrapRecyclerAdapter才不为空.
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.addHeaderView(view);
        }
    }
    public void addFooterView(View view) {
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.addFooterView(view);
        }
    }
    public void removeHeaderView(View view) {
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.removeHeaderView(view);
        }
    }
    public void removeFooterView(View view) {
        if (mWrapRecyclerAdapter != null) {
            mWrapRecyclerAdapter.removeFooterView(view);
        }
    }
}

xml中使用自定义RecyclerView


// layout_header_footer.xml



xml中使用自定义RecyclerView

/**
 * RecyclerView头部和底部的包装类。
 */
public class WrapRecyclerAdapter extends RecyclerView.Adapter {

    // 数据列表原始的适配器,它不包含头部
    private RecyclerView.Adapter mAdapter;
    // 头部和底部的集合ArrayList不好区分是否已经添加过了;使用Map集合,进行标识是头部还是底部?
    // 如果key是int,value是Object对象,建议使用SparseArray
    private SparseArray mHeaders, mFooters;

    private static int BASE_HEADER_KEY = 1000000;
    private static int BASE_FOOTER_KEY = 2000000;

    public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
        this.mAdapter = adapter;
        mHeaders = new SparseArray();
        mFooters = new SparseArray();
    }

    @Override
    public int getItemViewType(int position) {
        // 根据当前位置position,返回当前viewType.
        // 头部;mAdapter中间的数据;底部.
        int numHeaders = mHeaders.size();
        if (position < numHeaders) {
            return mHeaders.keyAt(position);
        }
        // mAdapter
        final int adjPosition = position - numHeaders; // 位置修正
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemViewType(adjPosition);
            }
        }
        // footer
        int footerPosition = adjPosition - adapterCount;
        return mFooters.keyAt(footerPosition);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        // 根据viewType,来区分是头部/底部,还是列表中元素。返回对应的key。
        if (mHeaders.indexOfKey(viewType) >= 0) {
            // 有头部,获取到对应头部,并包装成ViewHolder.
            return createHeaderFooterViewHolder(mHeaders.get(viewType));
        } else if (mFooters.indexOfKey(viewType) >= 0) {
            // 底部
            return createHeaderFooterViewHolder(mFooters.get(viewType));
        }
        // 中间列表
        return mAdapter.createViewHolder(parent, viewType);
    }

    // 将header,footer包装成ViewHolder。
    private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
        return new RecyclerView.ViewHolder(view) {
        };
    }

    /**
     * 是不是底部类型
     */
    private boolean isFooterViewType(int viewType) {
        int position = mFooters.indexOfKey(viewType);
        return position >= 0;
    }

    /**
     * 是不是头部类型
     */
    private boolean isHeaderViewType(int viewType) {
        int position = mHeaders.indexOfKey(viewType);
        return position >= 0;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        // 头部和底部,不用绑定数据
        int numHeaders = mHeaders.size();
        if (position < numHeaders) {
            return;
        }
        // mAdapter
        final int adjPosition = position - numHeaders; // 位置修正
        if (mAdapter != null) {
            int adapterCount = mAdapter.getItemCount(); // 总数量
            if (adjPosition < adapterCount) {
                // 绑定数据,使用矫正后的位置adjPosition
                mAdapter.onBindViewHolder(holder, adjPosition);
            }
        }
    }

    @Override
    public int getItemCount() {
        int totalCount = mAdapter.getItemCount() + mHeaders.size() + mFooters.size();
        return totalCount;
    }


    // 添加移除,头部和底部
    public void addHeaderView(View view) {
        // 不能重复添加
       /* if (mHeaders.indexOfValue(view) == -1) {
            // 集合中没有,就添加。不能重复添加。
            mHeaders.put(BASE_HEADER_KEY++, view);
        }*/
        int position = mHeaders.indexOfValue(view);
        if (position < 0) {
            mHeaders.put(BASE_HEADER_KEY++, view);
        }
        Log.d("TAG", "mHeaders:" + mHeaders.size());

        notifyDataSetChanged();
    }

    public void addFooterView(View view) {
        if (mFooters.indexOfValue(view) == -1) {
            // 集合中没有,就添加。不能重复添加。
            mFooters.put(BASE_FOOTER_KEY++, view);
            notifyDataSetChanged();
        }
    }
    public void removeHeaderView(View view) {
        if (mHeaders.indexOfValue(view) >= 0) {
            mHeaders.removeAt(mHeaders.indexOfValue(view));
            notifyDataSetChanged();
        }
    }
    public void removeFooterView(View view) {
        if (mFooters.indexOfValue(view) >= 0) {
            mFooters.removeAt(mFooters.indexOfValue(view));
            notifyDataSetChanged();
        }
    }
}

使用。

    private RecyclerAdapter mListAdapter;

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

        WrapRecyclerView mRecyclerView = (WrapRecyclerView) findViewById(R.id.recyclerView);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mListAdapter = new RecyclerAdapter(this, mData);
        mRecyclerView.setAdapter(mListAdapter);

        View header = LayoutInflater.from(this).inflate(R.layout.layout_header_footer, mRecyclerView, false);
        mRecyclerView.addHeaderView(header);
        header.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mRecyclerView.removeHeaderView(header);
            }
        });
        mRecyclerView.addItemDecoration(new GridLayoutItemDecoration(this, R.drawable.item_divider_01));
        // 添加点击事件。
        mListAdapter.setOnItemClickListener(new RecyclerAdapter.ItemClickListener() {
            @Override
            public void onItemClick(int position) {
                Toast.makeText(BaseUseActivity.this, "item:" + mData.get(position), Toast.LENGTH_SHORT).show();
                mData.remove(position);
                mListAdapter.notifyDataSetChanged(); // 回调观察者的onChanged方法
//                mListAdapter.notifyItemRemoved(position); // 回调观察者的onItemRangeRemoved方法
            }
        });
    }

五.

你可能感兴趣的:(RecyclerView03:装饰者模式,添加头部和底部)