RecyclerView页眉和页脚

时间:2014-10-27 10:53:38

标签: android header footer android-5.0-lollipop android-recyclerview

以前可能已经问过这个问题,但我似乎无法找到准确的答案或解决方案。我开始使用RecyclerView,并使用LinearLayoutManager实现它。现在我想添加自定义页眉和页脚项目,这些项目与我的RecyclerView中的其他项目不同。页眉和页脚不应该是粘性的,我希望它们与其余项目一起滚动。有人可以指出一些例子如何做到这一点或只是分享想法。我会非常感激。 THX

12 个答案:

答案 0 :(得分:51)

你的适配器中的

添加这个类:

private class VIEW_TYPES {
        public static final int Header = 1;
        public static final int Normal = 2;
        public static final int Footer = 3;
}

然后覆盖以下方法:

@Override
public int getItemViewType(int position) {

    if(items.get(position).isHeader)
        return VIEW_TYPES.Header;
    else if(items.get(position).isFooter)
        return VIEW_TYPES.Footer;
    else
        return VIEW_TYPES.Normal;

}

现在,在onCreateViewHolder方法中,基于视图类型::

来扩展您的布局
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

    View rowView;

    switch (i) {

        case VIEW_TYPES.Normal:
            rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.normal, viewGroup, false);
            break;
        case VIEW_TYPES.Header:
            rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.header, viewGroup, false);
            break;
        case VIEW_TYPES.Footer:
            rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.footer, viewGroup, false);
            break;
        default:
            rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.normal, viewGroup, false);
            break;
    }
    return new ViewHolder (rowView);
}

现在在onBindViewHolder方法中根据视图持有者绑定您的布局:

@Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {

        int viewType = getItemViewType(position);

        switch(viewType) {

            case VIEW_TYPES.Header: // handle row header
                break;
            case VIEW_TYPES.Footer: // handle row footer
                break;
            case VIEW_TYPES.Normal: // handle row item
                break;

        }

    }

希望这可以提供帮助。

答案 1 :(得分:23)

使用ItemDecorations并且不修改任何其他代码非常容易:

recyclerView.addItemDecoration(new HeaderDecoration(this,
                               recyclerView,  R.layout.test_header));

预留一些绘图空间,为想要绘制的布局增加空间并在预留空间中绘制。

装饰的代码:

public class HeaderDecoration extends RecyclerView.ItemDecoration {

    private View mLayout;

    public HeaderDecoration(final Context context, RecyclerView parent, @LayoutRes int resId) {
        // inflate and measure the layout
        mLayout = LayoutInflater.from(context).inflate(resId, parent, false);
        mLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        // layout basically just gets drawn on the reserved space on top of the first view
        mLayout.layout(parent.getLeft(), 0, parent.getRight(), mLayout.getMeasuredHeight());
        for (int i = 0; i < parent.getChildCount(); i++) {
            View view = parent.getChildAt(i);
            if (parent.getChildAdapterPosition(view) == 0) {
                c.save();
                final int height = mLayout.getMeasuredHeight();
                final int top = view.getTop() - height;
                c.translate(0, top);
                mLayout.draw(c);
                c.restore();
                break;
            }
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (parent.getChildAdapterPosition(view) == 0) {
            outRect.set(0, mLayout.getMeasuredHeight(), 0, 0);
        } else {
            outRect.setEmpty();
        }
    }
}

答案 2 :(得分:8)

您可以使用此GitHub库以最简单的方式向RecyclerView添加页眉或页脚。

您需要在项目中添加HFRecyclerView库,或者也可以从Gradle中获取它:

compile 'com.mikhaellopez:hfrecyclerview:1.0.0'
  

此库基于@hister

的作品

这是图片中的结果:

Preview

答案 3 :(得分:6)

recyclerview:1.2.0引入了 ConcatAdapter

ConcatAdapter是一个新的RecyclerView适配器,可以线性组合多个适配器。

如何使用ConcatAdapter?

将以下依赖项添加到您的build.gradle文件中

androidx.recyclerview:recyclerview:1.2.0-alpha04

然后,如果您有多个适配器,则可以轻松地使用它们进行合并

  MyAdapter adapter1 = ...;
 AnotherAdapter adapter2 = ...;
 ConcatAdapter merged = new ConcatAdapter(adapter1, adapter2);
 recyclerView.setAdapter(merged);

对于上面的示例,ConcatAdapter将显示适配器1中的项,然后是适配器2。

Here,您可以找到完整的文档。

找到完整的工作示例here

阅读this文章以了解更多信息。

Here,您可以找到源代码。

答案 4 :(得分:2)

点击here。 我做了RecyclerView.Adapter的扩展。易于添加页眉和页脚。

class HFAdapter extends HFRecyclerViewAdapter<String, HFAdapter.DataViewHolder>{

    public HFAdapter(Context context) {
        super(context);
    }

    @Override
    public DataViewHolder onCreateDataItemViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.data_item, parent, false);
        return new DataViewHolder(v);
    }

    @Override
    public void onBindDataItemViewHolder(DataViewHolder holder, int position) {
        holder.itemTv.setText(getData().get(position));
    }

    class DataViewHolder extends RecyclerView.ViewHolder{
        TextView itemTv;
        public DataViewHolder(View itemView) {
            super(itemView);
            itemTv = (TextView)itemView.findViewById(R.id.itemTv);
        }
    }
}

