前言
裝飾設(shè)計(jì)模式系列文章
裝飾設(shè)計(jì)模式(一) - 吃飯小示例
裝飾設(shè)計(jì)模式(二) - RecyclerView添加頭部和底部
裝飾設(shè)計(jì)模式(三) - 源碼中用到的裝飾設(shè)計(jì)模式
1. 概述
上一節(jié)我們講解了裝飾設(shè)計(jì)模式的定義潘悼、及如何去寫裝飾設(shè)計(jì)模式,并且也寫了一個(gè)小的實(shí)例 —— 吃飯小示例镇辉,那么這一節(jié)我們就采用裝飾設(shè)計(jì)模式來分析下 給RecyclerView添加頭部和底部
2. RecyclerView 不支持添加頭部和底部
其實(shí) RecyclerView是不支持頭部和底部的View對(duì)象的添加湿酸,但是ListView是支持的早抠。我們必須要想辦法解決這個(gè)問題到踏,當(dāng)然網(wǎng)上的解決辦法有很多遂填,但是我個(gè)人所贊同的就是采用裝飾設(shè)計(jì)模式 添加頭部和底部,那么接下來我們就首先來看下ListView它為什么可以添加頭部和底部逼蒙;
3. ListView為什么可以添加頭部和底部从绘?
- ListView源碼中的 addHeaderView()方法,里邊有 HeaderViewListAdapter 這個(gè)類是牢,這個(gè)類是ListView源碼給我們寫好的僵井,可以添加頭部和底部;
- 而RecyclerView是沒有寫這個(gè)類驳棱,所以 RecyclerView不支持手動(dòng)添加 頭部和底部批什,但是我們完全可以參照 ListView中的這個(gè)類的寫法,來 實(shí)現(xiàn) RecyclerView添加 頭部和底部社搅;
- HeaderViewListAdapter這個(gè)類也是采用的是 裝飾設(shè)計(jì)模式驻债,讓 ListAdapter 變得更加強(qiáng)大;
public void addHeaderView(View v, Object data, boolean isSelectable) {
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) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
wrapHeaderListAdapterInternal();
}
}
}
4. 具體代碼如下
1>:封裝 WrapRecyclerView 替代 原始的 RecyclerView
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/12 18:47
* Version 1.0
* Params:
* Description: 封裝的 WrapRecyclerView形葬,替代 原始的 RecyclerView
*/
public class WrapRecyclerView extends RecyclerView {
private WrapRecyclerAdapter mAdapter ;
public WrapRecyclerView(Context context) {
super(context);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setAdapter(Adapter adapter) {
mAdapter = new WrapRecyclerAdapter(adapter) ;
super.setAdapter(adapter);
}
/**
* 添加頭部
*/
public void addHeaderView(View view){
// 必須是:在設(shè)置 setAdapter之后合呐,才能夠去設(shè)置頭部和底部
if (mAdapter != null){
mAdapter.addHeaderView(view);
}
}
/**
* 添加底部
*/
public void addFooterView(View view){
if (mAdapter != null){
mAdapter.addFooterView(view);
}
}
/**
* 移除頭部
*/
public void removeHeaderView(View view){
if (mAdapter != null){
mAdapter.removeHeaderView(view);
}
}
/**
* 移除底部
*/
public void removeFooterView(View view){
if (mAdapter != null){
mAdapter.removeFooterView(view);
}
}
}
2>:裝飾設(shè)計(jì)模式的 RecyclerView.Adapter
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/12 17:25
* Version 1.0
* Params:
* Description: 裝飾設(shè)計(jì)模式的 RecyclerView.Adapter - 我們對(duì)其進(jìn)行功能擴(kuò)展,使其能夠支持頭部和底部的添加
*/
public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
// 原來的 RecyclerView.Adapter笙以,并不支持頭部和底部的添加
private final RecyclerView.Adapter mRealAdapter ;
ArrayList<View> mHeaderViews;
ArrayList<View> mFooterViews;
public WrapRecyclerAdapter(RecyclerView.Adapter adapter){
this.mRealAdapter = adapter ;
// 只要MainActivity中點(diǎn)擊 item條目 調(diào)用了 notifyDataSetChanged淌实,
// 這里就會(huì)收到通知,然后在這里也調(diào)用 notifyDataSetChanged方法,就能夠刪除item了
mRealAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
notifyDataSetChanged();
}
});
mHeaderViews = new ArrayList() ;
mFooterViews = new ArrayList<>() ;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
// 現(xiàn)在問題就是:如果想知道是哪個(gè)部分拆祈,就必須知道 position恨闪,也就是位置,但是目前只有 viewType
// 頭部返回 頭部的ViewHolder
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return createHeaderFooterViewHolder(mHeaderViews.get(position));
}
// mRealAdapter返回 mRealAdapter的ViewHolder
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
// 直接傳 position ,不兼容 萬能適配多布局條目
return mRealAdapter.onCreateViewHolder(parent,mRealAdapter.getItemViewType(adjPosition));
}
}
// 底部返回 底部的ViewHolder
return createHeaderFooterViewHolder(mFooterViews.get(adjPosition - adapterCount));
}
@Override
public int getItemViewType(int position) {
// 把 位置postion 作為 viewType
return position;
}
public int getHeadersCount() {
return mHeaderViews.size();
}
public int getFootersCount() {
return mFooterViews.size();
}
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view){};
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// 頭部和底部是不需要處理的放坏,只有中間的 mRealAdapter需要處理
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return ;
}
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mRealAdapter != null) {
adapterCount = mRealAdapter.getItemCount();
if (adjPosition < adapterCount) {
mRealAdapter.onBindViewHolder(holder,position);
}
}
}
/**
* 總共返回有多少條 = 頭部條數(shù) + 真實(shí)的Adapter條數(shù) + 底部條數(shù)
*/
@Override
public int getItemCount() {
return mHeaderViews.size() + mRealAdapter.getItemCount() + mFooterViews.size();
}
/**
* 添加頭部
*/
public void addHeaderView(View view){
if (!mHeaderViews.contains(view)){
mHeaderViews.add(view) ;
notifyDataSetChanged();
}
}
/**
* 添加底部
*/
public void addFooterView(View view){
if (!mFooterViews.contains(view)){
mFooterViews.add(view) ;
notifyDataSetChanged();
}
}
/**
* 移除頭部
*/
public void removeHeaderView(View view){
if(mHeaderViews.contains(view)){
mHeaderViews.remove(view) ;
notifyDataSetChanged();
}
}
/**
* 移除底部
*/
public void removeFooterView(View view){
if (mFooterViews.contains(view)){
mFooterViews.remove(view) ;
notifyDataSetChanged();
}
}
}
3>:最后在 MainActivity中調(diào)用即可:
/**
* Email: 2185134304@qq.com
* Created by Novate 2018/5/12 17:06
* Version 1.0
* Params:
* Description:
*/
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView ;
private List<Integer> mItems;
private WrapRecyclerView recycler_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wrap_recyclerview) ; // activity_main
mItems = new ArrayList<>();
for (int i=0;i<100;i++){
mItems.add(i);
}
// 使用原始 RecyclerView凛剥,每次都必須 創(chuàng)建RecyclerView,然后寫裝飾設(shè)計(jì)模式WrapRecyclerAdapter
// 把 RecyclerAdapter 包裝到 WrapRecyclerAdapter中轻姿,然后setAdapter時(shí)候 設(shè)置 WrapRecyclerAdapter
// 比較麻煩 犁珠,下邊 把RecyclerView封裝成WrapRecyclerView
/*
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view) ;
// RecyclerView必須設(shè)置布局管理,否則沒有數(shù)據(jù)
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
*//*mRecyclerView.setAdapter(new RecyclerAdapter());*//*
// 采用裝飾設(shè)計(jì)模式互亮,讓其支持添加頭部和底部
RecyclerAdapter recyclerAdapter = new RecyclerAdapter() ;
WrapRecyclerAdapter wrapRecyclerAdapter = new WrapRecyclerAdapter(recyclerAdapter) ;
mRecyclerView.setAdapter(wrapRecyclerAdapter);
// 添加頭部 下邊這兩種效果是一樣的 犁享,但是這種寫法有問題,就是頭部只有一小部分豹休,并沒有充滿屏幕
// 原因就是:父布局容器為null炊昆,才導(dǎo)致沒有充滿屏幕的,只需要添加一個(gè)父布局容器威根,把null修改為mRecyclerView就可以了
// View headView = LayoutInflater.from(this).inflate(R.layout.layout_header_view , null) ;
// View headView = LayoutInflater.from(this).inflate(R.layout.layout_header_view , null , false) ;
View headView = LayoutInflater.from(this).inflate(R.layout.layout_header_view , mRecyclerView , false) ;
wrapRecyclerAdapter.addHeaderView(headView);*/
// 把RecyclerView封裝成WrapRecyclerView
recycler_view = (WrapRecyclerView) findViewById(R.id.recycler_view);
recycler_view.setLayoutManager(new LinearLayoutManager(this));
recycler_view.setAdapter(new RecyclerAdapter());
View headView = LayoutInflater.from(this).inflate(R.layout.layout_header_view , recycler_view , false) ;
recycler_view.addHeaderView(headView);
}
private class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder>{
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_rv , parent , false) ;
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.text.setText("position = "+mItems.get(position));
// 這里是刪除RecyclerView 的 item凤巨,在這里移除當(dāng)前item,調(diào)用notifyDataSetChanged
// 然后在WrapRecyclerAdapter的構(gòu)造方法中 注冊(cè)觀察者registerAdapterDataObserver洛搀,
// 再去調(diào)用調(diào)用notifyDataSetChanged就可以刪除item了
holder.text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mItems.remove(position) ;
notifyDataSetChanged();
}
});
}
@Override
public int getItemCount() {
return mItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public TextView text ;
public ViewHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.text) ;
}
}
}
}