編輯:關於Android編程
對於ListView
有自帶的方法添加headerView及footerView,但是RecycleView
僅僅只是維護緩存的View,本身並不處理內容顯示,都交給了RecycleView.Adapter
處理,所以如果想要讓RecycleView
也可以添加headerView和footerView的話,只有兩種方式
RecycleView
是通過Adapter的方式去解決這個問題,將headerView及footerView轉換成內部數據的形式顯示出來.
針對這個問題,這裡使用第二種方式,擴展了Adapter,通過裝飾者模式,只需要將自己的Adatper包括進外層的Adapter中,即可直接添加headerView及footerView,同時不會對原有的數據造成任何影響.
這裡需要注意的是,此處的ExtroViewWrapperAdaper
包裝類是通用的,但是更針對於HeaderRecycleAdatper
,包括實現了HeaderRecycleAdapter
相關的一些接口用於擴展.
對於普通的Adapter,也是可以直接使用.
由於通過Adapter來完成頭部與尾部的功能添加(這樣做的好處是在任何的RecycleView
上都是可以使用的),所以實現方式上跟普通的多類型item的Adapter的實現方式是一致的,這裡不再強調.主要說明如何進行裝飾.
對於Adapter,由於每個Adapter完成的功能都不同,所以我們只能不能知道Adapter是具體如何完成的,那麼我們需要盡量將原始的數據與裝飾的數據(頭部/尾部)分離開來.ExtraViewWrapperAdapter
主要就是要處理這個.
首先是保存原有Adapter的引用.對於頭部和尾部一般是只添加一個,但這裡考慮到可能會添加多個,所以是允許添加多個的.
對於添加多個的情況,每一個headerView都可能不同,這時就需要提供每一個headerView的一個viewTag(標志),這個標志是唯一的,用於分辨不同的headerView的類型以顯示出來.
對此使用一個內部類來管理添加的headerView或者footerView.
/**
* 頭部/尾部添加額外View的緩存處理類
*/
public static class HeaderFooterViewCache {
private List> mViewCacheMap;
private Map mIndexMap;
public HeaderFooterViewCache() {
mViewCacheMap = new LinkedList>();
mIndexMap = new ArrayMap();
}
//返回當前保存的View的個數
public int size();
//根據位置獲取對應的標簽
public int getViewViewTag(int position);
//根據標簽獲取對應的view
public View getView(int viewTag);
//檢測是否已經存在某個標簽
public boolean isContainsView(int viewTag);
//添加新view及其唯一標簽,當該標簽已經存在某個view時,將替換該view,返回被替換的view,或者是null;若view為null,返回null,添加失敗
public View addView(int viewTag, View view);
//移除某個標簽對應的view
public boolean removeView(int viewTag);
//清除所有的view
public void clearAllView();
//獲取view的標簽列表,標簽應該是唯一的
public Set getViewTags();
}
這裡僅給出部分方法簽名及其作用說明.具體實現暫不給出.這裡緩存時使用兩個不同的數據結構,一個是List,一個是Map,原因是List用來保存添加的view的順序,Map是用於根據標簽匹配並保存添加的view.
Adapter是自己加載layout並創建view的,但是headerView與footerView是不同的,是直接添加的view並不通過adapter加載與綁定數據.所以在這裡使用的ViewHolder
也只是一個臨時保存的容器而已.
在onCreateViewHolder()
中創建headerView與footerView的holder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ExtraViewHolder(mHeaderCache.getView(viewType));
}
ExtraViewHolder
類的實現僅僅是繼承RecycleView.ViewHolder
而已,並沒有任何其它的操作.這裡只是為了配合Adapter的實現方式.
headerView與footerView的創建完全由外部處理,Adapter並不作任何處理(僅僅是緩存並顯示而已),view的獲取是通過緩存的HeaderFooterViewCache
類通過唯一的viewTag(標簽)進行獲取的.
由以上可知,headerView與FooterView是被HeaderFooterViewCache
緩存的,並且使用唯一的viewTag進行標識.
使用Map的原因是標簽必須是唯一的,一個標簽也只能用於一個view,這樣在Adapter加載view時才不會導致有關viewType問題.
以下為Adapter需要實現的一些方法.
//根據位置獲取view的類型
public int getItemViewType(int position){
//這裡僅給出headerView位置的代碼,其它位置代碼忽略
return mHeaderCache.getViewViewTag(position);
}
//根據view的類型加載view
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
//這裡僅給出headerView的holder創建
return new ExtraViewHolder(mHeaderCache.getView(viewType));
}
Map中的唯一的viewTag標簽就是用於此處的viewType,由於加載holder是通過viewType來創建的,所以不同的viewTag代表了不同的viewType,也是代表了不同的view.
通過這種方式處理的原因是,headerView與footerView是在創建ExtraViewHolder
時需要使用的,而創建一個holder只會在onCreateViewHolder()
中進行創建.此時參數來源僅有兩個:
這兩個參數顯然僅有第二個參數可以使用.由於headerView和FooterView理論上也不會有很多,因此可以通過指定唯一的viewTag匹配對應的view,同時將viewTag傳遞到onCreateViewHolder()
就可以獲取到該tag對應的view了.
對此,在Adapter的getItemViewType()
方法中返回的viewType實際上也就是view所對應的viewTag.這樣在一定程度上可以減少很多的計算與匹配工作.
RecycleView
很重要的一個特點是根據position進行顯示item.由於添加了headerView與FooterView,所以原始Adapter中position相對於原來的位置必定會改變.
這裡需要計算headerView與FooterView的數量.關於這部分在前面給出的緩存類HeaderFooterViewCache
中已經有相關的方法了,所以可以通過該方法直接獲取其對應的數量.
private int getHeaderViewCount() {
return mHeaderCache.size();
}
這裡需要注意,headerView是通過mHeaderCache緩存管理的,footerView是通過mFooterCache緩存管理的,並不是同一個類管理兩種view.
得到headerView或者footerView的數量後,就可以很方便地計算了.headerView必定在InnerAdapter的item之前,所以position對應的前面都是headerView.
//headerView的position
int headerPosition=position;
//innerAdapter中item的position,除去header部分
int innerPosition=position-headerViewCount;
//footerView的position,除去header及innerAdapter的item數量
int footerPosition=position-headerViewCount-innerAdapter.getItemCount();
ExtraViewWrapperAdapter
提供了獲取內部adapter的位置的方法,該方法就是基於以上的方式進行計算得到的.
//這裡的wrapAdapterPosition指 ExtraViewWrapperAdapter 對應的position
public int getInnerAdapterPosition(int wrapAdapterPosition) {
int headerViewCount = getHeaderViewCount();
int innerPosition = wrapAdapterPosition - headerViewCount;
if (mInnerAdapter != null && innerPosition < mInnerAdapter.getItemCount()) {
return innerPosition;
} else {
return -1;
}
}
通過以上計算頭部尾部的位置,我們是可以得到他們的判斷方式的.因為判斷頭部尾部是給定一個position,判斷其是否為頭部或者尾部.
//當position在前面的位置且位於headerViewCount的數量范圍內,則說明當前位置為頭部
private boolean isHeaderView(int position) {
//計算頭部view結束的位置
int headerEndPosition = getHeaderViewCount();
return position >= 0 && position < headerEndPosition;
}
尾部的計算方式也是相同的.都是計算當前位置是否在指定的view類型范圍內即可.
HeaderRecycleAdapter
的接口相關由於ExtraViewWrapperAdapter
是為了兼容HeaderRecycleAdapter
,所以實現了與其相同的一些接口,包括:
由於innerAdapter有自己的接口實現,wrapperAdapter並不能代替其實現的功能,所以只能是通過保存對應的接口實現,並在實現這些接口時(與position有關的方法中)屏蔽headerView及footerView的部分,有關innerAdapter的item部分由其對應的接口自行處理.
大致類似以下的處理方式,此處與兩個接口有關,需要了解的請查看HeaderRecycleAdapter
及StickHeaderItemDecoration
兩個類的分析文章.
//如 IStickerHeaderDecoration 接口中,判斷當前position的item是否有固定頭部時
@Override
public boolean hasStickHeader(int position) {
//當前位置的item為headerView或者footerView,都不存在固定頭部
if (isHeaderView(position) || isFooterView(position)) {
return false;
} else if (mIStickHeaderDecoration != null) {
//若不是,則說明當前位置應該是innerAdapter中的item,計算innerAdapter中對應的位置並回調其相關的接口處理
return mIStickHeaderDecoration.hasStickHeader(getInnerAdapterPosition(position));
} else {
return false;
}
}
這裡涉及到了原innerAdapter相關的一些接口問題.由於HeaderRecycleAdapter
是已經實現了相關的接口,所以可以在保存adapter時保存其實現接口的引用.
public void setInnerAdapter(@NonNull RecyclerView.Adapter innerAdapter) {
mInnerAdapter = innerAdapter;
//當前adapter實現了 IStickerHeaderDecoration接口,則保存其接口引用
if (mInnerAdapter != null && mInnerAdapter instanceof StickHeaderItemDecoration.IStickerHeaderDecoration) {
mIStickHeaderDecoration = (StickHeaderItemDecoration.IStickerHeaderDecoration) mInnerAdapter;
}
//當前adapter實現了 ISpanSizeHandler接口,則保存其接口引用
if (mInnerAdapter != null && mInnerAdapter instanceof HeaderSpanSizeLookup.ISpanSizeHandler) {
mISpanSizeLookup = (HeaderSpanSizeLookup.ISpanSizeHandler) mInnerAdapter;
}
}
通過直接判斷adatper是否實現了對應接口直接保存引用,可以更准確地綁定innerAdapter及其相關接口的實現.同時,也存在相關的接口直接設置方法手動綁定當前innerAdapter的實現接口.
ExtraViewWrapperAdapter
大致實現的原理如上,主要是為了增加headerView及footerView的添加功能,同時又能兼容原有innerAdapter的功能(主要指HeaderRecycleAdapter
).
同時提供了添加多個headerView及footerView的功能,並額外添加了refreshView(刷新View)及loadView(加載View)分別在頂部及底部顯示,默認為null不存在.
//設置刷新顯示的view
mExtraAdapter.setRefreshingHeaderView(yourRefreshView);
//切換刷新/加載/header/footer顯示的狀態
ExtraViewWrapAdapter.setRefreshingViewStatus(true,true,rv);
使用方式很簡單,需要添加headerView或者footerView時,直接進行添加,然後設置原始數據innerAdatper,如果是實現了ISpanSizeHandler
及IStcikerHeaderDecoration
接口的adapter則不需要作額外處理,否則需要考慮一下是否要設置相關的接口實現.
//設置innearAdapter
mExtraAdapter = new ExtraViewWrapAdapter(mNormalAdapter);
//添加headerView
mExtraAdapter.addHeaderView(R.id.header_view, yourView);
//使用extraViewWrapperAdapter代碼原有的adapter作為recycleView的數據綁定
rv.setAdapter(mExtraAdapter);
https://github.com/CrazyTaro/RecycleViewAdapter
與之前的RecycleView相關的都置於同一個項目中
連接網絡過程STA連接AP的過程可以參考該文章,http://support.huawei.com/ecommunity/bbs/10232527.htmlSTA需要認證
背景新項目的一個界面需要用到九宮格界面,每個Item包含一張圖片,下面是對應的文字描述,給每個Item設置點擊監聽器,當點擊時跳轉到相應的界面。於是想到使用Android
之前開發Android都是使用的eclipse,最近由於和外國朋友Timothy一起開發一款應用,他是從WP平台剛切換使用Android的,使用的開發環境時Android
本文實例講述了Android編程之TabWidget選項卡用法。分享給大家供大家參考,具體如下:1 概覽TabWidget與TabHost。tab組件一般包括TabHos