web-dev-qa-db-ja.com

Android 5.0 - RecyclerViewにヘッダ/フッタを追加する

私はRecyclerViewにヘッダーを追加する方法を見つけようとして少し時間をかけましたが、失敗しました。これは私がこれまでに得たものです:

@Override
protected void onCreate(Bundle savedInstanceState)
{
    ...

    layouManager = new LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(layouManager);

    LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    headerPlaceHolder = inflater.inflate(R.layout.view_header_holder_medium, null, false);
    layouManager.addView(headerPlaceHolder, 0);

   ...
}

LayoutManagerRecyclerView項目の処理を処理するオブジェクトのようです。私はaddHeaderView(View view)メソッドを見つけることができなかったので、私はLayoutManageraddView(View view, int position)メソッドを使い、ヘッダーのように振る舞うために最初の位置にヘッダービューを追加することにしました。

Aaandこれは物事がより巧妙になるところです:

Java.lang.NullPointerException: Attempt to read from field 'Android.support.v7.widget.RecyclerView$ViewHolder Android.support.v7.widget.RecyclerView$LayoutParams.mViewHolder' on a null object reference
            at Android.support.v7.widget.RecyclerView.getChildViewHolderInt(RecyclerView.Java:2497)
            at Android.support.v7.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.Java:4807)
            at Android.support.v7.widget.RecyclerView$LayoutManager.addView(RecyclerView.Java:4803)
            at com.mathieumaree.showz.fragments.CategoryFragment.setRecyclerView(CategoryFragment.Java:231)
            at com.mathieumaree.showz.fragments.CategoryFragment.access$200(CategoryFragment.Java:47)
            at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.Java:201)
            at com.mathieumaree.showz.fragments.CategoryFragment$2.success(CategoryFragment.Java:196)
            at retrofit.CallbackRunnable$1.run(CallbackRunnable.Java:41)
            at Android.os.Handler.handleCallback(Handler.Java:739)
            at Android.os.Handler.dispatchMessage(Handler.Java:95)
            at Android.os.Looper.loop(Looper.Java:135)
            at Android.app.ActivityThread.main(ActivityThread.Java:5221)
            at Java.lang.reflect.Method.invoke(Native Method)
            at Java.lang.reflect.Method.invoke(Method.Java:372)
            at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:899)
            at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:694)

Activity作成のさまざまな瞬間にaddView(View view)を呼び出そうとするいくつかのNullPointerExceptionsを取得した後(すべての設定が完了したらビューを追加してみた、アダプタのデータでも)、これが正しい方法かどうかわからないそしてそれはには見えません)。

PS:また、GridLayoutManagerに加えてLinearLayoutManagerを扱うことができる解決策は本当にありがたいです!

109
MathieuMaree

これに関して非常によい記事を見つけました https://plus.google.com/+WillBlaschko/posts/3MFmgPbQuWx

私はRecyclerViewにフッターを追加しなければなりませんでした、そして私はそれが役に立つかもしれないと思ったので、ここで私はコードスニペットを共有しています。全体的な流れをよく理解するために、コード内のコメントを確認してください。

import Android.content.Context;
import Android.support.v7.widget.RecyclerView;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;

import Java.util.ArrayList;

