SingleChoice RecyclerView - with RadioButtons

| Page Views: 104

Android SingleChoice RecyclerView Tutorial

In this tutorial we want to see how to create a single choice recyclerview. That is a recyclerview with radiobuttons. User can select a single item and we open the item's detail activity thus showing it's data.

Why SingleChoice RecyclerView?

Well the alternative is multi-choice recyclerview, that is recyclerview with checkboxes. However in some cases you just want the user to be able to select a single item from the recyclerview. This is where radiobuttons come in.

However, in our case we will open a detail activity when the user selects a given radiobutton. Then we use intents to pass data to the detail activity.

What do we show?

Well we show images, text and radiobuttons. Normally images get rendered in imageview. The text will be rendered in textview. On the other hand the radiobuttons will show for us a compound state, basically checked or not.

Demo

Here's the gif demo of the project:

Video Tutorial

Here's the video tutorial. Please watch the video if you want more step by step details.

Gradle Scripts

(a) Build.gradle

Let's start by examining our module level build.gradle file. You can see we have no special dependencies. Instead our dependencies are just support libraries.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.android.support:cardview-v7:28.0.0'
}
}

The CardView will hold our Galaxy objects. The RecyclerView will hold multiple cardviews. AppCompat will give us the AppCompatActivity from which our MainActivity extends.

Layouts

We have three layouts

We have seven layouts:

1. activity_main.xml

This layout will get inflated into MainActivity. It simply contains our RecyclerView. I have given mine a custom background color.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:background="#009968"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/mRecylcerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>
2. activity_detail.xml

The Layout for our Detail activity. Contains imageview and textviews.to show our data.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#009688"
    tools:context=".DetailsActivity"
    tools:showIn="@layout/activity_detail">

    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="3dp"
        card_view:cardCornerRadius="3dp"
        card_view:cardElevation="5dp">

        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

                <ImageView
                    android:id="@+id/galaxyImg"
                    android:layout_width="match_parent"
                    android:layout_height="200dp"
                    android:layout_alignParentTop="true"
                    android:padding="5dp"
                    android:scaleType="fitXY"
                    android:src="@drawable/placeholder" />

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal">

                        <TextView
                            android:id="@+id/nameLabel"
                            android:layout_width="0dp"
                            android:layout_weight="1"
                            android:layout_height="wrap_content"
                            android:maxLines="1"
                            android:padding="5dp"
                            android:text="NAME"
                            android:textColor="@color/colorAccent"
                            android:textAppearance="?android:attr/textAppearanceSmall"
                            android:textStyle="bold" />

                        <TextView
                            android:id="@+id/nameTxt"
                            android:layout_width="0dp"
                            android:layout_weight="2"
                            android:layout_height="wrap_content"
                            android:maxLines="1"
                            android:padding="5dp"
                            android:text="Milky Way"
                            android:textAppearance="?android:attr/textAppearanceSmall"/>
                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal">

                        <TextView
                            android:id="@+id/dateLabel"
                            android:layout_width="0dp"
                            android:layout_weight="1"
                            android:layout_height="wrap_content"
                            android:maxLines="1"
                            android:textColor="@color/colorAccent"
                            android:padding="5dp"
                            android:text="DATE"
                            android:textAppearance="?android:attr/textAppearanceSmall"
                            android:textStyle="bold" />

                        <TextView
                            android:id="@+id/dateDetailTextView"
                            android:layout_width="0dp"
                            android:layout_weight="2"
                            android:layout_height="wrap_content"
                            android:maxLines="1"
                            android:padding="5dp"
                            android:text="Today"
                            android:textAppearance="?android:attr/textAppearanceSmall"/>
                    </LinearLayout>

                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal">

                        <TextView
                            android:id="@+id/descLabel"
                            android:layout_width="0dp"
                            android:layout_weight="1"
                            android:layout_height="wrap_content"
                            android:maxLines="1"
                            android:textColor="@color/colorAccent"
                            android:padding="5dp"
                            android:text="DESCRIPTION : "
                            android:textAppearance="?android:attr/textAppearanceSmall"
                            android:textStyle="bold" />

                        <TextView
                            android:id="@+id/descTxt"
                            android:layout_width="0dp"
                            android:layout_weight="2"
                            android:layout_height="wrap_content"
                            android:maxLines="10"
                            android:padding="5dp"
                            android:text="Description..."
                            android:textAppearance="?android:attr/textAppearanceSmall"/>
                    </LinearLayout>

                </LinearLayout>
            </LinearLayout>
        </ScrollView>
    </android.support.v7.widget.CardView>
