概述
1供嚎、目的:為RecyclerView添加頭部和底部視圖
2、分析:RecyclerView在使用的過程中峭状,沒有發(fā)現(xiàn)想ListView中addHeadView()克滴、addFooterView()的方法
3、實現(xiàn)思路:模仿ListView的方式實現(xiàn)优床,研究ListView的添加頭部和底部視圖的實現(xiàn)源碼劝赔,依照同樣的模式實現(xiàn)
ListView添加頭部和底部的功能源碼解析
1、查看一下ListView的addHeaderView()方法:
從如下的方法中查看關(guān)鍵信息胆敞,會看到mHeaderViewInfos.add(info);就是將傳入的參數(shù)view通過mHeaderViewInfos進行存儲着帽,存放要添加的headerview
接著看到一個判斷語句 if (!(mAdapter instanceof HeaderViewListAdapter)),這個判斷中有一個方法wrapHeaderListAdapterInternal();進去看到的是mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
意思是如果這個mAdapte不是HeaderViewListAdapter竿秆,那么就把它轉(zhuǎn)化成一個HeaderViewListAdapter启摄,同時把這個原本的mAdapter通過構(gòu)造方法傳遞到HeaderViewListAdapter這個類中稿壁,傳入adapter的方式肯定是setAdapter方法幽钢,接著看setAdapter
addHeaderView源碼:
publicvoidaddHeaderView(View v, Object data,booleanisSelectable){
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.");
}
}
finalFixedViewInfo info =newFixedViewInfo();
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(!(mAdapterinstanceofHeaderViewListAdapter)) {
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();
}
}
}
protectedvoidwrapHeaderListAdapterInternal(){
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
2、查看一下ListView的setAdapter()方法:
從這個方法看到如下關(guān)鍵代碼塊:
if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {
mAdapter= wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
}else{
mAdapter= adapter;
}
就是說setAdapter會先去判斷是否你前面添加過headerView或者添加過footerView傅是,以此來判斷應(yīng)該給你轉(zhuǎn)化為一個HeaderViewListAdapter還是直接給你 mAdapter = adapter匪燕。
這里體現(xiàn)了偷梁換柱的概念,就是說你添加了headerView或者footerView就用一個新的Adapter來替代你傳入的adapter,這個在設(shè)計模式上使用了裝飾設(shè)計模式喧笔,接下來查看一下HeaderViewListAdapter
setAdapter源碼:
@Override
publicvoidsetAdapter(ListAdapter adapter){
if(mAdapter !=null&& mDataSetObserver !=null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {
mAdapter = wrapHeaderListAdapterInternal(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 =newAdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
intposition;
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();
}
3帽驯、查看HeaderViewListAdapter關(guān)鍵源碼:
分析看到getView()方法有三處判斷,分別是返回了三種視圖书闸,頭部視圖尼变、內(nèi)容視圖、底部視圖浆劲;發(fā)現(xiàn)頭部視圖和底部視圖專門處理嫌术,內(nèi)容視圖直接復(fù)用了傳入的adapter的getView()方法。
publicViewgetView(intposition, View convertView, ViewGroup parent){
// Header (negative positions will throw an IndexOutOfBoundsException)
intnumHeaders = getHeadersCount();
if(position < numHeaders) {
returnmHeaderViewInfos.get(position).view;
}
// Adapter
finalintadjPosition = position - numHeaders;
intadapterCount =0;
if(mAdapter !=null) {
adapterCount = mAdapter.getCount();
if(adjPosition < adapterCount) {
returnmAdapter.getView(adjPosition, convertView, parent);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
returnmFooterViewInfos.get(adjPosition - adapterCount).view;
}
publicintgetItemViewType(intposition){
intnumHeaders = getHeadersCount();
if(mAdapter !=null&& position >= numHeaders) {
intadjPosition = position - numHeaders;
intadapterCount = mAdapter.getCount();
if(adjPosition < adapterCount) {
returnmAdapter.getItemViewType(adjPosition);
}
}
returnAdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
綜上分析牌借,要實現(xiàn)RecyleView添加頭部和底部功能度气,
1、需要自定義一個RecyleView膨报,添加addHeaerView()磷籍、addFooterView()
2适荣、其次自定義一個HeaderViewRecyleViewAdapter,用于實現(xiàn)添加頭部和底部視圖的展示
自定義RecyleView實現(xiàn)WrapRecyleView類:
publicclassWrapRecyleViewextendsRecyclerView{
privateArrayList mHeaderViewInfos =newArrayList<>();
privateArrayList? mFooterViewInfos =newArrayList<>();
privateAdapter mAdapter;
publicWrapRecyleView(Context context,AttributeSet attrs){
super(context, attrs);
}
publicvoidaddHeaderView(View v){
mHeaderViewInfos.add(v);
// Wrap the adapter if it wasn't already wrapped.
if(mAdapter !=null) {
if(!(mAdapterinstanceofHeaderViewRecyleViewAdapter)) {
mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
publicvoidaddFooterView(View v){
mFooterViewInfos.add(v);
// Wrap the adapter if it wasn't already wrapped.
if(mAdapter !=null) {
if(!(mAdapterinstanceofHeaderViewRecyleViewAdapter)) {
mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
@Override
publicvoidsetAdapter(Adapter adapter){
if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {
mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
}else{
mAdapter = adapter;
}
super.setAdapter(mAdapter);
}
}
HeaderViewRecyleViewAdapter類實現(xiàn):
publicclassHeaderViewRecyleViewAdapterextendsRecyclerView.Adapter{
privateArrayList mHeaderViewInfos;
privateArrayList? mFooterViewInfos;
privateRecyclerView.Adapter mAdapter;
publicHeaderViewRecyleViewAdapter(ArrayList headerViewInfos, ArrayList footerViewInfos, RecyclerView.Adapter adapter){
mAdapter = adapter;
if(headerViewInfos ==null) {
mHeaderViewInfos =newArrayList<>();
}else{
mHeaderViewInfos = headerViewInfos;
}
if(footerViewInfos ==null) {
mFooterViewInfos =newArrayList<>();
}else{
mFooterViewInfos = footerViewInfos;
}
}
@NonNull
@Override
publicRecyclerView.ViewHolderonCreateViewHolder(@NonNull ViewGroup parent,intviewType){
//header
if(viewType==RecyclerView.INVALID_TYPE){
returnnewHeaderViewHolder(mHeaderViewInfos.get(0));
}elseif(viewType==RecyclerView.INVALID_TYPE-1){//footer
returnnewHeaderViewHolder(mFooterViewInfos.get(0));
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
returnmAdapter.onCreateViewHolder(parent, viewType);
}
@Override
publicvoidonBindViewHolder(@NonNull RecyclerView.ViewHolder holder,intposition){
//劃分頭部院领、正常部分弛矛、尾部
intnumHeaders = getHeadersCount();
//頭部
if(position < numHeaders) {
return;
}
//adapter bod正常部分
finalintadjPosition = position - numHeaders;
intadapterCount =0;
if(mAdapter !=null) {
adapterCount = mAdapter.getItemCount();
if(adjPosition < adapterCount) {
mAdapter.onBindViewHolder(holder, adjPosition);
return;
}
}
//footer
}
@Override
publicintgetItemCount(){
if(mAdapter !=null) {
returngetFootersCount() + getHeadersCount() + mAdapter.getItemCount();
}else{
returngetFootersCount() + getHeadersCount();
}
}
@Override
publicintgetItemViewType(intposition){
//判斷當(dāng)前條目是什么類型,就渲染什么視圖給什么數(shù)據(jù)
intnumHeaders = getHeadersCount();
//頭部
if(position < numHeaders) {
returnRecyclerView.INVALID_TYPE;
}
//正常條目部分
// Adapter
finalintadjPosition = position - numHeaders;
intadapterCount =0;
if(mAdapter !=null) {
adapterCount = mAdapter.getItemCount();
if(adjPosition < adapterCount) {
returnmAdapter.getItemViewType(adjPosition);
}
}
//footer部分
returnRecyclerView.INVALID_TYPE-1;
}
privatestaticclassHeaderViewHolderextendsRecyclerView.ViewHolder{
publicHeaderViewHolder(View view){
super(view);
}
}
publicintgetHeadersCount(){
returnmHeaderViewInfos.size();
}
publicintgetFootersCount(){
returnmFooterViewInfos.size();
}
}
接下來就是應(yīng)用栅盲,需要使用WrapRecyleView 來代替RecyleView 汪诉,其它都是正常的實現(xiàn)
setContentView(R.layout.activity_rvheader);
recyclerView = (WrapRecyleView) findViewById(R.id.recyclerView);
addHeaderView();
addFooterView();
List list =newArrayList<>();
for(inti =0; i <6; i++) {
list.add("item "+i);
}
MyAdapter adapter =newMyAdapter(list);
recyclerView.setLayoutManager(newLinearLayoutManager(this));
recyclerView.setAdapter(adapter);
效果圖如下:
效果圖如下:
源碼地址:https://github.com/heiyl/recyleview
微信公眾號:
圖注:Android進化之路公眾號