Android 5.0 - Add Header/Footer to a Recyclerview

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

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

Adding a footer view in RECYCLER VIEW?

I created a wrapper around my adapter using this gist.And after that i can include my own layout and viewholder for my header and footer as well as the rest of the items.

    public class SplitMembersAdapter extends HeaderFooterRecyclerViewAdapter  implements  AutoCompleteContactTextView.ContactSelectListener{

private final ArrayList<SplitMember> mSplitMembersList;
private final ImageLoader mImageLoader;
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private static final int TYPE_FOOTER = 2;
private int splitAmount;
private boolean isAmountSplitted;

public SplitMembersAdapter(ArrayList<SplitMember> members, ImageLoader imageLoader) {
mSplitMembersList = members;
mImageLoader = imageLoader;
}

@Override
public boolean useFooter() {
return true;
}

@Override
public RecyclerView.ViewHolder onCreateFooterViewHolder(ViewGroup viewGroup, int viewType) {
View itemView = LayoutInflater.from(viewGroup.getContext()).
inflate(R.layout.split_member_footer_layout, viewGroup, false);
return new SplitMemberFooterViewHolder(itemView);
}


//Binding data wrt to footer
@Override
public void onBindFooterView(final RecyclerView.ViewHolder holder, int position) {
if (holder instanceof SplitMemberFooterViewHolder){
((SplitMemberFooterViewHolder) holder).mAddFriends.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((SplitMemberFooterViewHolder) holder).mAddFriendPlaceholderTxt.setVisibility(View.GONE);
((SplitMemberFooterViewHolder) holder).mAddFriends.setVisibility(View.GONE);
((SplitMemberFooterViewHolder) holder).mPhoneEnterView.setVisibility(View.VISIBLE);
}
});

((SplitMemberFooterViewHolder) holder).mPhoneEnterView.setmContactSelectListener(this);
((SplitMemberFooterViewHolder) holder).mPhoneEnterView.setDisplayPhoto(true);
((SplitMemberFooterViewHolder) holder).mPhoneEnterView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
((SplitMemberFooterViewHolder) holder).mPhoneEnterView.setCursorVisible(true);
}
});
((SplitMemberFooterViewHolder) holder).mPhoneEnterView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP)
{
switch (keyCode)
{
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:

postContact(((SplitMemberFooterViewHolder) holder)
.mPhoneEnterView.getText().toString());

return true;
default:
break;
}
}
return false;
}
});


}
}

@Override
public void onContactSelect(People contact) {
String mobileNum = String.valueOf(contact.getData());
postContact(mobileNum);
}

private static void postContact(String contact) {
EventBus.getDefault().post(new Events(Events.GET_CONTACT,
true, contact));
}

@Override
public RecyclerView.ViewHolder onCreateBasicItemViewHolder(ViewGroup viewGroup, int viewType) {
View itemView = LayoutInflater.from(viewGroup.getContext()).
inflate(R.layout.split_member_item_layout, viewGroup, false);
return new SplitMemberViewHolder(itemView);
}
//Binding data wrt the items
@Override
public void onBindBasicItemView(RecyclerView.ViewHolder holder, int position) {
SplitMember contact = mSplitMembersList.get(position);

if (holder instanceof SplitMemberViewHolder){
((SplitMemberViewHolder) holder).splitMemAmount.setText(String.format("%.2f", contact.getShare()));
((SplitMemberViewHolder) holder).splitMemberColor.setBackgroundColor(contact.getColor());
if (position == TYPE_HEADER) {
((SplitMemberViewHolder) holder).splitMemName.setText("You");
mImageLoader.loadImage(contact.getImg(),
((SplitMemberViewHolder) holder).splitMemImg, R.drawable.empty_user_placeholder);
} else {
((SplitMemberViewHolder) holder).splitMemName.setText(contact.getName());
mImageLoader.loadImage(Utils.getProfileImageUrl(contact.getImg()),
((SplitMemberViewHolder) holder).splitMemImg, R.drawable.empty_user_placeholder);
}
}
}


