RecyclerView:實現帶header的grid

jopen 9年前發布 | 231K 次閱讀 Android開發 移動開發 RecyclerView

原文:RecyclerView: Grid with header 

GridView和ListView有許多的相似之處,不過也有一個顯著的不同:沒有header和footer。現在它們兩者都可以用RecyclerView實現,我想看看如何在grid上添加header。

GridLayoutManager

我用GridLayoutManager創建了一個spanCount為2的RecylcerView。

注:spanCount即列數。這里GridLayoutManager的第二個參數就是spanCount。

RecyclerView recyclerView = (RecyclerView) findViewById(
    R.id.recycler_view);
recyclerView.addItemDecoration(new MarginDecoration(this));
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
recyclerView.setAdapter(new NumberedAdapter(30));

NumberedAdapter 以字符串的形式顯示了每個item的position,在點擊的時候顯示一個toast。


RecyclerView:實現帶header的grid

GridLayoutActivity.java

可變的span size

在上面的基本設置中,我們的spanCount為2,每個item的span size為1,因此一個header需要的span size則為2。在我嘗試著添加header之前,我想先看看如何設置 span size。其實很簡單。

注:span size表示一個item的跨度,跨度了多少個span。

GridLayoutManager manager = new GridLayoutManager(this, 3);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
  @Override
  public int getSpanSize(int position) {
    return (3 - position % 3);
  }
});
recyclerView.setLayoutManager(manager);

setSpanSizeLookup可以讓你根據position來設置 span size,上面代碼中的公式所得到的 span size依次是3, 2, 1, 3, 2, 1...

RecyclerView:實現帶header的grid

GridLayoutVariableSpanSizeActivity.java

頭部(header)

現在讓我們來添加一個header!我們需要一個提供兩種view類型的adapter,一個為header一個為普通的item。可以看看HeaderNumberedAdapter,它在構造函數中把一個view作為header,然后把它存在一個成員變量中。

package com.sqisland.android.recyclerview;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class HeaderNumberedAdapter extends RecyclerView.Adapter<TextViewHolder> {
  private static final int ITEM_VIEW_TYPE_HEADER = 0;
  private static final int ITEM_VIEW_TYPE_ITEM = 1;

  private final View header;
  private final List<String> labels;

  public HeaderNumberedAdapter(View header, int count) {
    if (header == null) {
      throw new IllegalArgumentException("header may not be null");
    }
    this.header = header;
    this.labels = new ArrayList<String>(count);
    for (int i = 0; i < count; ++i) {
      labels.add(String.valueOf(i));
    }
  }

  public boolean isHeader(int position) {
    return position == 0;
  }

  @Override
  public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == ITEM_VIEW_TYPE_HEADER) {
      return new TextViewHolder(header);
    }
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
    return new TextViewHolder(view);
  }

  @Override
  public void onBindViewHolder(final TextViewHolder holder, final int position) {
    if (isHeader(position)) {
      return;
    }
    final String label = labels.get(position - 1);  // Subtract 1 for header
    holder.textView.setText(label);
    holder.textView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        Toast.makeText(
            holder.textView.getContext(), label, Toast.LENGTH_SHORT).show();
      }
    });
  }

  @Override
  public int getItemViewType(int position) {
    return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_ITEM;
  }

  @Override
  public int getItemCount() {
    return labels.size() + 1;
  }
}

其中TextViewHolder的代碼為:

package com.sqisland.android.recyclerview;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;

public class TextViewHolder extends RecyclerView.ViewHolder {
  public TextView textView;
  public TextViewHolder(View itemView) {
    super(itemView);
    textView = (TextView) itemView.findViewById(R.id.text);
  }
}

在RecyclerView新建一個view的時候,如果處于header的位置,我們用view holder來封裝這個header。onBindViewHolder中不必對header做任何事情,因為它的邏輯是在activity中處理的。


回到activity。我們需要用一個header來初始化HeaderNumberedAdapter,同時重寫setSpanSizeLookup,讓header橫跨所有列。

final GridLayoutManager manager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(manager);

View header = LayoutInflater.from(this).inflate(
    R.layout.header, recyclerView, false);
header.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    Toast.makeText(v.getContext(), R.string.grid_layout_header, 
        Toast.LENGTH_SHORT).show();
  }
});
final HeaderNumberedAdapter adapter 
    = new HeaderNumberedAdapter(header, 30);
recyclerView.setAdapter(adapter);

manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
  @Override
  public int getSpanSize(int position) {
    return adapter.isHeader(position) ? manager.getSpanCount() : 1;
  }
});

我們inflate header,定義它的點擊事件,使用它去構造adapter。然后再setSpanSizeLookup中,我們在header所處的位置返回和span count (列數)相等的 span size。

RecyclerView:實現帶header的grid

總結

為了用RecyclerView創建一個帶header的grid:

  1. 定義一個具有兩種view類型的adapter,一個為header一個為普通item。

  2. nflate一個header,把它傳遞給adapter。

  3. 重寫GridLayoutManager中的setSpanSizeLookup,在header所處的位置返回和span count (列數)相等的 span size。

源碼: https://github.com/chiuki/android-recyclerview

ps:在我的g+上有人評論說如果你不需要RecyclerView的功能,比如animation,reordering,staggering等,你也可以從AOSP中拷貝HeaderGridView.java。 

來自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0722/3214.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!