Android Custom RecyclerView - with ExpandableLayout


Android Custom RecyclerView with ExpandableLayout

In this class we see how to turn a recyclerview into an ExpandableRecyclerView with expandablelayout. We have complete control over the look of our expandablelayout as it's not some black box just an ordinary recyclerview with an expandablelayout.

This implies that we use the normalRecyclerView.Adapter and RecyclerView.ViewHolder classes.

We will maintain our expandstates in a SparseBooleanArray, a data structure that maps integers into boolean values.

In this case both the title of the expandable textview will be a simple textview. The description will also be a textview however we will wrap it in an expandablelayout.

Video Tutorial(ProgrammingWizards TV Channel)

Well we have a video tutorial as an alternative to this. If you prefer tutorials like this one then it would be good you subscribe to our YouTube channel.

Basically we have a TV for programming where do daily tutorials especially android.

Android Custom ExpandableRecyclerView Example with ExpandableLayout

Let's look at a complete example.

Here's the portrait mode:

Android RecyclerView ExpandableLayout

Here's the landscape mode:

Android RecyclerView ExpandableLayout

Project Structure

Here's our project structure;

Android RecyclerView ExpandableLayout Project Structure

Here

Gradle Scripts

(a). build.gradle(App)

Here's our app level build.gradle file. We have the dependencies DSL where we add our dependencies.

This file is called app level build.gradle since it's located in the app folder of the project.

If you are using Android Studio version 3 and above use implementation keyword while if you are using a version less than 3 then still use the compile keyword.

Once you've modified this build.gradle file you have to sync your project. Android Studio will indeed prompt you to do so.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    implementation 'com.github.aakira:expandable-layout:[email protected]'
    implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
}

Java Code

Android apps can be mainly written in Java or Kotlin. These days however there are many frameworks like Flutter also which use languages like Dart.

In this class we are using Java programming language.

We will have these classes in our project.

(a). Pioneer.java

This is our Pioneers class, basically a data object to represent a single computer Pioneer.

This Pioneer has a name and a description. We will receive those two properties via the constructor and set them to our public fields which can then be accessed to retrieve them.

package info.camposha.mrexpandable;

public class Pioneer {
    public final String name ;
    public final String description;

    public Pioneer(String name , String description) {
        this.name = name  ;
        this.description = description;
    }
}
(b). MyRecyclerViewAdapter.java

This is our RecyclerView.Adapter class.

package info.camposha.mrexpandable;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.SparseBooleanArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.github.aakira.expandablelayout.ExpandableLayout;
import com.github.aakira.expandablelayout.ExpandableLayoutListenerAdapter;
import com.github.aakira.expandablelayout.ExpandableLinearLayout;
import com.github.aakira.expandablelayout.Utils;

import java.util.List;

public class MyRecyclerAdapter  extends RecyclerView.Adapter<MyRecyclerAdapter.ViewHolder> {

    private final List<Pioneer> pioneers;
    private Context context;
    //SparseBooleanArrays map integers to booleans.
    private SparseBooleanArray expandStates = new SparseBooleanArray();

    /**
     * Our ViewHolder class extends RecyclerView.ViewHolder
     */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView txtTitle, txtDescription;
        public RelativeLayout buttonLayout;
        /**
         * You must use the ExpandableLinearLayout in the recycler view.
         * The ExpandableRelativeLayout doesn't work.
         */
        public ExpandableLinearLayout expandableLayout;