</RelativeLayout>
3. model.xml

This is our item view layout. It will get inflated into our recyclerview view items. This will be done by our RecyclerView adapter.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_margin="1dp"
    card_view:cardCornerRadius="1dp"
    card_view:cardElevation="5dp"
    android:layout_height="135dp">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent">

        <ImageView
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:id="@+id/mGalaxyImageView"
            android:padding="5dp"
            android:scaleType="fitXY"
            android:src="@drawable/placeholder" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:text="Name"
            android:id="@+id/mNameTxt"
            android:padding="5dp"
            android:textColor="@color/colorAccent"
            android:layout_alignParentTop="true"
            android:layout_toRightOf="@+id/mGalaxyImageView" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:text="Description"
            android:textStyle="italic"
            android:maxLines="3"
            android:id="@+id/mDescTxt"
            android:padding="5dp"
            android:layout_alignBottom="@+id/mGalaxyImageView"
            android:layout_toRightOf="@+id/mGalaxyImageView" />

            <RadioButton
                android:id="@+id/mRadioButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true" />

        </RelativeLayout>
</android.support.v7.widget.CardView>

Java Code.

We have the following java classes for this project:

  1. Galaxy.java
  2. RecyclerAdapter.java
  3. DetailsActivity.java
  4. MainActivity.java
(a).Galaxy.java

This will represent our data object. It is our model class and represents a galaxy with properties like:

  1. Name.
  2. Description
  3. Image.

Here's the full code.

package info.camposha.singlechoicerecyclerview_vela;

import java.io.Serializable;

/**
 * Please take note that our data object will implement Serializable.This
 * will allow us to pass this serialized object to DetailsActivity,
 */
public class Galaxy implements Serializable{
    private String name,description;
    private int image;

    public Galaxy(String name, String description,int image) {
        this.name = name;
        this.description = description;
        this.image=image;
    }

    public String getName() {return name;}
    public String getDescription() {return description;}
    public int getImage() {return image;}

}

You can see that we've implemented the Serializable interface. This allows us to serialize our data object so that it can be transported to the DetailsActivity activity as a whole via intent.

(b). RecyclerAdapter.java

This is our adapter class. It will inflate our model layout into a view object that can be rendered by the recyclerview. It will also bind data to those inflated view objects.

package info.camposha.singlechoicerecyclerview_vela;

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.AdapterView;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.TextView;

import java.util.List;