//add header
View headerView = LayoutInflater.from(this).inflate(R.layout.header, recyclerView, false);
hfAdapter.setHeaderView(headerView);
//add footer
View footerView = LayoutInflater.from(this).inflate(R.layout.footer, recyclerView, false);
hfAdapter.setFooterView(footerView);

//remove
hfAdapter.removeHeader();
hfAdapter.removeFooter();

答案 5 :(得分:2)

对于在Recyclerview中使用GridView项目的分段LinearView标题: -

检查SectionedGridRecyclerViewAdapter

enter image description here

答案 6 :(得分:2)

我建议不要自定义rv adapater。

保持原样...在你的rv项目布局中,只需添加页脚布局并设置可见性。

然后当你到达适配器中的最后一项时......让它可见。

当您尝试此操作时,请确保将其添加到您的rv适配器。

   @Override
    public void onBindViewHolder(final PersonViewHolder personViewHolder, int i) {
           if(i==List.size()) // Last item in recycle view
           personViewHolder.tv_footer.setVisibility(VISIBLE);// Make footer visible now }

 @Override
    public int getItemViewType(int position) {
        return position;
    }

为Header做同样的事情。 这里i == 0 //列表的第一项

最简单的解决方案。

答案 7 :(得分:1)

您可以使用库SectionedRecyclerViewAdapter,它具有&#34; Sections&#34;的概念,其中Section包含页眉,页脚和内容(项目列表)。在您的情况下,您可能只需要一个部分,但您可以拥有多个部分:

enter image description here

1)创建自定义Section类:

class MySection extends StatelessSection {

    List<String> myList = Arrays.asList(new String[] {"Item1", "Item2", "Item3" });

    public MySection() {
        // call constructor with layout resources for this Section header, footer and items 
        super(R.layout.section_header, R.layout.section_footer,  R.layout.section_item);
    }

    @Override
    public int getContentItemsTotal() {
        return myList.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(myList.get(position));
    }
}

2)为项目创建自定义ViewHolder:

class MyItemViewHolder extends RecyclerView.ViewHolder {

    private final TextView tvItem;

    public MyItemViewHolder(View itemView) {
        super(itemView);

        tvItem = (TextView) itemView.findViewById(R.id.tvItem);
    }
}

3)使用SectionedRecyclerViewAdapter设置ReclyclerView

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

MySection mySection = new MySection();

// Add your Sections
sectionAdapter.addSection(mySection);

// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);

答案 8 :(得分:1)

可能是你想要的docs

一个专门的RecyclerView.Adapter,它显示来自RecyclerView.Adapter序列的数据。序列是静态的,但每个适配器可以在零个或多个项目视图中显示。子适配器可以安全地使用ViewType。另外,我们可以像ListView一样添加HeaderView或addFooterView。

答案 9 :(得分:1)

这里是recyclelerview的一个标题itemdecoration

通过一些修改,您可以更改为页脚

public class HeaderItemDecoration extends RecyclerView.ItemDecoration {

private View customView;

public HeaderItemDecoration(View view) {
    this.customView = view;
}

@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    super.onDraw(c, parent, state);
    customView.layout(parent.getLeft(), 0, parent.getRight(), customView.getMeasuredHeight());
    for (int i = 0; i < parent.getChildCount(); i++) {
        View view = parent.getChildAt(i);
        if (parent.getChildAdapterPosition(view) == 0) {
            c.save();
            final int height = customView.getMeasuredHeight();
            final int top = view.getTop() - height;
            c.translate(0, top);
            customView.draw(c);
            c.restore();
            break;
        }
    }
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    if (parent.getChildAdapterPosition(view) == 0) {
        customView.measure(View.MeasureSpec.makeMeasureSpec(parent.getMeasuredWidth(), View.MeasureSpec.AT_MOST),
                View.MeasureSpec.makeMeasureSpec(parent.getMeasuredHeight(), View.MeasureSpec.AT_MOST));
        outRect.set(0, customView.getMeasuredHeight(), 0, 0);
    } else {
        outRect.setEmpty();
    }
}
}      

答案 10 :(得分:1)

另一种方法是将标头和reyclerview包装在 coordinatorlayout 中:

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:elevation="0dp">

    <View
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_scrollFlags="scroll" />

</android.support.design.widget.AppBarLayout>

<android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior" />

答案 11 :(得分:0)

如果您只需要一个空白的页眉和页脚,这是一种非常简单的方法(用Kotlin编写):

class HeaderFooterDecoration(private val headerHeight: Int, private val footerHeight: Int) : RecyclerView.ItemDecoration() {
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        val adapter = parent.adapter ?: return
        when (parent.getChildAdapterPosition(view)) {
            0 -> outRect.top = headerHeight
            adapter.itemCount - 1 -> outRect.bottom = footerHeight
            else -> outRect.set(0, 0, 0, 0)
        }
    }
}

这样称呼:

recyclerView.addItemDecoration(HeaderFooterDecoration(headerHeightPx, footerHeightPx))