大家都知道,RecycleView預設隻能通過setLayoutManager()方法指定一種布局結構,那麼像支付寶首頁這樣複雜的多布局情況如何處理呢?在ListView中,我們也遇到過這種情況,是通過getItemViewType結合其他方法,通過判斷類型來加載不同的布局。在RecycleView中也同樣使用,今天我們主要介紹RecycleView多布局實作,其中布局嵌套了GridView,有些坑需要注意,避免陷入。
一、效果圖
廢話不多說,獻上效果圖。
從圖中可以看出,一共有三種布局樣式。第一種:頭布局,包含各種類型的新聞頻道。第二種布局:普通的新聞。第三種布局:多圖圖集新聞。
二、多布局實作的思路
步驟一、getItemViewType()方法判斷不同的布局,傳回判斷結果。
步驟二、建立不同的ViewHolder類,針對不同的布局類型,進行對應的布局内控件的初始化。
步驟三、onCreateViewHolder()方法根據第一步的判斷結果,得到viewType,根據viewType值,執行個體化不同類型的ViewHolder對象。
步驟四、onBindViewHolder()方法判斷holder所屬類型,進行相對應類型的布局内控件内容的指派。
以上就是實作多布局的思路,很簡單(可能描述不到位,不易了解),下面我們以代碼的形式更直覺的來了解具體步驟。
三、RecycleView多布局實作代碼
首先定義三種類型常量
//新聞模式
private static final int TYPE_SINGLE = 0;
//圖集模式
private static final int TYPE_MULTI = 1;
//頭布局模式
private static final int TYPE_HEADER = 2;
getItemViewType()方法判斷不同的布局,傳回判斷結果
@Override
public int getItemViewType(int position) {
//所有的新聞詳情資料,在第一個位置自己手動加入了一個假的新聞資料,把它的title設定為“頭布局”
NewsInfo info = list.get(position);
//識别出“頭布局”,則是第一個資料,把它歸于頭布局類型
if(info.getTitle().endsWith("頭布局")){
return TYPE_HEADER;
}
//有新聞具體内容,是普通新聞
if (!TextUtils.isEmpty(info.getDigest()))
{
return TYPE_SINGLE;
}
//其他情況,是圖集新聞
return TYPE_MULTI;
}
建立不同的ViewHolder類,針對不同的布局類型,進行對應的布局内控件的初始化。
省略了具體的初始化操作
public class SingleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
.......
.......
public SingleViewHolder(View itemView) {
super(itemView);
.......
.......
}
@Override
public void onClick(View view) {
Toast.makeText(context,"點選了",Toast.LENGTH_SHORT).show();
}
}
public class MultiViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
.......
.......
public MultiViewHolder(View itemView) {
super(itemView);
.......
.......
}
@Override
public void onClick(View view) {
Toast.makeText(context,"點選了",Toast.LENGTH_SHORT).show();
}
}
public class HeaderViewHolder extends RecyclerView.ViewHolder{
.......
.......
public HeaderViewHolder(View itemView) {
super(itemView);
.......
.......
}
}
onCreateViewHolder()方法根據第一步的判斷結果,得到viewType,根據viewType值,執行個體化不同類型的ViewHolder對象。
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_SINGLE:
return new SingleViewHolder(inflater.inflate(R.layout.item_news, parent, false));
case TYPE_MULTI:
return new MultiViewHolder(inflater.inflate(R.layout.item_news_photo, parent, false));
case TYPE_HEADER:
return new HeaderViewHolder(inflater.inflate(R.layout.item_news_header,parent,false));
}
return null;
}
onBindViewHolder()方法判斷holder所屬類型,進行相對應類型的布局内控件内容的指派。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//設定不同的布局内控件的值
if (holder instanceof SingleViewHolder) {
setNormalItemValues((SingleViewHolder) holder,position);
}else if(holder instanceof MultiViewHolder){
setMultiItemValues((MultiViewHolder) holder,position);
}else{
setHeaderItemValues((HeaderViewHolder) holder);
}
}
針對不同類型,設定布局内控件的值的具體内容就不介紹了,比較繁多,下面有源碼連結。
由于本例中頭布局是個GridView,需要注意的一點是RecycleView中嵌套GridView會出現隻顯示一行的問題。下面給出具體解決方法:
在setHeaderItemValues((HeaderViewHolder) holder);中,主動測量出GridView的高度,并設定給布局。本例中預設GridView是兩行。
//解決GridView隻顯示一行的原因 主動設定GridView的高度
ViewGroup.LayoutParams params = holder.mGridView.getLayoutParams();
View view = mAdapter.getView(0, null, holder.mGridView);
view.measure(0,0);
int height =view.getMeasuredHeight();
int totalHeight = holder.mGridView.getVerticalSpacing() * 2 + height * 2;
params.height = totalHeight;
holder.mGridView.setLayoutParams(params);
源碼位址比較亂,直接看NewsFragment相關内容即可。