public class RecyclerAdapter extends RecyclerView.Adapter<
RecyclerAdapter.RecyclerViewHolder> {

    /**
     * Let's start by defining our instance fields
     */
    private int selectedStarPosition = -1;
    private List<Galaxy> galaxies;
    private Context c;
    private AdapterView.OnItemClickListener onItemClickListener;

    /**
     * Let's create our constructor
     * @param context
     * @param mGalaxies
     */
    public RecyclerAdapter(Context context, List<Galaxy> mGalaxies) {
        this.c = context;
        this.galaxies = mGalaxies;
    }

    /**
     * OnCreateViewHolder - here is where we inflate our model layout
     * @param viewGroup
     * @param i
     * @return
     */
    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        final View v = LayoutInflater.from(c).inflate(R.layout.model, viewGroup, false);
        return new RecyclerViewHolder(v, this);
    }

    /**
     * OnBindViewHolder - Here's where we bind our data
     * @param viewHolder
     * @param position
     */
    @Override
    public void onBindViewHolder(RecyclerViewHolder viewHolder, final int position) {
        Galaxy galaxy = galaxies.get(position);
        try {
            viewHolder.bindData(galaxy, position);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * Let's return the number of items to be bound to our adapter.
     * @return
     */
    @Override
    public int getItemCount() {
        return galaxies.size();
    }

    /**
     * Let's receive our onItemClickListener and assign it to our local one.
     * @param onItemClickListener
     */
    public void setOnItemClickListener(AdapterView.OnItemClickListener
     onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    /**
     * When user clicks our itemView, we still invoke the onItemClick
     * @param holder
     */
    public void onItemHolderClick(RecyclerViewHolder holder) {
        if (onItemClickListener != null)
            onItemClickListener.onItemClick(null, holder.itemView,
             holder.getAdapterPosition(), holder.getItemId());
    }

    /**
     * Let's come create our ViewHolder class.
     */
    class RecyclerViewHolder extends RecyclerView.ViewHolder implements
     View.OnClickListener {

        private RecyclerAdapter mAdapter;
        private RadioButton mRadioButton;
        private TextView mNameTxt,mDescTxt;
        private ImageView mGalaxyImg;

        /**
         * In our constructor, we reference our itemView widgets.
         * @param itemView
         * @param mAdapter
         */
        public RecyclerViewHolder(View itemView, final RecyclerAdapter mAdapter) {
            super(itemView);
            this.mAdapter = mAdapter;

            mNameTxt = itemView.findViewById(R.id.mNameTxt);
            mDescTxt = itemView.findViewById(R.id.mDescTxt);
            mRadioButton = itemView.findViewById(R.id.mRadioButton);
            mGalaxyImg=itemView.findViewById(R.id.mGalaxyImageView);
            itemView.setOnClickListener(this);
            mRadioButton.setOnClickListener(this);
        }

        /**
         * Let's create a method that allows us bind our data.
         * @param galaxy
         * @param position
         */
        public void bindData(Galaxy galaxy, int position) {
            mRadioButton.setChecked(position == selectedStarPosition);
            mNameTxt.setText(galaxy.getName());
            mDescTxt.setText(galaxy.getDescription());
            mGalaxyImg.setImageResource(galaxy.getImage());
        }

        /**
         * Let's override our OnClick method.
         * @param v
         */
        @Override
        public void onClick(View v) {
            selectedStarPosition = getAdapterPosition();
            notifyItemRangeChanged(0, galaxies.size());
            mAdapter.onItemHolderClick(RecyclerViewHolder.this);
        }
    }
}
//end
(c). DetailsActivity.java

Our detail activity class. This will show the details of the clicked galaxy.

package info.camposha.singlechoicerecyclerview_vela;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.TextView;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DetailsActivity extends AppCompatActivity {

  TextView nameTxt, descTxt, dateDetailTextView;
  ImageView galaxyImg;

  //We start by initializing our widgets.
  private void initializeWidgets() {
    nameTxt = findViewById(R.id.nameTxt);
    descTxt = findViewById(R.id.descTxt);
    dateDetailTextView = findViewById(R.id.dateDetailTextView);
    descTxt = findViewById(R.id.descTxt);
    galaxyImg = findViewById(R.id.galaxyImg);
  }

  //This method will get todays date and return it as a string
  private String getDateToday() {
    DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
    Date date = new Date();
    String today = dateFormat.format(date);
    return today;
  }

  //I'll create a method to receive and show data.
  private void receiveAndShowData() {
    //RECEIVE DATA FROM ITEMS ACTIVITY VIA INTENT
    Intent i = this.getIntent();
    Galaxy g= (Galaxy) i.getSerializableExtra("GALAXY_KEY");

    //SET RECEIVED DATA TO TEXTVIEWS AND IMAGEVIEWS
    nameTxt.setText(g.getName());
    descTxt.setText(g.getDescription());
    dateDetailTextView.setText(getDateToday());
    galaxyImg.setImageResource(g.getImage());
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_detail);

    initializeWidgets();
    receiveAndShowData();
  }
}
//end
(d). MainActivity.java

Our main activity class. This is where we reference our recyclerview and define our data source.

package info.camposha.singlechoicerecyclerview_vela;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {

    RecyclerView mRecyclerView;
    private RecyclerAdapter mAdapter;
    /**
     * Let's start by defining our data source.
     * @return
     */
    private ArrayList<Galaxy> getData()
    {
        ArrayList<Galaxy> galaxies=new ArrayList<>();
        Galaxy g=new Galaxy("Whirlpool",
                "The Whirlpool Galaxy, also known as Messier 51a, M51a, and NGC 5194, is an interacting grand-design spiral Galaxy with a Seyfert 2 active galactic nucleus in the constellation Canes Venatici.",
                R.drawable.whirlpool);
        galaxies.add(g);

        g=new Galaxy("Ring Nebular",
                "The Ring Nebula is a planetary nebula in the northern constellation of Lyra. Such objects are formed when a shell of ionized gas is expelled into the surrounding interstellar medium by a red giant star.",
                R.drawable.ringnebular);
        galaxies.add(g);

        g=new Galaxy("IC 1011",
                "C 1011 is a compact elliptical galaxy with apparent magnitude of 14.7, and with a redshift of z=0.02564 or 0.025703, yielding a distance of 100 to 120 Megaparsecs. Its light has taken 349.5 million years to travel to Earth.",
                R.drawable.ic1011);
        galaxies.add(g);

        g=new Galaxy("Cartwheel",
                "The Cartwheel Galaxy is a lenticular galaxy and ring galaxy about 500 million light-years away in the constellation Sculptor. It is an estimated 150,000 light-years diameter, and a mass of about 2.9–4.8 × 10⁹ solar masses; it rotates at 217 km/s.",
                R.drawable.cartwheel);
        galaxies.add(g);

        g=new Galaxy("Triangulumn",
                "The Triangulum Galaxy is a spiral Galaxy approximately 3 million light-years from Earth in the constellation Triangulum",
                R.drawable.triangulum);
        galaxies.add(g);

        g=new Galaxy("Small Magellonic Cloud",
                "The Small Magellanic Cloud, or Nubecula Minor, is a dwarf galaxy near the Milky Way. It is classified as a dwarf irregular galaxy.",
                R.drawable.smallamgellonic);
        galaxies.add(g);

        g=new Galaxy("Centaurus A",
                " Centaurus A or NGC 5128 is a galaxy in the constellation of Centaurus. It was discovered in 1826 by Scottish astronomer James Dunlop from his home in Parramatta, in New South Wales, Australia.",
                R.drawable.centaurusa);
        galaxies.add(g);

        g=new Galaxy("Ursa Minor",
                "The Milky Way is the Galaxy that contains our Solar System." +
                        " The descriptive milky is derived from the appearance from Earth of the Galaxy – a band of light seen in the night sky formed from stars",
                R.drawable.ursaminor);
        galaxies.add(g);

        g=new Galaxy("Large Magellonic Cloud",
                " The Large Magellanic Cloud is a satellite galaxy of the Milky Way. At a distance of 50 kiloparsecs, the LMC is the third-closest galaxy to the Milky Way, after the Sagittarius Dwarf Spheroidal and the.",
                R.drawable.largemagellonic);
        galaxies.add(g);

        g=new Galaxy("Milky Way",
                "The Milky Way is the Galaxy that contains our Solar System." +
                        " The descriptive milky is derived from the appearance from Earth of the Galaxy – a band of light seen in the night sky formed from stars",
                R.drawable.milkyway);
        galaxies.add(g);

        g=new Galaxy("Andromeda",
                "The Andromeda Galaxy, also known as Messier 31, M31, or NGC 224, is a spiral Galaxy approximately 780 kiloparsecs from Earth. It is the nearest major Galaxy to the Milky Way and was often referred to as the Great Andromeda Nebula in older texts.",
                R.drawable.andromeda);
        galaxies.add(g);

        g=new Galaxy("Messier 81",
                "Messier 81 is a spiral Galaxy about 12 million light-years away in the constellation Ursa Major. Due to its proximity to Earth, large size and active galactic nucleus, Messier 81 has been studied extensively by professional astronomers.",
                R.drawable.messier81);
        galaxies.add(g);

        g=new Galaxy("Own Nebular",
                " The Owl Nebula is a planetary nebula located approximately 2,030 light years away in the constellation Ursa Major. It was discovered by French astronomer Pierre Méchain on February 16, 1781",
                R.drawable.ownnebular);
        galaxies.add(g);

        g=new Galaxy("Messier 87",
                "Messier 87 is a supergiant elliptical galaxy in the constellation Virgo. One of the most massive galaxies in the local universe, it is notable for its large population of globular clusters—M87 contains",
                R.drawable.messier87);
        galaxies.add(g);

        g=new Galaxy("Cosmos Redshift",
                "Cosmos Redshift 7 is a high-redshift Lyman-alpha emitter Galaxy, in the constellation Sextans, about 12.9 billion light travel distance years from Earth, reported to contain the first stars —formed ",
                R.drawable.cosmosredshift);
        galaxies.add(g);

        g=new Galaxy("StarBust",
                "A starburst Galaxy is a Galaxy undergoing an exceptionally high rate of star formation, as compared to the long-term average rate of star formation in the Galaxy or the star formation rate observed in most other galaxies. ",
                R.drawable.starbust);
        galaxies.add(g);

        g=new Galaxy("Sombrero",
                "Sombrero Galaxy is an unbarred spiral galaxy in the constellation Virgo located 31 million light-years from Earth. The galaxy has a diameter of approximately 50,000 light-years, 30% the size of the Milky Way.",
                R.drawable.sombrero);
        galaxies.add(g);

        g=new Galaxy("Pinwheel",
                "The Pinwheel Galaxy is a face-on spiral galaxy distanced 21 million light-years away from earth in the constellation Ursa Major. ",
                R.drawable.pinwheel);
        galaxies.add(g);

        g=new Galaxy("Canis Majos Overdensity",
                "The Canis Major Dwarf Galaxy or Canis Major Overdensity is a disputed dwarf irregular galaxy in the Local Group, located in the same part of the sky as the constellation Canis Major. ",
                R.drawable.canismajoroverdensity);
        galaxies.add(g);

        g=new Galaxy("Virgo Stella Stream",
                " Group, located in the same part of the sky as the constellation Canis Major. ",
                R.drawable.virgostellarstream);
        galaxies.add(g);

        return galaxies;
    }

    /**
     * Let's create a method to open our detail activity and pass our object.
     * @param galaxy
     */
    private void openDetailActivity(Galaxy galaxy)
    {
        Intent i=new Intent(this,DetailsActivity.class);
        i.putExtra("GALAXY_KEY",galaxy);
        startActivity(i);
    }

    /**
     * Let's implemenent our onItemClick method
     * @param parent
     * @param view
     * @param position
     * @param id
     */
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Galaxy g=getData().get(position);
        Toast.makeText(MainActivity.this,String.valueOf(position)+". "+ g.getName()+" Chosen.", Toast.LENGTH_SHORT).show();
        openDetailActivity(g);
    }

    /**
     * Let's override our onCreate callback
     * @param savedInstanceState
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mRecyclerView=findViewById(R.id.mRecylcerview);

        mAdapter = new RecyclerAdapter(this, getData());
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mAdapter);
        mAdapter.setOnItemClickListener(this);
    }
}
Download

You can download the full source code below or watch the video from the link provided.

No. Location Link
1. GitHub Direct Download
2. GitHub Browse
3. YouTube Video Tutorial
4. YouTube ProgrammingWizards TV Channel

How do You Feel after reading this?

According to scientists, we humans have 8 primary innate emotions: joy, acceptance, fear, surprise, sadness, disgust, anger, and anticipation. Feel free to tell us how you feel about this article using these emotes or via the comment section.

About Me.

After completing his Software Engineering bachelors program, Oclemy(Clement Ochieng) these days is a man of two lives. At day he works for a startup in Nairobi, Kenya. At night he works tirelessly on building ProgrammingWizards TV, a tv channel for student coders and this website to help share the source code. In between he practices Meditation and Self actualization to help him keep balance. He also likes going for long solo walks to connect more with nature.




Recommendations


What do You Think


Previous Post