public class RecyclerViewWithFooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int FOOTER_VIEW = 1;
    private ArrayList<String> data; // Take any list that matches your requirement.
    private Context context;

    // Define a constructor
    public RecyclerViewWithFooterAdapter(Context context, ArrayList<String> data) {
        this.context = context;
        this.data = data;
    }

    // Define a ViewHolder for Footer view
    public class FooterViewHolder extends ViewHolder {
        public FooterViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the item
                }
            });
        }
    }

    // Now define the ViewHolder for Normal list item
    public class NormalViewHolder extends ViewHolder {
        public NormalViewHolder(View itemView) {
            super(itemView);

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Do whatever you want on clicking the normal items
                }
            });
        }
    }

    // And now in onCreateViewHolder you have to pass the correct view
    // while populating the list item.

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        View v;

        if (viewType == FOOTER_VIEW) {
            v = LayoutInflater.from(context).inflate(R.layout.list_item_footer, parent, false);
            FooterViewHolder vh = new FooterViewHolder(v);
            return vh;
        }

        v = LayoutInflater.from(context).inflate(R.layout.list_item_normal, parent, false);

        NormalViewHolder vh = new NormalViewHolder(v);

        return vh;
    }

    // Now bind the ViewHolder in onBindViewHolder
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

        try {
            if (holder instanceof NormalViewHolder) {
                NormalViewHolder vh = (NormalViewHolder) holder;

                vh.bindView(position);
            } else if (holder instanceof FooterViewHolder) {
                FooterViewHolder vh = (FooterViewHolder) holder;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Now the critical part. You have return the exact item count of your list
    // I've only one footer. So I returned data.size() + 1
    // If you've multiple headers and footers, you've to return total count
    // like, headers.size() + data.size() + footers.size()

    @Override
    public int getItemCount() {
        if (data == null) {
            return 0;
        }

        if (data.size() == 0) {
            //Return 1 here to show nothing
            return 1;
        }

        // Add extra view to show the footer view
        return data.size() + 1;
    }

    // Now define getItemViewType of your own.

    @Override
    public int getItemViewType(int position) {
        if (position == data.size()) {
            // This is where we'll add footer.
            return FOOTER_VIEW;
        }

        return super.getItemViewType(position);
    }

    // So you're done with adding a footer and its action on onClick.
    // Now set the default ViewHolder for NormalViewHolder

    public class ViewHolder extends RecyclerView.ViewHolder {
        // Define elements of a row here
        public ViewHolder(View itemView) {
            super(itemView);
            // Find view by ID and initialize here
        }

        public void bindView(int position) {
            // bindView() method to implement actions
        }
    }
}

上記のコードスニペットはRecyclerViewにフッターを追加します。あなたは このGitHubリポジトリ をチェックすることでヘッダとフッタの両方の追加の実装をチェックすることができます。

98
Reaz Murshed

私はLollipopでも同じ問題を抱えていて、Recyclerviewアダプターをラップするための2つのアプローチを作成しました。 1つは非常に使いやすいです、しかし私はそれが変化するデータセットでどのように振る舞うかわからない。それはあなたのアダプタをラップするので、あなたは自分自身が正しいアダプタオブジェクトのnotifyDataSetChangedのようなメソッドを呼ぶことを確実にする必要があるからです。

他の人はそのような問題を抱えてはいけません。通常のアダプタでクラスを拡張し、抽象メソッドを実装するだけで準備は完了です。そしてここに彼らは:

要旨

HeaderRecyclerViewAdapterV1

import Android.support.v7.widget.RecyclerView;
import Android.view.ViewGroup;

/**
 * Created by sebnapi on 08.11.14.
 * <p/>
 * This is a Plug-and-Play Approach for adding a Header or Footer to
 * a RecyclerView backed list
 * <p/>
 * Just wrap your regular adapter like this
 * <p/>
 * new HeaderRecyclerViewAdapterV1(new RegularAdapter())
 * <p/>
 * Let RegularAdapter implement HeaderRecyclerView, FooterRecyclerView or both
 * and you are ready to go.
 * <p/>
 * I'm absolutely not sure how this will behave with changes in the dataset.
 * You can always wrap a fresh adapter and make sure to not change the old one or
 * use my other approach.
 * <p/>
 * With the other approach you need to let your Adapter extend HeaderRecyclerViewAdapterV2
 * (and therefore change potentially more code) but possible omit these shortcomings.
 * <p/>
 * TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :)
 */
public class HeaderRecyclerViewAdapterV1 extends RecyclerView.Adapter {
    private static final int TYPE_HEADER = Integer.MIN_VALUE;
    private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1;
    private static final int TYPE_ADAPTEE_OFFSET = 2;

    private final RecyclerView.Adapter mAdaptee;


    public HeaderRecyclerViewAdapterV1(RecyclerView.Adapter adaptee) {
        mAdaptee = adaptee;
    }

    public RecyclerView.Adapter getAdaptee() {
        return mAdaptee;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEADER && mAdaptee instanceof HeaderRecyclerView) {
            return ((HeaderRecyclerView) mAdaptee).onCreateHeaderViewHolder(parent, viewType);
        } else if (viewType == TYPE_FOOTER && mAdaptee instanceof FooterRecyclerView) {
            return ((FooterRecyclerView) mAdaptee).onCreateFooterViewHolder(parent, viewType);
        }
        return mAdaptee.onCreateViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (position == 0 && holder.getItemViewType() == TYPE_HEADER && useHeader()) {
            ((HeaderRecyclerView) mAdaptee).onBindHeaderView(holder, position);
        } else if (position == mAdaptee.getItemCount() && holder.getItemViewType() == TYPE_FOOTER && useFooter()) {
            ((FooterRecyclerView) mAdaptee).onBindFooterView(holder, position);
        } else {
            mAdaptee.onBindViewHolder(holder, position - (useHeader() ? 1 : 0));
        }
    }

    @Override
    public int getItemCount() {
        int itemCount = mAdaptee.getItemCount();
        if (useHeader()) {
            itemCount += 1;
        }
        if (useFooter()) {
            itemCount += 1;
        }
        return itemCount;
    }

    private boolean useHeader() {
        if (mAdaptee instanceof HeaderRecyclerView) {
            return true;
        }
        return false;
    }

    private boolean useFooter() {
        if (mAdaptee instanceof FooterRecyclerView) {
            return true;
        }
        return false;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0 && useHeader()) {
            return TYPE_HEADER;
        }
        if (position == mAdaptee.getItemCount() && useFooter()) {
            return TYPE_FOOTER;
        }
        if (mAdaptee.getItemCount() >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) {
            new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + ".");
        }
        return mAdaptee.getItemViewType(position) + TYPE_ADAPTEE_OFFSET;
    }


    public static interface HeaderRecyclerView {
        public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType);

        public void onBindHeaderView(RecyclerView.ViewHolder holder, int position);
    }

    public static interface FooterRecyclerView {
        public RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType);

        public void onBindFooterView(RecyclerView.ViewHolder holder, int position);
    }

}

