Open In App

Two Dimensional RecyclerView in Android with Example

Last Updated : 05 May, 2021
Improve
Improve
Like Article
Like
Save
Share
Report

In many cases, we have to display the horizontally scrollable list in such a way that we also have to show headers on it to indicate the list belongs to which category. So for that, we have to create a two-dimensional RecyclerView in our app. In this article, we will take a look at creating a 2 Dimensional RecyclerView in our app. 

What we are going to build in this article? 

We will be building a simple application in which we will be displaying two RecyclerView. One will be vertical and one will be horizontal. In our vertical RecyclerView, we will be displaying the news categories and inside that categories, we will be displaying the different news belonging to that category in a horizontally scrollable format. A sample GIF is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using the Java language. 

Two Dimensional RecyclerView in Android with Example Sample GIF

Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.

Step 2: Add the below dependency in your build.gradle file and add internet permission in the manifest file

Below is the dependency for Volley which we will be using to get the data from API. For adding this dependency navigate to the app > Gradle Scripts > build.gradle(app) and add the below dependency in the dependencies section. We have used the Picasso dependency for image loading from the URL.  

// below line is used for volley library
implementation ‘com.android.volley:volley:1.1.1’

// below line is used for image loading library
implementation ‘com.squareup.picasso:picasso:2.71828’

 Also, add internet permission in the manifest file

<uses-permission android:name="android.permission.INTERNET" /> 

Step 3: Working with the activity_main.xml file

Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Below is the code for the activity_main.xml file. 

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
  
    <!--recycler view for displaying categories-->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/idRVCategories"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
  
</RelativeLayout>


Step 4: Creating a modal class for storing our news  

Navigate to the app > java > your app’s package name > Right-click on it > New > Java class and name it as NewsModal and add the below code to it. Below is the code for the NewsModal.java file. Comments are added inside the code to understand the code in more detail.

Java




public class NewsModal {
      
    // string for news title,image and description.
    private String newsTitle;
    private String newsDesc;
    private String newsImg;
  
    // on below line we have created a constructor.
    public NewsModal(String newsTitle, String newsDesc, String newsImg) {
        this.newsTitle = newsTitle;
        this.newsDesc = newsDesc;
        this.newsImg = newsImg;
    }
  
    public String getNewsImg() {
        return newsImg;
    }
  
    public void setNewsImg(String newsImg) {
        this.newsImg = newsImg;
    }
  
    // creating a getter and setter.
    public String getNewsTitle() {
        return newsTitle;
    }
  
    public void setNewsTitle(String newsTitle) {
        this.newsTitle = newsTitle;
    }
  
    public String getNewsDesc() {
        return newsDesc;
    }
  
    public void setNewsDesc(String newsDesc) {
        this.newsDesc = newsDesc;
    }
  
}


Step 5: Creating a modal class for storing our News Categories 

Similarly, create another Java class and name it as CategoriesModal and add the below code to it. Below is the code for the CategoriesModal.java file. Comments are added inside the code to understand the code in more detail.

Java




import java.util.ArrayList;
  
public class CategoriesModal {
    // creating variable for our category, 
    // news array list and expanded boolean.
    private String newsCategory;
    private ArrayList<NewsModal> newsModalArrayList;
  
    // creating a constructor.
    public CategoriesModal(String newsCategory, ArrayList<NewsModal> newsModalArrayList) {
        this.newsCategory = newsCategory;
        this.newsModalArrayList = newsModalArrayList;
  
    }
  
    public String getNewsCategory() {
        return newsCategory;
    }
  
    public void setNewsCategory(String newsCategory) {
        this.newsCategory = newsCategory;
    }
  
    public ArrayList<NewsModal> getNewsModalArrayList() {
        return newsModalArrayList;
    }
  
    public void setNewsModalArrayList(ArrayList<NewsModal> newsModalArrayList) {
        this.newsModalArrayList = newsModalArrayList;
    }
}