@Override
public int getBasicItemCount() {
return mSplitMembersList.size();
}

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


}

//Seperate viewholders for different footer as well as items
class SplitMemberViewHolder extends RecyclerView.ViewHolder {

public BezelImageView splitMemImg;
public TextView splitMemName;
public TextView splitMemAmount;
public View splitMemberColor;

public SplitMemberViewHolder(View itemView) {
super(itemView);
splitMemberColor = itemView.findViewById(R.id.member_color);
splitMemImg = (BezelImageView) itemView.findViewById(R.id.member_profile_image);
splitMemName = (TextView) itemView.findViewById(R.id.member_name);
splitMemAmount = (TextView) itemView.findViewById(R.id.member_amount);
}
}class SplitMemberFooterViewHolder extends RecyclerView.ViewHolder {

public BezelImageView mAddFriends;
public AutoCompleteContactTextView mPhoneEnterView;
public TextView mAddFriendPlaceholderTxt;

public SplitMemberFooterViewHolder(View itemView) {
super(itemView);
mAddFriends = (BezelImageView) itemView.findViewById(R.id.add_friends_img);
mPhoneEnterView = (AutoCompleteContactTextView) itemView.findViewById(R.id.add_member_edit_text);
mAddFriendPlaceholderTxt = (TextView) itemView.findViewById(R.id.add_friend_static_txt);
}
}

FooterView to recyclerView

You can override getItemViewType():

@Override
public int getItemViewType(int position) {
// you can define your view type as your requirement

return super.getItemViewType(position);
}

@Override
public PostHandler onCreateViewHolder(ViewGroup parent, int viewType) {
View v = inflater.inflate(R.layout.post_row, parent, false);
if(viewType == yourviewtype) // inflate your layout accordingly
PostHandler postHandler = new PostHandler(v);
return postHandler;
}

And update your posthander constructor:

     public PostHandler(View itemView, int viewType) {
super(itemView);
if(viewType == yourrequiredview) {

}else {
mprofilePic = (SimpleDraweeView) itemView.findViewById(R.id.post_profilePic);
mpostAuthor = (TextView) itemView.findViewById(R.id.post_authorName);
}
}
//final adapter class is as

public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostHandler> {

private LayoutInflater inflater;
Context context;

public PostAdapter(Context con) {
context = con;
inflater = LayoutInflater.from(con);
}

@Override
public PostHandler onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if(viewType == 2){
v = inflater.inflate(R.layout.post_row, parent, false);
}else{
v = inflater.inflate(R.layout.progressbar_layout, parent, false);
}


PostHandler postHandler = new PostHandler(v, viewType);

return postHandler;
}

@Override
public void onBindViewHolder(final PostHandler holder, final int position)
{if (getItemViewType(position)==2)
{ //The bind actions set text and others happen here...

}
}

@Override
public int getItemCount() {
return FreshPostContainer.getInstance().getPosts().size();
}

class PostHandler extends RecyclerView.ViewHolder {

SimpleDraweeView mprofilePic;
TextView mpostAuthor;
ProgressBar mProgressBar;
;

public PostHandler(View itemView) {
super(itemView);
mprofilePic = (SimpleDraweeView) itemView.findViewById(R.id.post_profilePic);
mpostAuthor = (TextView) itemView.findViewById(R.id.post_authorName);
}
public PostHandler(View itemView, int viewType) {
super(itemView);
if(viewType == 2) {
mprofilePic = (SimpleDraweeView) itemView.findViewById(R.id.post_profilePic);
mpostAuthor = (TextView) itemView.findViewById(R.id.post_authorName);
}else {
mProgressBar = (ProgressBar) itemView.findViewById(R.id.progressbar);
}
}
}

@Override
public int getItemViewType(int position) {
if(position == (getItemCount()-1)){
return 1;
}else {
return 2;
}
}
}

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" />

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.



Related Topics



Leave a reply



Submit