HeaderRecyclerViewAdapterV2

import Android.support.v7.widget.RecyclerView;
import Android.view.ViewGroup;

/**
 * Created by sebnapi on 08.11.14.
 * <p/>
 * If you extend this Adapter you are able to add a Header, a Footer or both
 * by a similar ViewHolder pattern as in RecyclerView.
 * <p/>
 * If you want to omit changes to your class hierarchy you can try the Plug-and-Play
 * approach HeaderRecyclerViewAdapterV1.
 * <p/>
 * Don't override (Be careful while overriding)
 * - onCreateViewHolder
 * - onBindViewHolder
 * - getItemCount
 * - getItemViewType
 * <p/>
 * You need to override the abstract methods introduced by this class. This class
 * is not using generics as RecyclerView.Adapter make yourself sure to cast right.
 * <p/>
 * TOTALLY UNTESTED - USE WITH CARE - HAVE FUN :)
 */
public abstract class HeaderRecyclerViewAdapterV2 extends RecyclerView.Adapter {
    private static final int TYPE_HEADER = Integer.MIN_VALUE;
    private static final int TYPE_FOOTER = Integer.MIN_VALUE + 1;
    private static final int TYPE_ADAPTEE_OFFSET = 2;

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEADER) {
            return onCreateHeaderViewHolder(parent, viewType);
        } else if (viewType == TYPE_FOOTER) {
            return onCreateFooterViewHolder(parent, viewType);
        }
        return onCreateBasicItemViewHolder(parent, viewType - TYPE_ADAPTEE_OFFSET);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (position == 0 && holder.getItemViewType() == TYPE_HEADER) {
            onBindHeaderView(holder, position);
        } else if (position == getBasicItemCount() && holder.getItemViewType() == TYPE_FOOTER) {
            onBindFooterView(holder, position);
        } else {
            onBindBasicItemView(holder, position - (useHeader() ? 1 : 0));
        }
    }

    @Override
    public int getItemCount() {
        int itemCount = getBasicItemCount();
        if (useHeader()) {
            itemCount += 1;
        }
        if (useFooter()) {
            itemCount += 1;
        }
        return itemCount;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0 && useHeader()) {
            return TYPE_HEADER;
        }
        if (position == getBasicItemCount() && useFooter()) {
            return TYPE_FOOTER;
        }
        if (getBasicItemType(position) >= Integer.MAX_VALUE - TYPE_ADAPTEE_OFFSET) {
            new IllegalStateException("HeaderRecyclerViewAdapter offsets your BasicItemType by " + TYPE_ADAPTEE_OFFSET + ".");
        }
        return getBasicItemType(position) + TYPE_ADAPTEE_OFFSET;
    }

    public abstract boolean useHeader();

    public abstract RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindHeaderView(RecyclerView.ViewHolder holder, int position);

    public abstract boolean useFooter();

    public abstract RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindFooterView(RecyclerView.ViewHolder holder, int position);

    public abstract RecyclerView.ViewHolder onCreateBasicItemViewHolder(ViewGroup parent, int viewType);

    public abstract void onBindBasicItemView(RecyclerView.ViewHolder holder, int position);

    public abstract int getBasicItemCount();

    /**
     * make sure you don't use [Integer.MAX_VALUE-1, Integer.MAX_VALUE] as BasicItemViewType
     *
     * @param position
     * @return
     */
    public abstract int getBasicItemType(int position);

}

