Java Android 5.0 - 将页眉/页脚添加到 RecyclerView
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26448717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android 5.0 - Add header/footer to a RecyclerView
提问by MathieuMaree
I spent a moment trying to figure out a way to add a header to a RecyclerView
, unsuccessfully.
我花了一些时间试图找出一种向 a 添加标题的方法RecyclerView
,但没有成功。
This is what I got so far:
这是我到目前为止得到的:
@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);
...
}
The LayoutManager
seems to be the object handling the disposition of the RecyclerView
items. As I couldn't find any addHeaderView(View view)
method, I decided to go with the LayoutManager
's addView(View view, int position)
method and to add my header view in the first position to act as a header.
在LayoutManager
似乎是处理的配置对象RecyclerView
的项目。由于找不到任何addHeaderView(View view)
方法,我决定使用LayoutManager
'saddView(View view, int position)
方法并将我的标题视图添加到第一个位置以充当标题。
Aaand this is where things get uglier:
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.access0(CategoryFragment.java:47)
at com.mathieumaree.showz.fragments.CategoryFragment.success(CategoryFragment.java:201)
at com.mathieumaree.showz.fragments.CategoryFragment.success(CategoryFragment.java:196)
at retrofit.CallbackRunnable.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)
After getting several NullPointerExceptions
trying to call the addView(View view)
at different moments of the Activity creation (also tried adding the view once everything is set up, even the Adapter's data), I realized I have no idea if this is the right way to do it (and it doesn't look to be).
在多次NullPointerExceptions
尝试调用addView(View view)
Activity 创建的不同时刻(还尝试在设置完所有内容后添加视图,甚至是适配器的数据)后,我意识到我不知道这是否是正确的方法(并且它看起来不像)。
PS: Also, a solution that could handle the GridLayoutManager
in addition to the LinearLayoutManager
would be really appreciated!
PS:另外,一个可以处理GridLayoutManager
除了的解决方案LinearLayoutManager
将不胜感激!
采纳答案by Reaz Murshed
I had to add a footer to my RecyclerView
and here I'm sharing my code snippet as I thought it might be useful. Please check the comments inside the code for better understanding of the overall flow.
我不得不向我的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
}
}
}
The above code snippet adds a footer to the RecyclerView
. You can check this GitHub repositoryfor checking the implementation of adding both header and a footer.
上面的代码片段向RecyclerView
. 您可以查看此 GitHub 存储库以检查添加页眉和页脚的实现。
回答by Ian Newson
I haven't tried this, but I would simply add 1 (or 2, if you want both a header and footer) to the integer returned by getItemCount in your adapter. You can then override getItemViewType
in your adapter to return a different integer when i==0
: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)
我还没有尝试过这个,但我会简单地将 1(或 2,如果你想要页眉和页脚)添加到适配器中 getItemCount 返回的整数。然后getItemViewType
,您可以在您的适配器中覆盖以在以下情况下返回不同的整数i==0
:https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#getItemViewType(int)
createViewHolder
is then passed the integer you returned from getItemViewType
, allowing you to create or configure the view holder differently for the header view: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#createViewHolder(android.view.ViewGroup, int)
createViewHolder
然后传递您从 返回的整数getItemViewType
,允许您为标题视图创建或配置不同的视图持有者:https: //developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html# createViewHolder(android.view.ViewGroup, int)
Don't forget to subtract one from the position integer passed to bindViewHolder
.
不要忘记从传递给 的位置整数中减去一bindViewHolder
。
回答by seb
I had the same problem on Lollipop and created two approaches to wrap the Recyclerview
adapter. One is pretty easy to use, but I'm not sure how it will behave with a changing dataset. Because it wraps your adapter and you need to make yourself sure to call methods like notifyDataSetChanged
on the right adapter-object.
我在 Lollipop 上遇到了同样的问题,并创建了两种包装Recyclerview
适配器的方法。一个很容易使用,但我不确定它会如何处理不断变化的数据集。因为它包装了你的适配器,你需要确保自己调用notifyDataSetChanged
正确的适配器对象上的方法。
The other shouldn't have such problems. Just let your regular adapter extend the class, implement the abstract methods and you should be ready. And here they are:
另一个应该不会有这样的问题。只需让您的常规适配器扩展类,实现抽象方法,您就应该准备好了。他们在这里:
gists
要点
- HeaderRecyclerViewAdapterV1.javausage
new HeaderRecyclerViewAdapterV1(new RegularAdapter());
- HeaderRecyclerViewAdapterV2.javausage
RegularAdapter extends HeaderRecyclerViewAdapterV2
- HeaderRecyclerViewAdapterV1.java用法
new HeaderRecyclerViewAdapterV1(new RegularAdapter());
- HeaderRecyclerViewAdapterV2.java用法
RegularAdapter extends HeaderRecyclerViewAdapterV2
HeaderRecyclerViewAdapterV1
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
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);
}
Feedback and forks appreciated. I will use HeaderRecyclerViewAdapterV2
by my self and evolve, test and post the changes in the future.
反馈和分叉表示赞赏。我会自己使用HeaderRecyclerViewAdapterV2
并在未来发展,测试和发布更改。
EDIT: @OvidiuLatcu Yes I had some problems. Actually I stopped offsetting the Header implicitly by position - (useHeader() ? 1 : 0)
and instead created a public method int offsetPosition(int position)
for it. Because if you set an OnItemTouchListener
on Recyclerview, you can intercept the touch, get the x,y coordinates of the touch, find the according child view and thencall recyclerView.getChildPosition(...)
and you will always get the non-offsetted position in the adapter! This is a shortcomming in the RecyclerView Code, I don't see an easy method to overcome this. This is why I now offset the positions explicit when I need to by my owncode.
编辑:@OvidiuLatcu 是的,我遇到了一些问题。实际上,我停止了隐式偏移 Header position - (useHeader() ? 1 : 0)
,而是int offsetPosition(int position)
为它创建了一个公共方法。因为如果你OnItemTouchListener
在 Recyclerview 上设置一个,你可以拦截触摸,获取触摸的 x,y 坐标,找到相应的子视图然后调用recyclerView.getChildPosition(...)
,你将始终获得适配器中的非偏移位置!这是 RecyclerView 代码中的一个缺点,我没有看到克服这个问题的简单方法。这就是为什么我现在需要通过自己的代码来明确抵消位置的原因。
回答by darnmason
I ended up implementing my own adapter to wrap any other adapter and provide methods to add header and footer views.
我最终实现了自己的适配器来包装任何其他适配器并提供添加页眉和页脚视图的方法。
Created a gist here: HeaderViewRecyclerAdapter.java
在这里创建了一个要点:HeaderViewRecyclerAdapter.java
The main feature I wanted was a similar interface to a ListView, so I wanted to be able to inflate the views in my Fragment and add them to the RecyclerView
in onCreateView
. This is done by creating a HeaderViewRecyclerAdapter
passing the adapter to be wrapped, and calling addHeaderView
and addFooterView
passing your inflated views. Then set the HeaderViewRecyclerAdapter
instance as the adapter on the RecyclerView
.
我想要的主要功能是与 ListView 类似的界面,因此我希望能够扩充 Fragment 中的视图并将它们添加到RecyclerView
in onCreateView
. 这是通过创建HeaderViewRecyclerAdapter
传递要包装的适配器,并调用addHeaderView
和addFooterView
传递膨胀的视图来完成的。然后将HeaderViewRecyclerAdapter
实例设置为RecyclerView
.
An extra requirement was that I needed to be able to easily swap out adapters while keeping the headers and footers, I didn't want to have multiple adapters with multiple instances of these headers and footers. So you can call setAdapter
to change the wrapped adapter leaving the headers and footers intact, with the RecyclerView
being notified of the change.
一个额外的要求是我需要能够在保留页眉和页脚的同时轻松更换适配器,我不希望多个适配器具有这些页眉和页脚的多个实例。因此,您可以调用setAdapter
更改包装的适配器,保持页眉和页脚不变,并RecyclerView
收到更改通知。
回答by mato
Based on @seb's solution, I created a subclass of RecyclerView.Adapter that supports an arbitrary number of headers and footers.
基于@seb 的解决方案,我创建了一个 RecyclerView.Adapter 的子类,它支持任意数量的页眉和页脚。
https://gist.github.com/mheras/0908873267def75dc746
https://gist.github.com/mheras/0908873267def75dc746
Although it seems to be a solution, I also think this thing should be managed by the LayoutManager. Unfortunately, I need it now and I don't have time to implement a StaggeredGridLayoutManager from scratch (nor even extend from it).
虽然看起来是个解决方案,但我也觉得这个东西应该由LayoutManager来管理。不幸的是,我现在需要它,而且我没有时间从头开始实现 StaggeredGridLayoutManager(甚至没有扩展它)。
I'm still testing it, but you can try it out if you want. Please let me know if you find any issues with it.
我仍在测试它,但如果您愿意,可以尝试一下。如果您发现任何问题,请告诉我。
回答by yefeng
You can use viewtype to solve this problem, here is my demo: https://github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView
你可以使用viewtype来解决这个问题,这里是我的演示:https: //github.com/yefengfreedom/RecyclerViewWithHeaderFooterLoadingEmptyViewErrorView
you can define some recycler view display mode:
public static final int MODE_DATA = 0, MODE_LOADING = 1, MODE_ERROR = 2, MODE_EMPTY = 3, MODE_HEADER_VIEW = 4, MODE_FOOTER_VIEW = 5;
您可以定义一些回收站视图显示模式:
public static final int MODE_DATA = 0, MODE_LOADING = 1, MODE_ERROR = 2, MODE_EMPTY = 3, MODE_HEADER_VIEW = 4, MODE_FOOTER_VIEW = 5;
2.override the getItemViewType mothod
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.override the getItemCount method
3.覆盖getItemCount方法
@Override
public int getItemCount() {
if (mMode == RecyclerViewMode.MODE_DATA) {
return mData.size() + mHeaders.size() + mFooters.size();
} else {
return 1;
}
}
4.override the onCreateViewHolder method. create view holder by viewType
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.Override the onBindViewHolder method. bind data by viewType
5.覆盖 onBindViewHolder 方法。按视图类型绑定数据
@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());
}
}
}
回答by lopez.mikhael
You can used this GitHublibrary allowing to add Headerand/or Footerin your RecyclerViewin the simplest way possible.
您可以使用这个GitHub库,以最简单的方式在RecyclerView中添加页眉和/或页脚。
You need to add HFRecyclerViewlibrary in your project or you can also grab it from Gradle:
你需要在你的项目中添加HFRecyclerView库,或者你也可以从 Gradle 获取它:
compile 'com.mikhaellopez:hfrecyclerview:1.0.0'
This is a result in image:
这是图像中的结果:
EDIT:
编辑:
If you just want to add a margin at the top and/or bottom with this library: SimpleItemDecoration:
如果您只想使用此库在顶部和/或底部添加边距:SimpleItemDecoration:
int offsetPx = 10;
recyclerView.addItemDecoration(new StartOffsetItemDecoration(offsetPx));
recyclerView.addItemDecoration(new EndOffsetItemDecoration(offsetPx));
回答by Davideas
I know I come late, but only recently I was able to implement such "addHeader" to the Adapter. In my FlexibleAdapterproject you can call setHeader
on a Sectionableitem, then you call showAllHeaders
. If you need only 1 header then the first item should have the header. If you delete this item, then the header is automatically linked to the next one.
我知道我来晚了,但直到最近我才能够为适配器实现这样的“addHeader”。在我FlexibleAdapter的项目,你可以调用setHeader
一个Sectionable项目,然后调用showAllHeaders
。如果您只需要 1 个标题,那么第一项应该有标题。如果删除此项,则标题会自动链接到下一项。
Unfortunately footers are not covered (yet).
不幸的是,页脚还没有被覆盖。
The FlexibleAdapter allows you to do much more than create headers/sections. You really should have a look: https://github.com/davideas/FlexibleAdapter.
FlexibleAdapter 允许您做的不仅仅是创建标题/部分。你真的应该看看:https: //github.com/davideas/FlexibleAdapter。
回答by Gustavo
You can use the library SectionedRecyclerViewAdapterto group your items in sections and add a header to each section, like on the image below:
您可以使用库SectionedRecyclerViewAdapter将您的项目分组并为每个部分添加标题,如下图所示:
First you create your section class:
首先你创建你的部分类:
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);
}
}
Then you set up the RecyclerView with your sections and change the SpanSize of the headers with a GridLayoutManager:
然后你用你的部分设置 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);
回答by Nishant Shah
Very simple to solve!!
解决起来很简单!!
I don't like an idea of having logic inside adapter as a different view type because every time it checks for the view type before returning the view. Below solution avoids extra checks.
我不喜欢将适配器内部的逻辑作为不同的视图类型的想法,因为每次它在返回视图之前检查视图类型。下面的解决方案避免了额外的检查。
Just add LinearLayout (vertical) header view + recyclerview + footer view inside android.support.v4.widget.NestedScrollView.
只需在android.support.v4.widget.NestedScrollView 中添加 LinearLayout(垂直)页眉视图 + recyclerview + 页脚视图。
Check this out:
看一下这个:
<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>
Add this line of code for smooth scrolling
添加这行代码以实现平滑滚动
RecyclerView v = (RecyclerView) findViewById(...);
v.setNestedScrollingEnabled(false);
This will lose all RV performance and RV will try to lay out all view holders regardless of the layout_height
of RV
这将失去所有 RV 性能,并且 RV 将尝试布置所有视图持有者,而不管layout_height
RV
Recommended using for the small size list like Nav drawer or settings etc.
推荐用于小尺寸列表,如导航抽屉或设置等。