RecyclerView header and footer

走远了吗. 提交于 2019-11-26 10:29:57

in your adapter add this class:

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

then Override the following method like this:

@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;

}

Now in the onCreateViewHolder method inflate your layout based on the view type::

@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);
}

Now in the onBindViewHolder method bind your layout based on the view holder:

@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;

        }

    }

Hope this can help.

This is very easy with ItemDecorations and without modifying any other code:

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

Reserve some space for drawing, inflate the layout you want drawn and draw it in the reserved space.

The code for the Decoration:

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();
        }
    }
}
lopez.mikhael

You can use this GitHub] library to add a Header or Footer to your RecyclerView in the simplest way possible.

You need to add the HFRecyclerView library in your project or you can also grab it from Gradle:

compile 'com.mikhaellopez:hfrecyclerview:1.0.0'

This library is based on a work at @hister

This is a result in image:

Click here. I did a extension of RecyclerView.Adapter. Easy to add header and footer.

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();
Harpreet

For Sectioned LinearView headings with GridView items in Recyclerview:-

Check SectionedGridRecyclerViewAdapter

I would suggest not to customize rv adapater.

Keep it as it as...in your rv item layout just add the footer with the layout and set the visisbilty gone.

Then when you reach the last item in adapter...make it visible.

and when you try this make sure you add this to your rv adapter.

   @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;
    }

Do the same for Header. Here i==0 // first item of list

Easiest solution to me.

May be GroupAdapter is what you want.

A specialized RecyclerView.Adapter that presents data from a sequence of RecyclerView.Adapter. The sequence is static but each adapter can be presented in zero or more item views. The child adapter can use ViewType safely. In addition, we can addHeaderView or addFooterView like ListView.

You can use the library SectionedRecyclerViewAdapter, it has the concept of "Sections", where which Section has a Header, Footer and Content (list of items). In your case you might only need one Section but you can have many:

1) Create a custom Section class:

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) Create a custom ViewHolder for the items:

class MyItemViewHolder extends RecyclerView.ViewHolder {

    private final TextView tvItem;

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

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

3) Set up your ReclyclerView with the SectionedRecyclerViewAdapter

// 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);

here some header itemdecoration for recyclerview

withsome modification you can change to footer

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();
    }
}
}      

One other way would be wrapping header and reyclerview in a 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" />

If all you need is a blank header and footer, here is a very simple way to achieve this (written in 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)
        }
    }
}

Call it this way:

recyclerView.addItemDecoration(HeaderFooterDecoration(headerHeightPx, footerHeightPx))
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!