フィードバックとフォークは大歓迎です。私は自分自身でHeaderRecyclerViewAdapterV2を使用し、将来的に変更を展開し、テストし、投稿します。

EDIT:@OvidiuLatcuはい問題がありました。実際、私はposition - (useHeader() ? 1 : 0)によって暗黙のうちにHeaderをオフセットすることをやめ、代わりにそれのためにパブリックメソッドint offsetPosition(int position)を作成しました。 RecyclerviewでOnItemTouchListenerを設定すると、タッチを傍受し、タッチのx、y座標を取得し、それに応じて子ビューを検索しrecyclerView.getChildPosition(...)を呼び出すと、アダプタ内のオフセットされていない位置が常に得られます。これはRecyclerViewコードの欠点です。これを克服する簡単な方法はわかりません。これが私が自分の自身のコードによって必要なときに明示的な位置をオフセットする理由です。

25
seb

解決するのはとても簡単!

ビューを返す前にビューの種類をチェックするたびに、アダプタ内のロジックを別のビューの種類として持つのは嫌です。以下の解決策は余分なチェックを避けます。

LinearLayout(vertical)ヘッダービュー+ recyclerview +フッタービューを追加するだけですAndroid.support.v4.widget.NestedScrollView

これをチェックしてください。

 <Android.support.v4.widget.NestedScrollView
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:orientation="vertical">

       <View
            Android:id="@+id/header"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"/>

        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/list"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            app:layoutManager="LinearLayoutManager"/>

        <View
            Android:id="@+id/footer"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"/>
    </LinearLayout>
</Android.support.v4.widget.NestedScrollView>

スムーズなスクロールのためにこのコード行を追加

RecyclerView v = (RecyclerView) findViewById(...);
v.setNestedScrollingEnabled(false);

これはすべてのRVパフォーマンスを失い、RVはlayout_heightに関係なくすべてのビューホルダーをレイアウトしようとします

