Recyclerview becoming one of the most useful components of android applications. It uses less memory while displaying data from large Data-sets and Collections. Furthermore, it provides a seamless user experience. There are lots of customizing possibilities packed with Recyclerview.In these posts, we will create Recyclerview with header and footer android example application.
While Playing with lists instead of Listview always use Recyclerview for best optimization and experience.
before creating Recyclerview with header and footer lets understand some important concepts.
Creating RecyclerView with multiple view type
The best way to understand this concept is by using examples. So consider I want to develop a media player. Now the media player has a search option. When we search it will show all the songs and albums matching the search terms. To display this list we are using Recyclerview. Now If we show the same view for both of them then how the user is going to differentiate between song and album. Song view should contain the song name, artist, art, length. Album’s view should contain the album names and a number of songs. Let say we have two layouts song_layout.xml and album_layout.xml. Now inside Recyclerview adapter override getItemViewType() method.
getItemViewType() : This method used when you want different types of views for different rows.
Now in our case, we want two views. We can return a different integer for different views. Suppose I will return 0 for the song and for album return 1. Create two separate viewHolders for song and album. Now when we will initialize Recyclerview.
- It will first call getItemViewType() method and will return view type
- Then control will be passed to onCreateViewHolder() method. Check view type and create view holder for song or album view accordingly
- Now onBindViewHolder() method will be called. This method check instance of viewHolder and Bind data Accordingly
- Recyclerview adapter will repeat this process until all rows are created
Recyclerview with header and footer android example
In this example, we will create a beautiful material designed application of the hotel menu. This menu list will have a header with menu info and footer with some random food quote.
DEMO
DOWNLOAD SOURCE CODE
STEP 1: Recyclerview is included in the android support library package. So to use Recyclerview add a dependency for Support library as shown below. Glide and CardView dependencies are optional as we need in this project.
apply plugin: 'com.android.application' android { compileSdkVersion 27 defaultConfig { applicationId "com.loopwiki.recylerviewheaderandfooter" minSdkVersion 15 targetSdkVersion 27 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:27.1.0' /*Dependency for design library*/ implementation 'com.android.support:design:27.1.0' /*Dependency for glide library*/ implementation 'com.github.bumptech.glide:glide:4.6.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1' //Dependency for cardview library implementation 'com.android.support:cardview-v7:27.1.0' }
STEP 2: Create three new packages adapter, models, utils to keep these classes in a more organized manner.
STEP 3: Now we will create data models for our view. There are three types of views in our Recyclerview. Now to store different objects in single reference we need to create a parent class. Let’s call this class as RecyclerViewItem. Now all the data models of views will extend this class as shown below.
models -> RecyclerViewItem.java
package com.loopwiki.recylerviewheaderandfooter.models; /** * Created by amary on 25-03-2018. */ //Empty class for inheriting //You can also write common properties here public class RecyclerViewItem { }
models -> Header.java
package com.loopwiki.recylerviewheaderandfooter.models; /** * Created by amary on 25-03-2018. */ //Object of Header item public class Header extends RecyclerViewItem{ private String HeaderText; private String Category; private String ImageUrl; public Header(String headerText, String category, String imageUrl) { HeaderText = headerText; Category = category; ImageUrl = imageUrl; } public String getHeaderText() { return HeaderText; } public void setHeaderText(String headerText) { HeaderText = headerText; } public String getCategory() { return Category; } public void setCategory(String category) { Category = category; } public String getImageUrl() { return ImageUrl; } public void setImageUrl(String imageUrl) { ImageUrl = imageUrl; } }
models -> Footer.java
package com.loopwiki.recylerviewheaderandfooter.models; /** * Created by amary on 25-03-2018. */ //Object of Footer item public class Footer extends RecyclerViewItem{ private String Quote; private String Author; private String ImageUrl; public Footer(String quote, String author, String imageUrl) { Quote = quote; Author = author; ImageUrl = imageUrl; } public String getQuote() { return Quote; } public void setQuote(String quote) { Quote = quote; } public String getAuthor() { return Author; } public void setAuthor(String author) { Author = author; } public String getImageUrl() { return ImageUrl; } public void setImageUrl(String imageUrl) { ImageUrl = imageUrl; } }
models -> FoodItem.java
package com.loopwiki.recylerviewheaderandfooter.models; //Object of food item public class FoodItem extends RecyclerViewItem { private String title; private String description; private String imageUrl; private String price; private boolean isHot = false; public FoodItem(String title, String description, String imageUrl, String price, boolean isHot) { this.title = title; this.description = description; this.imageUrl = imageUrl; this.price = price; this.isHot = isHot; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getImageUrl() { return imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public boolean isHot() { return isHot; } public void setHot(boolean hot) { isHot = hot; } }
STEP 5 : Create three different layouts for header, footer and food item layout.
res -> layout -> custom_row_food.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="180dp"> <ImageView android:id="@+id/imageViewFood" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> <TextView android:id="@+id/textViewIsHot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentEnd="true" android:layout_alignParentRight="true" android:background="@android:color/holo_green_dark" android:padding="4dp" android:text="HOT" android:textAppearance="@style/TextAppearance.AppCompat.Large.Inverse" android:textStyle="bold" /> </RelativeLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="8dp" android:weightSum="10"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="3" android:orientation="vertical"> <TextView android:id="@+id/texViewFoodTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textStyle="bold" /> <TextView android:id="@+id/texViewDescription" android:layout_width="match_parent" android:layout_height="wrap_content" android:ellipsize="end" android:lines="1" android:textAppearance="@style/TextAppearance.AppCompat.Small" /> </LinearLayout> <FrameLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="7"> <TextView android:id="@+id/textViewPrice" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center_vertical|right" android:text="15" android:textColor="@color/colorAccent" android:textSize="30dp" android:textStyle="bold" /> </FrameLayout> </LinearLayout> </LinearLayout> </android.support.v7.widget.CardView>
res -> layout -> custom_row_header.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="220dp"> <ImageView android:scaleType="centerCrop" android:id="@+id/imageViewHeader" android:layout_width="match_parent" android:layout_height="match_parent" /> <FrameLayout android:layout_width="match_parent" android:background="#b53e3b3b" android:layout_height="match_parent"/> <LinearLayout android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/texViewHeaderText" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textAppearance="@style/TextAppearance.AppCompat.Large.Inverse" /> <TextView android:id="@+id/textViewCategory" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textAppearance="@style/TextAppearance.AppCompat.Small.Inverse" /> </LinearLayout> </FrameLayout>
res -> layout -> custom_row_footer.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="220dp"> <ImageView android:scaleType="centerCrop" android:id="@+id/imageViewFooter" android:layout_width="match_parent" android:layout_height="match_parent" /> <FrameLayout android:layout_width="match_parent" android:background="#b53e3b3b" android:layout_height="match_parent"/> <LinearLayout android:layout_gravity="center" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/texViewQuote" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textAppearance="@style/TextAppearance.AppCompat.Large.Inverse" /> <TextView android:id="@+id/textViewAuthor" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textAppearance="@style/TextAppearance.AppCompat.Small.Inverse" /> </LinearLayout> </FrameLayout>
STEP 6: Now we will create an adapter. In the adapter first, we check item type. Then create viewholders according to view type as shown below
adapter -> Adapter.java
package com.loopwiki.recylerviewheaderandfooter.adapter; import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; import com.loopwiki.recylerviewheaderandfooter.R; import com.loopwiki.recylerviewheaderandfooter.models.FoodItem; import com.loopwiki.recylerviewheaderandfooter.models.Footer; import com.loopwiki.recylerviewheaderandfooter.models.Header; import com.loopwiki.recylerviewheaderandfooter.models.RecyclerViewItem; import java.util.List; public class Adapter extends RecyclerView.Adapter { //Declare List of Recyclerview Items List<RecyclerViewItem> recyclerViewItems; //Header Item Type private static final int HEADER_ITEM = 0; //Footer Item Type private static final int FOOTER_ITEM = 1; ////Food Item Type private static final int FOOD_ITEM = 2; Context mContext; public Adapter(List<RecyclerViewItem> recyclerViewItems, Context mContext) { this.recyclerViewItems = recyclerViewItems; this.mContext = mContext; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View row; //Check fot view Type inflate layout according to it if (viewType == HEADER_ITEM) { row = inflater.inflate(R.layout.custom_row_header, parent, false); return new HeaderHolder(row); } else if (viewType == FOOTER_ITEM) { row = inflater.inflate(R.layout.custom_row_footer, parent, false); return new FooterHolder(row); } else if (viewType == FOOD_ITEM) { row = inflater.inflate(R.layout.custom_row_food, parent, false); return new FoodItemHolder(row); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { RecyclerViewItem recyclerViewItem = recyclerViewItems.get(position); //Check holder instance to populate data according to it if (holder instanceof HeaderHolder) { HeaderHolder headerHolder = (HeaderHolder) holder; Header header = (Header) recyclerViewItem; //set data headerHolder.texViewHeaderText.setText(header.getHeaderText()); headerHolder.textViewCategory.setText(header.getCategory()); Glide.with(mContext).load(header.getImageUrl()).into(headerHolder.imageViewHeader); } else if (holder instanceof FooterHolder) { FooterHolder footerHolder = (FooterHolder) holder; Footer footer = (Footer) recyclerViewItem; //set data footerHolder.texViewQuote.setText(footer.getQuote()); footerHolder.textViewAuthor.setText(footer.getAuthor()); Glide.with(mContext).load(footer.getImageUrl()).into(footerHolder.imageViewFooter); } else if (holder instanceof FoodItemHolder) { FoodItemHolder foodItemHolder = (FoodItemHolder) holder; FoodItem foodItem = (FoodItem) recyclerViewItem; //set data foodItemHolder.texViewFoodTitle.setText(foodItem.getTitle()); foodItemHolder.texViewDescription.setText(foodItem.getDescription()); foodItemHolder.textViewPrice.setText(foodItem.getPrice()); Glide.with(mContext).load(foodItem.getImageUrl()).into(foodItemHolder.imageViewFood); //check food item is hot or not to set visibility of hot text on image if (foodItem.isHot()) foodItemHolder.textViewIsHot.setVisibility(View.VISIBLE); else foodItemHolder.textViewIsHot.setVisibility(View.GONE); } } @Override public int getItemViewType(int position) { //here we can set view type RecyclerViewItem recyclerViewItem = recyclerViewItems.get(position); //if its header then return header item if (recyclerViewItem instanceof Header) return HEADER_ITEM; //if its Footer then return Footer item else if (recyclerViewItem instanceof Footer) return FOOTER_ITEM; //if its FoodItem then return Food item else if (recyclerViewItem instanceof FoodItem) return FOOD_ITEM; else return super.getItemViewType(position); } @Override public int getItemCount() { return recyclerViewItems.size(); } //Food item holder private class FoodItemHolder extends RecyclerView.ViewHolder { TextView texViewFoodTitle, texViewDescription, textViewPrice, textViewIsHot; ImageView imageViewFood; FoodItemHolder(View itemView) { super(itemView); texViewFoodTitle = itemView.findViewById(R.id.texViewFoodTitle); texViewDescription = itemView.findViewById(R.id.texViewDescription); imageViewFood = itemView.findViewById(R.id.imageViewFood); textViewPrice = itemView.findViewById(R.id.textViewPrice); textViewIsHot = itemView.findViewById(R.id.textViewIsHot); } } //header holder private class HeaderHolder extends RecyclerView.ViewHolder { TextView texViewHeaderText, textViewCategory; ImageView imageViewHeader; HeaderHolder(View itemView) { super(itemView); texViewHeaderText = itemView.findViewById(R.id.texViewHeaderText); textViewCategory = itemView.findViewById(R.id.textViewCategory); imageViewHeader = itemView.findViewById(R.id.imageViewHeader); } } //footer holder private class FooterHolder extends RecyclerView.ViewHolder { TextView texViewQuote, textViewAuthor; ImageView imageViewFooter; FooterHolder(View itemView) { super(itemView); texViewQuote = itemView.findViewById(R.id.texViewQuote); textViewAuthor = itemView.findViewById(R.id.textViewAuthor); imageViewFooter = itemView.findViewById(R.id.imageViewFooter); } } }
STEP 7: Now to add space between Recyclerview items we will create a new decoration class. Read a more in-depth article about recyclerview decoration here.
utils -> Space.java
package com.loopwiki.recylerviewheaderandfooter.utils; import android.graphics.Rect; import android.support.v7.widget.RecyclerView; import android.view.View; //Recyclerview decoration class to add space between items public class Space extends RecyclerView.ItemDecoration { int space; public Space(int space) { this.space = space; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); //Check if its header or footer because we don't want to add space for header and footer if (!(parent.getChildLayoutPosition(view) == 0 | parent.getChildLayoutPosition(view) == parent.getAdapter().getItemCount() - 1)) { if (parent.getChildLayoutPosition(view) == 1) { outRect.top = space; } outRect.right = space; outRect.left = space; outRect.bottom = space; } } }
STEP 8: Now recyclerview adapter, layouts, decoration classes, models are ready. Now to it’s time to create activity. Create new layout
layout -> content_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/recyclerView" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.loopwiki.recylerviewheaderandfooter.MainActivity" tools:showIn="@layout/activity_main"> </android.support.v7.widget.RecyclerView>
layout -> activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.loopwiki.recylerviewheaderandfooter.MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways|snap" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_main" /> </android.support.design.widget.CoordinatorLayout>
Inside activity class bind Recyclerview adapter. Add decoration to recyclerview. Provide some dummy data to recyclerview as shown below.
Package Name -> MainActivity.java
package com.loopwiki.recylerviewheaderandfooter; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import com.loopwiki.recylerviewheaderandfooter.adapter.Adapter; import com.loopwiki.recylerviewheaderandfooter.models.FoodItem; import com.loopwiki.recylerviewheaderandfooter.models.Footer; import com.loopwiki.recylerviewheaderandfooter.models.Header; import com.loopwiki.recylerviewheaderandfooter.models.RecyclerViewItem; import com.loopwiki.recylerviewheaderandfooter.utils.Space; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); //init RecyclerView initRecyclerView(); } private void initRecyclerView() { RecyclerView recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); //add space item decoration and pass space you want to give recyclerView.addItemDecoration(new Space(20)); //finally set adapter recyclerView.setAdapter(new Adapter(createDummyList(), this)); } //Method to create dummy data private List<RecyclerViewItem> createDummyList() { List<RecyclerViewItem> recyclerViewItems = new ArrayList<>(); Header header = new Header("Welcome To Food Express", "Non-Veg Menu", "https://cdn.pixabay.com/photo/2017/09/30/15/10/pizza-2802332_640.jpg"); //add header recyclerViewItems.add(header); String[] imageUrls = {"https://cdn.pixabay.com/photo/2016/11/18/17/42/barbecue-1836053_640.jpg", "https://cdn.pixabay.com/photo/2016/07/11/03/23/chicken-rice-1508984_640.jpg", "https://cdn.pixabay.com/photo/2017/03/30/08/10/chicken-intestine-2187505_640.jpg", "https://cdn.pixabay.com/photo/2017/02/15/15/17/meal-2069021_640.jpg", "https://cdn.pixabay.com/photo/2017/06/01/07/15/food-2362678_640.jpg"}; String[] titles = {"5 in 1 Chicken Zinger Box", "Paneer Butter Masala", "Chicken Lollipop Masala", "Paneer Manchurian", "Non-Veg. Lemon & Coriander Soup"}; String[] descriptions = {"Chicken zinger+hot wings [2 pieces]+veg strip [1 piece]+Pillsbury cookie cake+Pepsi [can]", "A spicy North Indian dish made from cottage cheese, cream, butter and select spices", "Chicken wings coated with batter of flour", "Deep-fried cottage cheese balls sautéed with ginger", "Meat shreds, lime juice and coriander"}; String[] price = {"₹220", "₹530", "₹400", "₹790", "₹150"}; boolean[] isHot = {true, false, true, true, false}; for (int i = 0; i < imageUrls.length; i++) { FoodItem foodItem = new FoodItem(titles[i], descriptions[i], imageUrls[i], price[i],isHot[i]); //add food items recyclerViewItems.add(foodItem); } Footer footer = new Footer("Your diet is a bank account. Good food choices are good investments.", "Bethenny Frankel", "https://cdn.pixabay.com/photo/2016/12/26/17/28/background-1932466_640.jpg"); //add footer recyclerViewItems.add(footer); return recyclerViewItems; } }
STEP 9: Our Activity is ready set it as launcher activity in manifest as below.
Manifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.loopwiki.recylerviewheaderandfooter"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="Food Express" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="Food Express" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
STEP 10: Modify and add flowing colors and styles in colors.xml and styles.xml respectively.
colors.xml
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#3F51B5</color> <color name="colorPrimaryDark">#303F9F</color> <color name="colorAccent">#FF4081</color> </resources>
styles.xml
<resources> <! – Base application theme. – > <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <! – Customize your theme here. – > <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> <style name="AppTheme.NoActionBar"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> </style> <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" /> <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" /> </resources>
STEP 11: Run application
If you still have any queries, please post them in the comments section below, I will be happy to help you.
6 Comments
Excellent tutorial it’s helped me a lot. Thank you
I have add header and footer to the RecyclerView , when the screen starts, header appears but as I scroll after the last item of recycler view activity destroys without showing the footer. The error is in onBindViewHolder when the data is bind to the normal list items of the recycler veiw.
Please check your data provided to recyclerview adapter may be some data is null in footer item
This helped a lot in understanding RecyclerView better. Many thanks
Your work is awesome man. Keep up the good work and if you need help don’t hesitate brother.
Thank you 😍😍