1. 程式人生 > >Material Design RecyclerView新增頭部底部佈局(五)

Material Design RecyclerView新增頭部底部佈局(五)

前言:相信很多人都知道,現在的日常開發中,已經逐漸由RecyclerView代替ListView、GridView,但相對於後者來說,RecyclerView存在著一些缺點,比如這篇文章要講的,RecyclerView沒有新增頭部佈局和底部佈局的方法。

想到這裡,我就想著那listview裡面是怎麼實現的呢?是不是可以去模仿Listview也寫一個自己的RecyclerView,話不多說,直接開始

ListView原始碼分析:

	//新增存放頭部View和底部view的集合
	private ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList();
	private ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList();
	//新增頭部
	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;
		//如果mAdapter不為空,這個if的作用個人理解應該是在setAdapter後,使用者假如再次新增頭部佈局,
		會將原先已經經歷過一次包裝的HeaderViewListAdapter再一次包裝
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewListAdapter)) {
				//如果此adapter沒有被包裝過,包裝成具有HeaderView的adapter
                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }
    }
	
	public void addFooterView(View v, Object data, boolean isSelectable) {
		//儲存底部資訊
        final FixedViewInfo info = new FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
		//新增資訊到底部集合
        mFooterViewInfos.add(info);
        mAreAllItemsSelectable &= isSelectable;
      	//如果mAdapter不為空,這個if的作用個人理解應該是在setAdapter後,使用者假如再次新增底部佈局,
		會將原先已經經歷過一次包裝的HeaderViewListAdapter再一次包裝
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewListAdapter)) {
				//如果此adapter沒有被包裝過,包裝成具有FooterView的adapter
                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }
    }
	
	@Override
	public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        resetList();
        mRecycler.clear();
		//如果頭部集合或者底部集合不為空,則將Activity編寫的adapter帶入到HeadViewListAdapter進行包裝,否則還是用原來的adapter
        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

        mOldSelectedPosition = INVALID_POSITION;
        mOldSelectedRowId = INVALID_ROW_ID;

        // AbsListView#setAdapter will update choice mode states.
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();
            checkFocus();

            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);

            mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

            int position;
            if (mStackFromBottom) {
                position = lookForSelectablePosition(mItemCount - 1, false);
            } else {
                position = lookForSelectablePosition(0, true);
            }
            setSelectedPositionInt(position);
            setNextSelectedPositionInt(position);

            if (mItemCount == 0) {
                // Nothing selected
                checkSelectionChanged();
            }
        } else {
            mAreAllItemsSelectable = true;
            checkFocus();
            // Nothing selected
            checkSelectionChanged();
        }

        requestLayout();
    }
	
	
	
	HeaderViewListAdapter:
		//得到主體item、頭部item、底部item總和
	    public int getCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }
	
	//根據不同條件返回主體、頭部、底部View
	public View getView(int position, View convertView, ViewGroup parent) {
        // Header (negative positions will throw an IndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return mHeaderViewInfos.get(position).view;
        }

        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getView(adjPosition, convertView, parent);
            }
        }
        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
        return mFooterViewInfos.get(adjPosition - adapterCount).view;
    }

以上程式碼運用了強大的裝飾者模式,裝飾者模式實現的是從一個物件外部給物件新增功能,
相當於改變了物件的外觀,裝飾過的物件,從外部系統來看已經不再是原來的物件,而是經過一系列裝飾器裝飾過的物件。 
這裡就是將自己的寫的Adapter轉換成了內部的HeadViewListAdapter。

這裡我們知道了listview如何實現新增頭部和底部佈局的現在我們就來模仿listview做一個新增頭部和底部佈局的Recyclerview

WrapRecyclerView.java:

public class WrapRecyclerView extends RecyclerView{

    private ArrayList<View> mHeaderViewInfos = new ArrayList<View>();
    private ArrayList<View> mFooterViewInfos = new ArrayList<View>();
    private Adapter mAdapter;