Nav引き出しや設定などの小さいサイズのリストに使用することをお勧めします

22
Nishant Shah

試したことはありませんが、アダプタのgetItemCountによって返される整数に単純に1(ヘッダーとフッターの両方が必要な場合は2)を追加します。 i==0の場合は、アダプタ内のgetItemViewTypeをオーバーライドして別の整数を返すことができます。 https://developer.Android.com/reference/Android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)

createViewHolderにはgetItemViewTypeから返された整数が渡され、ヘッダービューに対してビューホルダーを異なる方法で作成または設定できます。 https://developer.Android.com/reference/Android/support/v7/widget/ RecyclerView.Adapter.html#createViewHolder(Android.view.ViewGroup 、int)

bindViewHolderに渡された位置整数から1を引くことを忘れないでください。

10
Ian Newson

このGitHubライブラリを使ってヘッダや/フッターをあなたのRecyclerViewにできるだけ簡単な方法で。

プロジェクトに HFRecyclerView ライブラリを追加する必要があります。またはGradleから入手することもできます。

compile 'com.mikhaellopez:hfrecyclerview:1.0.0'

これはimageの結果です:

Preview

編集:

このライブラリの一番上または一番下に余白を追加するだけの場合は、 SimpleItemDecoration

int offsetPx = 10;
recyclerView.addItemDecoration(new StartOffsetItemDecoration(offsetPx));
recyclerView.addItemDecoration(new EndOffsetItemDecoration(offsetPx));
9
lopez.mikhael

私は自分のアダプタを実装して他のアダプタをラップし、ヘッダビューとフッタビューを追加するメソッドを提供しました。

ここに要旨を作成しました。 HeaderViewRecyclerAdapter.Java

私が望んでいた主な機能は、ListViewに似たインターフェースでしたので、Fragmentのビューを膨らませてRecyclerViewonCreateViewに追加できるようにしたいと思いました。これは、ラップするアダプタを渡すHeaderViewRecyclerAdapterを作成し、膨張したビューを渡すaddHeaderViewおよびaddFooterViewを呼び出すことによって行われます。次に、HeaderViewRecyclerAdapterインスタンスをRecyclerView上のアダプタとして設定します。

追加の要件は、ヘッダーとフッターを保持しながらアダプターを簡単に交換できるようにする必要があるということでした。これらのヘッダーとフッターの複数のインスタンスを持つ複数のアダプターを持つことは望ましくありませんでした。そのため、setAdapterを呼び出して、ヘッダーとフッターを変更せずにラップされたアダプターを変更し、RecyclerViewに変更を通知することができます。

6
darnmason

あなたはこの問題を解決するためにviewtypeを使うことができます、これが私のデモです: https://github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView

  1. いくつかのリサイクラービュー表示モードを定義できます。

    mODE_DATA = 0、MODE_LOADING = 1、MODE_ERROR = 2、MODE_EMPTY = 3、MODE_HEADER_VIEW = 4、MODE_FOOTER_VIEW = 5。

2. getItemViewTypeモデルをオーバーライドします。

 @Override
public int getItemViewType(int position) {
    if (mMode == RecyclerViewMode.MODE_LOADING) {
        return RecyclerViewMode.MODE_LOADING;
    }
    if (mMode == RecyclerViewMode.MODE_ERROR) {
        return RecyclerViewMode.MODE_ERROR;
    }
    if (mMode == RecyclerViewMode.MODE_EMPTY) {
        return RecyclerViewMode.MODE_EMPTY;
    }
    //check what type our position is, based on the assumption that the order is headers > items > footers
    if (position < mHeaders.size()) {
        return RecyclerViewMode.MODE_HEADER_VIEW;
    } else if (position >= mHeaders.size() + mData.size()) {
        return RecyclerViewMode.MODE_FOOTER_VIEW;
    }
    return RecyclerViewMode.MODE_DATA;
}