Step 6: Creating a layout file for our news item 

Navigate to the app > res > layout > Right-click on it > New > Layout Resource File and name it as news_rv_item and add the below code to it. Below is the code for the news_rv_item.xml file. 

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 
    android:layout_width="300dp"
    android:layout_height="200dp"
    android:layout_gravity="center"
    android:layout_margin="3dp"
    android:backgroundTint="@color/purple_200"
    app:cardCornerRadius="4dp">
  
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent">
  
        <!--image view for displaying image-->
        <ImageView
            android:id="@+id/idIVnews"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@id/idTVNewsTitle"
            android:src="@drawable/logo1" />
  
        <!--text view for displaying news title-->
        <TextView
            android:id="@+id/idTVNewsTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_above="@id/idTVNewsDesc"
            android:layout_marginStart="4dp"
            android:layout_marginTop="8dp"
            android:padding="4dp"
            android:text="News Title"
            android:textAllCaps="false"
            android:textColor="@color/white"
            android:textStyle="bold" />
  
        <!--text view for displaying news description-->
        <TextView
            android:id="@+id/idTVNewsDesc"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginStart="4dp"
            android:layout_marginTop="4dp"
            android:layout_marginBottom="8dp"
            android:maxLines="3"
            android:padding="4dp"
            android:text="News Desc"
            android:textColor="@color/white"
            android:textSize="12sp" />
          
    </RelativeLayout>
      
</androidx.cardview.widget.CardView>


Step 7: Creating a new layout file for news categories  

Similarly, create another layout resource file and name it as news_categories_rv_item and add the below code to it. Below is the code for the news_categories_rv_item.xml file. 

XML




<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="4dp"
    app:cardCornerRadius="4dp">
  
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
  
        <!--text view for displaying news category-->
        <TextView
            android:id="@+id/idTVCategory"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:text="Category"
            android:textSize="15sp" />
  
        <androidx.cardview.widget.CardView
            android:id="@+id/idCVCategory"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:elevation="0dp"
            app:cardElevation="0dp">
  
            <!--recycler view for displaying news recycler view-->
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/idRVNews"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
  
        </androidx.cardview.widget.CardView>
  
    </LinearLayout>
      
</androidx.cardview.widget.CardView>


Step 8: Creating a new Adapter class for setting data in news RecyclerView

Create another new Java class and name it as NewsRVAdapter and add the below code to it. Below is the code for the NewsRVAdapter.java file. Comments are added inside the code to understand the code in more detail.

Java




import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
  
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
  
import com.squareup.picasso.Picasso;
  
import java.util.ArrayList;
  
public class NewsRVAdapter extends RecyclerView.Adapter<NewsRVAdapter.ViewHolder> {
     
    // variables for array list and context.
    private ArrayList<NewsModal> newsModalArrayList;
    private Context context;
  
    // creating a constructor.
    public NewsRVAdapter(ArrayList<NewsModal> newsModalArrayList, Context context) {
        this.newsModalArrayList = newsModalArrayList;
        this.context = context;
    }
  
    @NonNull
    @Override
    public NewsRVAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // passing our layout file for displaying our card item
        return new NewsRVAdapter.ViewHolder(LayoutInflater.from(context).inflate(R.layout.news_rv_item, parent, false));
  
    }
  
    @Override
    public void onBindViewHolder(@NonNull NewsRVAdapter.ViewHolder holder, int position) {
        // on below line we are setting data to our ui components.
        NewsModal modal = newsModalArrayList.get(position);
        holder.newsDescTV.setText(modal.getNewsDesc());
        holder.newsTitleTV.setText(modal.getNewsTitle());
        Picasso.get().load(modal.getNewsImg()).into(holder.newsIV);
    }
  
    @Override
    public int getItemCount() {
        // returning the size of array list
        return newsModalArrayList.size();
    }
  
    public class ViewHolder extends RecyclerView.ViewHolder {
        // creating variables for our text view.
        private TextView newsTitleTV, newsDescTV;
        private ImageView newsIV;
  
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            // initializing our text view
            newsIV = itemView.findViewById(R.id.idIVnews);
            newsTitleTV = itemView.findViewById(R.id.idTVNewsTitle);
            newsDescTV = itemView.findViewById(R.id.idTVNewsDesc);
        }
    }
}