    public WrapRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void addHeaderView(View v) {
        mHeaderViewInfos.add(v);
		
        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }

        }
    }

    public void addFooterView(View v) {
        mFooterViewInfos.add(v);
        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {
                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }
        }
    }


    @Override
    public void setAdapter(Adapter adapter) {

        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewRecyclerAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }
        super.setAdapter(mAdapter);
    }
}

HeaderViewRecyclerAdapter.java:

public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter {

    private RecyclerView.Adapter mAdapter;
    ArrayList<View> mHeaderViewInfos;
    ArrayList<View> mFooterViewInfos;


    public HeaderViewRecyclerAdapter(ArrayList<View> headerViewInfos, ArrayList<View> footerViewInfos, RecyclerView.Adapter adapter) {
        mAdapter = adapter;
        if (headerViewInfos == null) {
            mHeaderViewInfos = new ArrayList<View>();
        } else {
            mHeaderViewInfos = headerViewInfos;
        }
        if (footerViewInfos == null) {
            mFooterViewInfos = new ArrayList<View>();
        } else {
            mFooterViewInfos = footerViewInfos;
        }

    }

    //判讀當前條目是什麼型別的--決定渲染什麼檢視
    @Override
    public int getItemViewType(int position) {
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {    //是頭部
            return RecyclerView.INVALID_TYPE;
        }
        //正常條目部分
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                return mAdapter.getItemViewType(adjPosition);
            }
        }
        //footer部分
        return RecyclerView.INVALID_TYPE - 1;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //header
        if(viewType == RecyclerView.INVALID_TYPE){//頭部
            return new HeadViewHolder(mHeaderViewInfos.get(0));
        }else if(viewType == RecyclerView.INVALID_TYPE-1){//尾部
            return new HeadViewHolder(mFooterViewInfos.get(0));
        }
        return mAdapter.onCreateViewHolder(parent,viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        //也要劃分三個區域
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {    //是頭部
           return;
        }
        //adapter body
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                mAdapter.onBindViewHolder(holder,adjPosition);
                return ;
            }
        }
        //footer部分

    }

    @Override
    public int getItemCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }

    public int getHeadersCount() {
        return mHeaderViewInfos.size();
    }

    public int getFootersCount() {
        return mFooterViewInfos.size();
    }

    private static class HeadViewHolder extends RecyclerView.ViewHolder{

        public HeadViewHolder(View itemView) {
            super(itemView);
        }
    }
}

MyAdapter.java:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private List<String> list;

    public MyAdapter(List<String> list) {
        this.list = list;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
        View view = layoutInflater.inflate(R.layout.listitem, parent, false);
        ViewHolder holder = new ViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.tv.setText(list.get(position));
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder{
        public TextView tv;


        public ViewHolder(View itemView) {
            super(itemView);
            tv = (TextView) itemView.findViewById(R.id.tv);
        }
    }
}

MainActivity.java:
public class MainActivity extends AppCompatActivity {

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

        WrapRecyclerView recyclerView = (WrapRecyclerView) findViewById(R.id.recyclerview);


        //View headerView = View.inflate(this,resource,root);
        TextView headerView = new TextView(this);
        //TextView tv = headerView.findViewById(id);
        ActionBar.LayoutParams params = new ActionBar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        headerView.setLayoutParams(params);
        headerView.setText("top");
        recyclerView.addHeaderView(headerView);

        TextView footview = new TextView(this);
        footview.setLayoutParams(params);
        footview.setText("bottom");
        recyclerView.addFooterView(footview);

        List<String> list = new ArrayList<>();
        for (int i = 0; i < 30; i++) {
            list.add("item"+i);
        }
        MyAdapter adapter = new MyAdapter(list);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
    }
}

這裡相關的佈局檔案也很簡單,這裡就不貼程式碼了。

總結完畢。

不喜勿噴 謝謝!