3. getItemCountメソッドをオーバーライドします。

@Override
public int getItemCount() {
    if (mMode == RecyclerViewMode.MODE_DATA) {
        return mData.size() + mHeaders.size() + mFooters.size();
    } else {
        return 1;
    }
}

4. onCreateViewHolderメソッドをオーバーライドします。 viewTypeでビューホルダーを作成します

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == RecyclerViewMode.MODE_LOADING) {
        RecyclerView.ViewHolder loadingViewHolder = onCreateLoadingViewHolder(parent);
        loadingViewHolder.itemView.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
        );
        return loadingViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_ERROR) {
        RecyclerView.ViewHolder errorViewHolder = onCreateErrorViewHolder(parent);
        errorViewHolder.itemView.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
        );
        errorViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnErrorViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnErrorViewClickListener.onErrorViewClick(v);
                        }
                    }, 200);
                }
            }
        });
        return errorViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_EMPTY) {
        RecyclerView.ViewHolder emptyViewHolder = onCreateEmptyViewHolder(parent);
        emptyViewHolder.itemView.setLayoutParams(
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, parent.getHeight() - mToolBarHeight)
        );
        emptyViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnEmptyViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnEmptyViewClickListener.onEmptyViewClick(v);
                        }
                    }, 200);
                }
            }
        });
        return emptyViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_HEADER_VIEW) {
        RecyclerView.ViewHolder headerViewHolder = onCreateHeaderViewHolder(parent);
        headerViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnHeaderViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnHeaderViewClickListener.onHeaderViewClick(v, v.getTag());
                        }
                    }, 200);
                }
            }
        });
        return headerViewHolder;
    }
    if (viewType == RecyclerViewMode.MODE_FOOTER_VIEW) {
        RecyclerView.ViewHolder footerViewHolder = onCreateFooterViewHolder(parent);
        footerViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                if (null != mOnFooterViewClickListener) {
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mOnFooterViewClickListener.onFooterViewClick(v, v.getTag());
                        }
                    }, 200);
                }
            }
        });
        return footerViewHolder;
    }
    RecyclerView.ViewHolder dataViewHolder = onCreateDataViewHolder(parent);
    dataViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(final View v) {
            if (null != mOnItemClickListener) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mOnItemClickListener.onItemClick(v, v.getTag());
                    }
                }, 200);
            }
        }
    });
    dataViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(final View v) {
            if (null != mOnItemLongClickListener) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mOnItemLongClickListener.onItemLongClick(v, v.getTag());
                    }
                }, 200);
                return true;
            }
            return false;
        }
    });
    return dataViewHolder;
}

5. onBindViewHolderメソッドをオーバーライドします。 viewTypeによるデータのバインド

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (mMode == RecyclerViewMode.MODE_LOADING) {
        onBindLoadingViewHolder(holder, position);
    } else if (mMode == RecyclerViewMode.MODE_ERROR) {
        onBindErrorViewHolder(holder, position);
    } else if (mMode == RecyclerViewMode.MODE_EMPTY) {
        onBindEmptyViewHolder(holder, position);
    } else {
        if (position < mHeaders.size()) {
            if (mHeaders.size() > 0) {
                onBindHeaderViewHolder(holder, position);
            }
        } else if (position >= mHeaders.size() + mData.size()) {
            if (mFooters.size() > 0) {
                onBindFooterViewHolder(holder, position - mHeaders.size() - mData.size());
            }
        } else {
            onBindDataViewHolder(holder, position - mHeaders.size());
        }
    }
}
1
yefeng

@ sebのソリューションに基づいて、任意の数のヘッダーとフッターをサポートするRecyclerView.Adapterのサブクラスを作成しました。

https://Gist.github.com/mheras/0908873267def75dc746