Step 9: Creating an adapter class for our categories RecyclerView  

Similarly, create another Java class name it as CategoriesRVAdapter, and add the below code to it. Below is the code for the CategoriesRVAdapter.java file. Comments are added inside the code to understand the code in more detail.

Java




import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
  
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
  
import java.util.ArrayList;
  
public class CategoriesRVAdapter extends RecyclerView.Adapter<CategoriesRVAdapter.ViewHolder> {
      
    // variables for array list and context.
    private ArrayList<CategoriesModal> categoriesModalArrayList;
    private Context context;
  
    // creating a constructor
    public CategoriesRVAdapter(ArrayList<CategoriesModal> categoriesModalArrayList, Context context) {
        this.categoriesModalArrayList = categoriesModalArrayList;
        this.context = context;
    }
  
    @NonNull
    @Override
    public CategoriesRVAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // passing our layout file for displaying our card item
        return new CategoriesRVAdapter.ViewHolder(LayoutInflater.from(context).inflate(R.layout.news_categories_rv_item, parent, false));
    }
  
    @Override
    public void onBindViewHolder(@NonNull CategoriesRVAdapter.ViewHolder holder, int position) {
        // setting data to our views on below line.
        CategoriesModal modal = categoriesModalArrayList.get(position);
        holder.categoryTV.setText(modal.getNewsCategory());
        NewsRVAdapter adapter = new NewsRVAdapter(modal.getNewsModalArrayList(), context);
        // below line is for setting a layout manager for our recycler view.
        // here we are creating horizontal list so we will provide orientation as vertical
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
        holder.newsRV.setLayoutManager(linearLayoutManager);
        holder.newsRV.setAdapter(adapter);
    }
  
    @Override
    public int getItemCount() {
        // returning the size of array list on below line.
        return categoriesModalArrayList.size();
    }
  
    public class ViewHolder extends RecyclerView.ViewHolder {
        // creating new variables for our views.
        private RecyclerView newsRV;
        private TextView categoryTV;
  
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            // initializing our views.
            categoryTV = itemView.findViewById(R.id.idTVCategory);
            newsRV = itemView.findViewById(R.id.idRVNews);
        }
    }
}


Step 10: Working with the MainActivity.java file

Go to the MainActivity.java file and refer to the following code. Below is the code for the MainActivity.java file. Comments are added inside the code to understand the code in more detail.

Java




import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
  
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
  
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
  
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
  
import java.util.ArrayList;
  
public class MainActivity extends AppCompatActivity {
  