        public ViewHolder(View v) {
            super(v);
            txtTitle = v.findViewById(R.id.txt_title);
            txtDescription = v.findViewById(R.id.txt_description);
            buttonLayout = v.findViewById(R.id.button);
            expandableLayout = v.findViewById(R.id.expandableLayout);
        }
    }

    /**
     * Toggle our ExpandableLayout state when clicked.
     * @param expandableLayout
     */
    private void onClickButton(final ExpandableLayout expandableLayout) {
        expandableLayout.toggle();
    }

    /**
     * Create Animation for our ExpandableLayout.
     * We use ObjectAnimator,a subclass of ValueAnimator that will provide us support for
     * animating properties on target objects.
     * @param target
     * @param from
     * @param to
     * @return
     */
    public ObjectAnimator createRotateAnimator(final View target, final float from, final float to) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(target, "rotation", from, to);
        animator.setDuration(300);
        animator.setInterpolator(Utils.createInterpolator(Utils.LINEAR_INTERPOLATOR));
        return animator;
    }

    /**
     * Our Adapter's constructor
     * @param pioneers - list of Pioneer objects
     */
    public MyRecyclerAdapter(final List<Pioneer> pioneers) {
        this.pioneers = pioneers;
        for (int i = 0; i < pioneers.size(); i++) {
            expandStates.append(i, false);
        }
    }

    /**
     * Inflate our Layout, pass the resultant view to our ViewHolder constructor
     * and return the ViewHolder instance
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public MyRecyclerAdapter.ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
        this.context = parent.getContext();
        return new ViewHolder(LayoutInflater.from(context)
                .inflate(R.layout.recycler_view_list, parent, false));
    }

    /**
     * Bind our data
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(final MyRecyclerAdapter.ViewHolder holder, final int position) {
        final Pioneer pioneer = pioneers.get(position);
        holder.setIsRecyclable(false);
        holder.txtTitle.setText(pioneer.name);
        holder.txtDescription.setText(pioneer.description);
        holder.itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.material_teal_500));
        holder.expandableLayout.setInRecyclerView(true);
        holder.expandableLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.gray_light));
        holder.expandableLayout.setInterpolator(Utils.createInterpolator(Utils.BOUNCE_INTERPOLATOR));
        holder.expandableLayout.setExpanded(expandStates.get(position));
        holder.expandableLayout.setListener(new ExpandableLayoutListenerAdapter() {
            @Override
            public void onPreOpen() {
                //pass target view, and two foating points
                createRotateAnimator(holder.buttonLayout, 0f, 180f).start();
                expandStates.put(position, true);
            }

            @Override
            public void onPreClose() {
                createRotateAnimator(holder.buttonLayout, 180f, 0f).start();
                expandStates.put(position, false);
            }
        });

        holder.buttonLayout.setRotation(expandStates.get(position) ? 180f : 0f);
        holder.buttonLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View v) {
                onClickButton(holder.expandableLayout);
            }
        });
    }
    /**
     * Return total items in our adapter
     * @return
     */
    @Override
    public int getItemCount() {
        return pioneers.size();
    }

}
(c). MainActivity.java
package info.camposha.mrexpandable;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {

    /**
     * Our onCreate method.
     * - @param savedInstanceState - a Bundle object to hold our Object state
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportActionBar().setTitle(MainActivity.class.getSimpleName());

        final RecyclerView recyclerView = findViewById(R.id.recyclerView);
        recyclerView.addItemDecoration(new DividerItemDecoration(this,0));
        recyclerView.setLayoutManager(new LinearLayoutManager(this));

        final List<Pioneer> pioneers = new ArrayList<>();
        pioneers.add(new Pioneer("0 Atanasoff John Vincent",
                "John V. Atanasoff is considered by many historians to be" +
                        "the inventor of the modern electronic computer. He was" +
                        "born October 4, 1903, in Hamilton, New York. As a young" +
                        "man, Atanasoff showed considerable interest in and a talent" +
                        "for electronics. His academic background (B.S. in electrical" +
                        "engineering, Florida State University, 1925; m.S. in mathematics, Iowa State College, 1926; and Ph.D. in experimental" +
                        "physics, University of Wisconsin, 1930) well equipped him" +
                        "for the design of computing devices.  "));
        pioneers.add(new Pioneer("1 Andreessen Marc",
                " marc Andreessen brought the World Wide Web and its" +
                        "wealth of information, graphics, and services to the desktop, setting the stage for the first “e-commerce” revolution" +
                        "of the later 1990s. As founder of Netscape, Andreessen also"));
        pioneers.add(new Pioneer("2 Amdahl Gene Myron",
                "gene Amdahl played a major role in designing and developing the mainframe computer that dominated data processing through the 1970s (see mainfRame). Amdahl was born" +
                        "on November 16, 1922, in Flandreau, South Dakota. After" +
                        "having his education interrupted by World War II, Amdahl" +
                        "received a B.S. from South Dakota State University in 1948" +
                        "and a Ph.D. in physics at the University of Wisconsin in" +
                        "1952. "));
        pioneers.add(new Pioneer("3 Aiken Howard",
                " Howard Hathaway Aiken was a pioneer in the development" +
                        "of automatic calculating machines. Born on march 8, 1900," +
                        "in Hoboken, New Jersey, he grew up in Indianapolis, Indiana, where he pursued his interest in electrical engineering" +
                        "by working at a utility company while in high school. He" +
                        "earned a B.A. in electrical engineering in 1923 at the University of Wisconsin. "));
        pioneers.add(new Pioneer("4 Babbage Charles",
                " Charles Babbage made wide-ranging applications of mathematics to a variety of fields including economics, social" +
                "statistics, and the operation of railroads and lighthouses." +
                "Babbage is best known, however, for having conceptualized" +
                "the key elements of the general-purpose computer about a" +
                "century before the dawn of electronic digital computing."));
        pioneers.add(new Pioneer("5 Bell, C. Gordon",
                "Chester gordon Bell (also known as gordon Bennet Bell)" +
                        "was born August 19, 1934, in Kirksville, missouri. As a" +
                        "young boy Bell worked in his father’s electrical contracting" +
                        "business, learning to repair appliances and wire circuits." +
                        "This work led naturally to an interest in electronics, and" +
                        "Bell studied electrical engineering at mIT, earning a B.S. in" +
                        "1956 and an m.S. in 1957. After graduation and a year spent" +
                        "as a Fulbright Scholar in Australia, Bell worked in the mIT" +
                        "Speech Computation Laboratory (see speech Recognition" +
                        "and synthesis). In 1960 he was invited to join the Digital" +
                        "Equipment Corporation (DEC) by founders Ken Olsen and" +
                        "Harlan Anderson. "));
        pioneers.add(new Pioneer("6 Berners-Lee Tim",
                "A graduate of Oxford University, Tim Berners-Lee created" +
                        "what would become the World Wide Web in 1989 while" +
                        "working at CERN, the giant European physics research" +
                        "institute. At CERN, he struggled with organizing the dozens of incompatible computer systems and software that\n" +
                        "had been brought to the labs by thousands of scientists" +
                        "from around the world. With existing systems each requiring a specialized access procedure, researchers had little\n" +
                        "hope of finding out what their colleagues were doing or of" +
                        "learning about existing software tools that might solve their" +
                        "problems.  "));
        pioneers.add(new Pioneer("7 Bezos, Jeffrey P.",
                " With its ability to display extensive information and interact" +
                        "with users, the World Wide Web of the mid-1990s clearly" +
                        "had commercial possibilities. But it was far from clear how" +
                        "traditional merchandising could be adapted to the online" +
                        "world, and how the strengths of the new medium could be" +
                        "translated into business advantages. In creating Amazon." +
                        "com, “the world’s largest bookstore,” Jeff Bezos would show" +
                        "how the Web could be used to deliver books and other merchandise to millions of consumers. "));
        recyclerView.setAdapter(new MyRecyclerAdapter(pioneers));
    }
}

Layout Resources

(a). activity_main.xml

This is our main activity's layout. It will contain our RecyclerView.

This layout will get inflated into the main activity's user interface. This will happen via the Activity's setContentView() method which will require us to pass it the layout.

We will do so inside the onCreate() method of Activity.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="info.camposha.mrexpandable.MainActivity">

    <android.support.v7.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical" />

</LinearLayout>
(b). recycler_view_list.xml

Our model layout. This layout will represent a single expandable item.

Our expandable item will have a textview as well as a description.

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

    <RelativeLayout
        android:id="@+id/button"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:gravity="center"
        >

        <View
            android:layout_width="12dp"
            android:layout_height="12dp"
            android:background="@drawable/triangle"
            />
    </RelativeLayout>

    <TextView
        android:id="@+id/txt_title"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:layout_alignParentTop="true"
        android:layout_toLeftOf="@id/button"
        android:gravity="left|fill_horizontal"
        android:padding="8dp"
        android:textColor="@color/white"
        android:textSize="30sp" />

    <com.github.aakira.expandablelayout.ExpandableLinearLayout
        android:id="@+id/expandableLayout"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:layout_below="@id/txt_title"
        android:orientation="vertical"
        app:ael_duration="400"
        app:ael_expanded="false"
        >

        <TextView
            android:id="@+id/txt_description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="left|fill_horizontal"
            android:padding="@dimen/margin_normal"
            android:text=" Let's have the description for each pioneer here."
            android:textColor="@color/dark"
            android:textSize="20sp" />
    </com.github.aakira.expandablelayout.ExpandableLinearLayout>
</RelativeLayout>

Value Resources

(a). styles.xml

Our application's style.

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/material_amber_A700</item>
        <item name="colorPrimaryDark">@color/material_amber_800</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>
(b). dimens.xml

Our dimensions.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="margin_normal">5dp</dimen>
</resources>

Download

You can download full source code below.

No. Location Link
1. GitHub Direct Download
2. GitHub Browse

Best Regards,

Oclemy.

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. This feedback helps us gauge our progress.

Help me Grow.

I set myself some growth ambitions I desire to achieve by this year's end regarding this website and my youtube channel. Am halfway. Help me reach them by:




Recommendations


What do You Think


Previous Post Next Post