これは解決策のようですが、これもLayoutManagerで管理する必要があると思います。残念ながら、今はそれが必要で、StaggeredGridLayoutManagerを一から実装する時間もありません(さらに拡張することもできません)。

まだテスト中ですが、試してみることもできます。あなたがそれに関して何か問題を見つけたら私に知らせてください。

1
mato

下の画像のように、ライブラリ SectionedRecyclerViewAdapter を使用して、アイテムをセクションにグループ化し、各セクションにヘッダーを追加できます。

enter image description here

まずセクションクラスを作成します。

class MySection extends StatelessSection {

    String title;
    List<String> list;

    public MySection(String title, List<String> list) {
        // call constructor with layout resources for this Section header, footer and items 
        super(R.layout.section_header, R.layout.section_item);

        this.title = title;
        this.list = list;
    }

    @Override
    public int getContentItemsTotal() {
        return list.size(); // number of items of this section
    }

    @Override
    public RecyclerView.ViewHolder getItemViewHolder(View view) {
        // return a custom instance of ViewHolder for the items of this section
        return new MyItemViewHolder(view);
    }

    @Override
    public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyItemViewHolder itemHolder = (MyItemViewHolder) holder;

        // bind your view here
        itemHolder.tvItem.setText(list.get(position));
    }

    @Override
    public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
        return new SimpleHeaderViewHolder(view);
    }

    @Override
    public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
        MyHeaderViewHolder headerHolder = (MyHeaderViewHolder) holder;

        // bind your header view here
        headerHolder.tvItem.setText(title);
    }
}

次に、自分のセクションを使ってRecyclerViewを設定し、GridLayoutManagerを使ってヘッダーのSpanSizeを変更します。

// Create an instance of SectionedRecyclerViewAdapter 
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();

// Create your sections with the list of data
MySection section1 = new MySection("My Section 1 title", dataList1);
MySection section2 = new MySection("My Section 2 title", dataList2);

// Add your Sections to the adapter
sectionAdapter.addSection(section1);
sectionAdapter.addSection(section2);

// Set up a GridLayoutManager to change the SpanSize of the header
GridLayoutManager glm = new GridLayoutManager(getContext(), 2);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int position) {
        switch(sectionAdapter.getSectionItemViewType(position)) {
            case SectionedRecyclerViewAdapter.VIEW_TYPE_HEADER:
                return 2;
            default:
                return 1;
        }
    }
});

// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(glm);
recyclerView.setAdapter(sectionAdapter);
0
Gustavo

遅くなったことは知っていますが、つい最近になってそのような "addHeader"をアダプタに実装できました。私のFlexibleAdapterプロジェクトでは、Sectionable項目でsetHeaderを呼び出し、次にshowAllHeadersを呼び出すことができます。ヘッダーが1つだけ必要な場合は、最初の項目にヘッダーがあります。このアイテムを削除すると、ヘッダーは自動的に次のアイテムにリンクされます。

残念ながらフッターはカバーされていません(まだ)。

FlexibleAdapterを使用すると、ヘッダー/セクションを作成する以上のことを実行できます。あなたは本当に見てみるべきです: https://github.com/davideas/F flexibleAdapter

0
Davideas

私はちょうどそれらすべてのHeaderRecyclerViewAdapter実装に代わるものを追加するでしょう。 CompoundAdapter:

https://github.com/negusoft/CompoundAdapter-Android

AdaptersからAdapterGroupを作成できるので、より柔軟なアプローチです。ヘッダの例では、ヘッダ用の項目を1つ含むアダプタとともに、アダプタをそのまま使用します。

AdapterGroup adapterGroup = new AdapterGroup();
adapterGroup.addAdapter(SingleAdapter.create(R.layout.header));
adapterGroup.addAdapter(new MyAdapter(...));

recyclerView.setAdapter(adapterGroup);

それはかなり単純で読みやすいです。同じ原理を使って、より複雑なアダプタを簡単に実装できます。

0
blurkidi