Recyclerview Header and Footer

Android 5.0 - Add header/footer to a RecyclerView

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.

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 repository for checking the implementation of adding both header and a footer.

how do I add header/footer on my Recyclerview in android

I think there is a small issue in position of onBindViewHolder

Can you change your code like this and give it a try.

else if (holder instanceof ItemViewHolder) {
ItemViewHolder itemView = (ItemViewHolder) holder;
itemView.tvPrice.setText(mDataset.get(position - 1).getPrice());
itemView.tvProblems.setText(mDataset.get(position - 1).getProblems());
}

The issue with your code is you are returning the same viewholder you should change the code like this in onCreateViewHolder

    View itemView;
Log.i("info", String.valueOf(viewType));
if (viewType == TYPE_ITEM) {
//Inflating recycle view item layout
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_problems, parent, false);
ViewHolder vh = new ViewHolder(itemView);
return vh;
} else if (viewType == TYPE_HEADER) {
//Inflating header view
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.problem_recycler_header, parent, false);
HeaderViewHolder vh = new HeaderViewHolder(itemView);
return vh;
} else if (viewType == TYPE_FOOTER) {
//Inflating footer view
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.problem_recycler_footer, parent, false);
FooterViewHolder vh = new FooterViewHolder(itemView);
return vh;
}

Generic recycleview adapter with header footer

Problem was here:
Sample Image

Solution:

  private int getLastPosition() {
return getItemCount() - 1;
}

private boolean isLastPosition(int position) {
return position == getLastPosition();
}

@Override
public int getItemViewType(int position) {
if (position == 0 && mHeaderData != null) {
return TYPE_HEADER;
} else if (isLastPosition(position) && mFooterData != null) {
return TYPE_FOOTER;
} else {
return TYPE_ITEM;
}
}

Additional:

This approach looks fine. Implementation is simple and easy readable.

Here is official example from Google using similar approach:
https://developer.android.com/codelabs/kotlin-android-training-headers#0.

IndexOutOfBoundsException for Recyclerview header and footer

Try Below code

public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if (holder instanceof HeaderViewHolder) {
HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
headerHolder.headerFlag.setText("Flag");
headerHolder.headerCountry.setText("Country");
headerHolder.headerPopulation.setText("Population");
headerHolder.headerCountry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext, "You clicked at Header View!", Toast.LENGTH_SHORT).show();
}
});
} else if (holder instanceof FooterViewHolder) {
((FooterViewHolder) holder).footerFlag.setText("Footer");
} else if (holder instanceof ItemViewHolder) {
ItemViewHolder itemViewHolder=(ItemViewHolder)holder;
position = position -1;
Glide.with(mContext).load(mArrayList.get(position).getImagePath()).into(itemViewHolder.imageViewCountryFlag);
itemViewHolder.textViewCountryName.setText(mArrayList.get(position).getCountry());
itemViewHolder.textViewcountryPopulation.setText(mArrayList.get(position).getPopulation());
}}

Is there an addHeaderView equivalent for RecyclerView?

There isn't an easy way like listview.addHeaderView() but you can achieve this by adding a type to your adapter for header.

Here is an example

public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
String[] data;

public HeaderAdapter(String[] data) {
this.data = data;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
//inflate your layout and pass it to view holder
return new VHItem(null);
} else if (viewType == TYPE_HEADER) {
//inflate your layout and pass it to view holder
return new VHHeader(null);
}

throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof VHItem) {
String dataItem = getItem(position);
//cast holder to VHItem and set data
} else if (holder instanceof VHHeader) {
//cast holder to VHHeader and set data for header.
}
}

@Override
public int getItemCount() {
return data.length + 1;
}

@Override
public int getItemViewType(int position) {
if (isPositionHeader(position))
return TYPE_HEADER;

return TYPE_ITEM;
}

private boolean isPositionHeader(int position) {
return position == 0;
}

private String getItem(int position) {
return data[position - 1];
}

class VHItem extends RecyclerView.ViewHolder {
TextView title;

public VHItem(View itemView) {
super(itemView);
}
}

class VHHeader extends RecyclerView.ViewHolder {
Button button;

public VHHeader(View itemView) {
super(itemView);
}
}
}

link to gist -> here

Kotlin RecyclerView Add Loading footer

In order to create a footer item in your Adapter your should implement a few things inside your code.

First thing is that you should create a new itemViewType for your footer.
The second thing which you have to do is to create a way that you tell your Adapter that this view is actually a footer. Since you are using List with String you should add something like this at the end of the List:

numberList.add("VIEW_TYPE_FOOTER")

and after that:

override fun getItemViewType(position: Int): Int {
val item = numberList[position]
if(position == 0) {
return TYPE_HEADER
} else if(item == "VIEW_TYPE_FOOTER") {
return TYPE_FOOTER
}
return TYPE_ITEMS
}

And you should check for lastVisibleItem, not the firstVisible one and get the nextPage after that.
Keep in mind that after getting the next page you will have to remove the latest item from the List which will be your footer view, add the next page to the List and then decide if you will need to add a new footer.

Hope this helps you to understand how to make it work in your situation.

Is there an addHeaderView equivalent for RecyclerView?

There isn't an easy way like listview.addHeaderView() but you can achieve this by adding a type to your adapter for header.

Here is an example

public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
String[] data;

public HeaderAdapter(String[] data) {
this.data = data;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
//inflate your layout and pass it to view holder
return new VHItem(null);
} else if (viewType == TYPE_HEADER) {
//inflate your layout and pass it to view holder
return new VHHeader(null);
}

throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof VHItem) {
String dataItem = getItem(position);
//cast holder to VHItem and set data
} else if (holder instanceof VHHeader) {
//cast holder to VHHeader and set data for header.
}
}

@Override
public int getItemCount() {
return data.length + 1;
}

@Override
public int getItemViewType(int position) {
if (isPositionHeader(position))
return TYPE_HEADER;

return TYPE_ITEM;
}

private boolean isPositionHeader(int position) {
return position == 0;
}

private String getItem(int position) {
return data[position - 1];
}

class VHItem extends RecyclerView.ViewHolder {
TextView title;

public VHItem(View itemView) {
super(itemView);
}
}

class VHHeader extends RecyclerView.ViewHolder {
Button button;

public VHHeader(View itemView) {
super(itemView);
}
}
}

link to gist -> here

How to add a simple 8dp header/footer to Android's RecyclerView?

Adding a top padding and setting clipToPadding to false will do the trick.

Something like this:

  <android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:paddingTop="8dp"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />


Related Topics



Leave a reply



Submit