    // creating array list for our categories,
    // different news and category recycler view.
    private RecyclerView categoriesRV;
    private ArrayList<CategoriesModal> categoriesModalArrayList;
    private ArrayList<NewsModal> popularNewsArrayList, sportsNews, techNews;
    private CategoriesRVAdapter adapter;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        categoriesRV = findViewById(R.id.idRVCategories);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        categoriesRV.setLayoutManager(manager);
        categoriesModalArrayList = new ArrayList<>();
        sportsNews = new ArrayList<>();
        techNews = new ArrayList<>();
        popularNewsArrayList = new ArrayList<>();
        adapter = new CategoriesRVAdapter(categoriesModalArrayList, this);
        categoriesRV.setAdapter(adapter);
        getPopularNews(popularNewsArrayList, categoriesModalArrayList);
        getSportsNews(categoriesModalArrayList, sportsNews);
        getTechnews(categoriesModalArrayList, techNews);
        adapter.notifyDataSetChanged();
  
    }
  
    private void getSportsNews(ArrayList<CategoriesModal> categoriesModals, ArrayList<NewsModal> sportsNews) {
        Log.e("TAG", "SIZE IS " + categoriesModalArrayList.size());
        String url = "https://jsonkeeper.com/b/U1BV";
        // creating a new variable for our request queue
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // in this case the data we are getting is in the form
        // of array so we are making a json array request.
        // below is the line where we are making an json array
        // request and then extracting data from each json object.
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                Log.e("TAG", "SPORTS RESPONSE IS " + response);
                try {
                    String category = response.getString("newsCategory");
                    JSONArray newsArray = response.getJSONArray("news");
                    for (int i = 0; i < newsArray.length(); i++) {
                        JSONObject newsObj = newsArray.getJSONObject(i);
                        String newsTitle = newsObj.getString("newsTitle");
                        String newsDesc = newsObj.getString("newsDesc");
                        String newsImg = newsObj.getString("newsImg");
                        sportsNews.add(new NewsModal(newsTitle, newsDesc, newsImg));
                    }
                    categoriesModals.add(new CategoriesModal(category, sportsNews));
                    Log.e("TAG", "MODALS = " + sportsNews.size() + "\n" + categoriesModals.size());
                    adapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, "Fail to get Data", Toast.LENGTH_SHORT).show();
            }
        });
        queue.add(jsonObjectRequest);
    }
  
    private void getTechnews(ArrayList<CategoriesModal> categoriesModals, ArrayList<NewsModal> techNews) {
        // ArrayList<CategoriesModal> categoriesModals = null;
        String url = "https://jsonkeeper.com/b/ZCWD";
        // creating a new variable for our request queue
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // in this case the data we are getting is in the form
        // of array so we are making a json array request.
        // below is the line where we are making an json array
        // request and then extracting data from each json object.
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                Log.e("TAG", "TECH RESPONSE IS " + response);
                try {
                    String category = response.getString("newsCategory");
                    JSONArray newsArray = response.getJSONArray("news");
                    for (int i = 0; i < newsArray.length(); i++) {
                        JSONObject newsObj = newsArray.getJSONObject(i);
                        String newsTitle = newsObj.getString("newsTitle");
                        String newsDesc = newsObj.getString("newsDesc");
                        String newsImg = newsObj.getString("newsImg");
                        techNews.add(new NewsModal(newsTitle, newsDesc, newsImg));
                    }
                    categoriesModals.add(new CategoriesModal(category, techNews));
                    adapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, "Fail to get Data", Toast.LENGTH_SHORT).show();
            }
        });
        queue.add(jsonObjectRequest);
    }
  
    private void getPopularNews(ArrayList<NewsModal> popularNewsArrayList, ArrayList<CategoriesModal> categoriesModals) {
        String url = "https://jsonkeeper.com/b/ZCWD";
        // creating a new variable for our request queue
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // in this case the data we are getting is in the form
        // of array so we are making a json array request.
        // below is the line where we are making an json array
        // request and then extracting data from each json object.
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                Log.e("TAG", "POPULAR NEWS RESPONSE IS " + response);
                try {
                    String category = response.getString("newsCategory");
                    JSONArray newsArray = response.getJSONArray("news");
                    for (int i = 0; i < newsArray.length(); i++) {
                        JSONObject newsObj = newsArray.getJSONObject(i);
                        String newsTitle = newsObj.getString("newsTitle");
                        String newsDesc = newsObj.getString("newsDesc");
                        String newsImg = newsObj.getString("newsImg");
                        popularNewsArrayList.add(new NewsModal(newsTitle, newsDesc, newsImg));
                    }
                    categoriesModals.add(new CategoriesModal(category, popularNewsArrayList));
                    adapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, "Fail to get Data", Toast.LENGTH_SHORT).show();
            }
        });
        queue.add(jsonObjectRequest);
    }
}


Now run your app and see the output of the app. 

Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads