{"id":1515,"date":"2022-10-14T14:06:07","date_gmt":"2022-10-14T14:06:07","guid":{"rendered":"https:\/\/infotheme.net\/blog\/?p=1515"},"modified":"2022-10-14T16:10:05","modified_gmt":"2022-10-14T16:10:05","slug":"android-creating-gmail-like-inbox-using-recyclerview","status":"publish","type":"post","link":"https:\/\/infotheme.net\/blog\/android-creating-gmail-like-inbox-using-recyclerview\/","title":{"rendered":"Android Creating Gmail Like Inbox using RecyclerView"},"content":{"rendered":"\n<p>Introduction of RecyclerView is the best thing ever happen to android world. You can create stunningly beautiful lists and grids using the RecyclerView. Lot of us are very familiar in rendering the basic lists when the UI is very simple. But it comes to more complex lists that contains multiple UI elements along with animations, not everybody can achieve the final output they are looking for.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/Android-Creating-Gmail-Like-Inbox-using-RecyclerView.png\"><img loading=\"lazy\" width=\"1024\" height=\"536\" src=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/Android-Creating-Gmail-Like-Inbox-using-RecyclerView-1024x536.png\" alt=\"Android Creating Gmail Like Inbox using RecyclerView\" class=\"wp-image-1521\" srcset=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/Android-Creating-Gmail-Like-Inbox-using-RecyclerView-1024x536.png 1024w, https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/Android-Creating-Gmail-Like-Inbox-using-RecyclerView-300x157.png 300w, https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/Android-Creating-Gmail-Like-Inbox-using-RecyclerView-768x402.png 768w, https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/Android-Creating-Gmail-Like-Inbox-using-RecyclerView.png 1200w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>This article aims to improvise your knowledge on RecyclerView by taking an example of Gmail style inbox that contains the complex list design with interactive animations.<\/p>\n\n\n\n<div id=\"toc_container\" class=\"no_bullets\"><p class=\"toc_title\">Contents<\/p><ul class=\"toc_list\"><li><a href=\"#1_Overview\"><span class=\"toc_number toc_depth_1\">1<\/span> 1. Overview<\/a><\/li><li><a href=\"#2_Sample_JSON_for_Inbox_Messages\"><span class=\"toc_number toc_depth_1\">2<\/span> 2. Sample JSON for Inbox Messages<\/a><\/li><li><a href=\"#3_Creating_New_Project\"><span class=\"toc_number toc_depth_1\">3<\/span> 3. Creating New Project<\/a><\/li><li><a href=\"#4_Adding_Retrofit_Fetching_JSON\"><span class=\"toc_number toc_depth_1\">4<\/span> 4. Adding Retrofit \u2013 Fetching JSON<\/a><\/li><li><a href=\"#5_Generating_Random_Material_Color\"><span class=\"toc_number toc_depth_1\">5<\/span> 5. Generating Random Material Color<\/a><\/li><li><a href=\"#6_Flip_Animation_using_Object_Animators\"><span class=\"toc_number toc_depth_1\">6<\/span> 6. Flip Animation using Object Animators<\/a><\/li><li><a href=\"#7_Finally_Rendering_the_Inbox_in_RecyclerView\"><span class=\"toc_number toc_depth_1\">7<\/span> 7. Finally Rendering the Inbox in RecyclerView<\/a><\/li><li><a href=\"#Whats_Next\"><span class=\"toc_number toc_depth_1\">8<\/span> Whats Next?<\/a><\/li><\/ul><\/div>\n<h2><span id=\"1_Overview\">1. Overview<\/span><\/h2>\n\n\n\n<p>The desired output like Gmail app can\u2019t be achieved just with the RecyclerView alone. It needs combination of other few android concepts. Overall we gonna use the below mentioned components to get the finest appearance and functionality.<\/p>\n\n\n\n<p><strong>&gt; RecyclerView<\/strong><br>The basic component required for this app is&nbsp;<strong>RecyclerView<\/strong> as our primary task is to display the data in list fashion. The appearance of the list is customized just like Gmail app displaying a thumbnail icon, three line message, timestamp and a star icon to mark the message as important.<\/p>\n\n\n\n<p><strong>&gt; SwipeRefreshLayout<\/strong><br>In order to refresh the inbox,&nbsp;SwipeRefreshLayout is wrapped around the RecyclerView. This article doesn\u2019t explains the persistence of the data. So the inbox will be reset to initial state up on refresh.<\/p>\n\n\n\n<p><strong>&gt; ActionMode<\/strong><br><a href=\"https:\/\/developer.android.com\/reference\/android\/view\/ActionMode.html\" data-type=\"URL\" data-id=\"https:\/\/developer.android.com\/reference\/android\/view\/ActionMode.html\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">ActionMode<\/a>&nbsp;is used to display the contextual toolbar when a row is long pressed in the list. This enables us to provide set of alternative toolbar icons when the recycler view is in multiple choice mode. Here we provide delete option to delete the selected messages.<\/p>\n\n\n\n<p><strong>&gt; Object Animators<\/strong><br><a href=\"https:\/\/developer.android.com\/reference\/android\/animation\/ObjectAnimator.html\" data-type=\"URL\" data-id=\"https:\/\/developer.android.com\/reference\/android\/animation\/ObjectAnimator.html\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">Object Animators<\/a>&nbsp;allows us to animate a target element. In this we use the object animators to perform the&nbsp;<strong>Flip Animation<\/strong>&nbsp;of list thumbnail icon when a row is long pressed.<\/p>\n\n\n\n<p><strong>&gt; Retrofit<\/strong><br>In a production app, all the inbox messages are dynamic i.e they are fetched from a&nbsp;<strong>REST<\/strong>&nbsp;API. To demonstrate that, I have used a JSON url to list the messages. We use&nbsp;<strong>Retrofit<\/strong> library to fetch and deserialize the JSON.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/demo-gif-compressed.gif\"><img loading=\"lazy\" width=\"750\" height=\"706\" src=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/demo-gif-compressed.gif\" alt=\"\" class=\"wp-image-1516\"\/><\/a><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2><span id=\"2_Sample_JSON_for_Inbox_Messages\">2. Sample JSON for Inbox Messages<\/span><\/h2>\n\n\n\n<p>I have created an endpoint which serves the inbox messages in a JSON format. The JSON contains the information like profile picture, from, subject, message, timestamp and other details necessary to render the list. In realtime this json should be generated from a database using any server side language.<\/p>\n\n\n\n<p><strong>https:\/\/demoapi.infotheme.net\/gmail\/json\/inbox.json<\/strong><\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n[\n  {\n    &quot;id&quot;: 1,\n    &quot;isImportant&quot;: false,\n    &quot;picture&quot;: &quot;https:\/\/demoapi.infotheme.net\/gmail\/json\/google.png&quot;,\n    &quot;from&quot;: &quot;Google Alerts&quot;,\n    &quot;subject&quot;: &quot;Google Alert - android&quot;,\n    &quot;message&quot;: &quot;Android N update is released to Nexus Family!&quot;,\n    &quot;timestamp&quot;: &quot;10:30 AM&quot;,\n    &quot;isRead&quot;: false\n  },\n  .\n  .\n  .\n]\n<\/pre>\n\n\n<h2><span id=\"3_Creating_New_Project\">3. Creating New Project<\/span><\/h2>\n\n\n\n<p>We\u2019ll start by creating new project in Android Studio and do the basic setup required. Below is the final project structure I have planned for this article. This post seems to be lengthy but trust me this will enhance your knowledge and you will see surprising results when you reach to the bottom.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-project-structure-1.png\"><img loading=\"lazy\" width=\"720\" height=\"694\" src=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-project-structure-1.png\" alt=\"\" class=\"wp-image-1518\" srcset=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-project-structure-1.png 720w, https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-project-structure-1-300x289.png 300w\" sizes=\"(max-width: 720px) 100vw, 720px\" \/><\/a><\/figure>\n\n\n\n<p><strong>1<\/strong>. Create a new project in Android Studio from&nbsp;<strong>File \u21d2 New Project<\/strong>&nbsp;and fill the project details. While creating the project, I have selected the&nbsp;<strong>Basic Activity<\/strong>&nbsp;as default activity to get the Toolbar, FAB and other elements.<\/p>\n\n\n\n<p><strong>2<\/strong>. Open&nbsp;<strong>build.gradle<\/strong>&nbsp;located under&nbsp;<strong>app<\/strong>&nbsp;module and add&nbsp;<strong>RecyclerView<\/strong>,&nbsp;<strong>Retrofit<\/strong>&nbsp;and&nbsp;<strong>Glide<\/strong>&nbsp;dependencies and Sync the project.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ndependencies {\n    compile fileTree(dir: &#039;libs&#039;, include: [&#039;*.jar&#039;])\n    androidTestCompile(&#039;com.android.support.test.espresso:espresso-core:2.2.2&#039;, {\n        exclude group: &#039;com.android.support&#039;, module: &#039;support-annotations&#039;\n    })\n    compile &#039;com.android.support:appcompat-v7:24.2.1&#039;\n    compile &#039;com.android.support.constraint:constraint-layout:1.0.0-alpha7&#039;\n    compile &#039;com.android.support:design:24.2.1&#039;\n    testCompile &#039;junit:junit:4.12&#039;\n \n    \/\/ RecyclerView\n    compile &#039;com.android.support:recyclerview-v7:24.2.1&#039;\n \n    \/\/ retrofit, gson\n    compile &#039;com.google.code.gson:gson:2.6.2&#039;\n    compile &#039;com.squareup.retrofit2:retrofit:2.0.2&#039;\n    compile &#039;com.squareup.retrofit2:converter-gson:2.0.2&#039;\n \n    \/\/ glide\n    compile &#039;com.github.bumptech.glide:glide:3.7.0&#039;\n}\n<\/pre>\n\n\n<p><strong>3<\/strong>. <strong>res<\/strong> folder contains all the necessary icons required for the RecyclerView and Toolbar.<\/p>\n\n\n\n<p><strong>4<\/strong>. Add the below&nbsp;<strong>colors<\/strong>,&nbsp;<strong>strings<\/strong>&nbsp;and&nbsp;<strong>dimens<\/strong>&nbsp;to respective files.<br><strong>colors.xml<\/strong><\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncolors.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;resources&gt;\n    &lt;color name=&quot;colorPrimary&quot;&gt;#db4437&lt;\/color&gt;\n    &lt;color name=&quot;colorPrimaryDark&quot;&gt;#b93221&lt;\/color&gt;\n    &lt;color name=&quot;colorAccent&quot;&gt;#FFFFFF&lt;\/color&gt;\n    &lt;color name=&quot;from&quot;&gt;#000000&lt;\/color&gt;\n    &lt;color name=&quot;subject&quot;&gt;#111111&lt;\/color&gt;\n    &lt;color name=&quot;timestamp&quot;&gt;#4285f4&lt;\/color&gt;\n    &lt;color name=&quot;message&quot;&gt;#7a7a7a&lt;\/color&gt;\n    &lt;color name=&quot;icon_tint_normal&quot;&gt;#7a7a7a&lt;\/color&gt;\n    &lt;color name=&quot;icon_tint_selected&quot;&gt;#fed776&lt;\/color&gt;\n    &lt;color name=&quot;row_activated&quot;&gt;#e0e0e0&lt;\/color&gt;\n    &lt;color name=&quot;bg_action_mode&quot;&gt;#757575&lt;\/color&gt;\n    &lt;color name=&quot;bg_circle_default&quot;&gt;#666666&lt;\/color&gt;\n&lt;\/resources&gt;\n<\/pre>\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ndimens.xml\n&lt;resources&gt;\n    &lt;dimen name=&quot;fab_margin&quot;&gt;16dp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;padding_list_row&quot;&gt;16dp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;messages_padding_left&quot;&gt;72dp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;icon_width_height&quot;&gt;40dp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;msg_text_primary&quot;&gt;16sp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;msg_text_secondary&quot;&gt;14sp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;icon_star&quot;&gt;25dp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;icon_text&quot;&gt;22dp&lt;\/dimen&gt;\n    &lt;dimen name=&quot;timestamp&quot;&gt;12dp&lt;\/dimen&gt;\n&lt;\/resources&gt;\n<\/pre>\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nstrings.xml\n&lt;resources&gt;\n    &lt;string name=&quot;app_name&quot;&gt;Gmail&lt;\/string&gt;\n    &lt;string name=&quot;action_settings&quot;&gt;Settings&lt;\/string&gt;\n    &lt;string name=&quot;action_search&quot;&gt;Search&lt;\/string&gt;\n    &lt;string name=&quot;action_delete&quot;&gt;Delete&lt;\/string&gt;\n&lt;\/resources&gt;\n<\/pre>\n\n\n<p><strong>5<\/strong>. Open&nbsp;<strong>styles.xml<\/strong>&nbsp;and add the below styles. Here&nbsp;<em>windowActionModeOverlay<\/em>&nbsp;is added to overlap the ActionMode onto Toolbar.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nstyles.xml\n&lt;resources&gt;\n \n    &lt;!-- Base application theme. --&gt;\n    &lt;style name=&quot;AppTheme&quot; parent=&quot;Theme.AppCompat.Light.DarkActionBar&quot;&gt;\n        &lt;!-- Customize your theme here. --&gt;\n        &lt;item name=&quot;colorPrimary&quot;&gt;@color\/colorPrimary&lt;\/item&gt;\n        &lt;item name=&quot;colorPrimaryDark&quot;&gt;@color\/colorPrimaryDark&lt;\/item&gt;\n        &lt;item name=&quot;colorAccent&quot;&gt;@color\/colorAccent&lt;\/item&gt;\n    &lt;\/style&gt;\n \n    &lt;style name=&quot;AppTheme.NoActionBar&quot;&gt;\n        &lt;item name=&quot;windowActionBar&quot;&gt;false&lt;\/item&gt;\n        &lt;item name=&quot;windowNoTitle&quot;&gt;true&lt;\/item&gt;\n        &lt;item name=&quot;windowActionModeOverlay&quot;&gt;true&lt;\/item&gt;\n        &lt;item name=&quot;android:actionModeBackground&quot;&gt;@color\/bg_action_mode&lt;\/item&gt;\n    &lt;\/style&gt;\n \n    &lt;style name=&quot;AppTheme.AppBarOverlay&quot; parent=&quot;ThemeOverlay.AppCompat.Dark.ActionBar&quot; \/&gt;\n \n    &lt;style name=&quot;AppTheme.PopupOverlay&quot; parent=&quot;ThemeOverlay.AppCompat.Light&quot; \/&gt;\n&lt;\/resources&gt;\n<\/pre>\n\n\n<p><strong>6<\/strong>. As we are going to make network calls we need&nbsp;<em>INTERNET<\/em>&nbsp;permission in the manifest file. Open&nbsp;<strong>AndroidManifest.xml<\/strong>&nbsp;and add the permission.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nAndroidManifest.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;manifest xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    package=&quot;net.infotheme.gmail&quot;&gt;\n \n    &lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; \/&gt;\n \n    &lt;application\n        android:allowBackup=&quot;true&quot;\n        android:icon=&quot;@mipmap\/ic_launcher&quot;\n        android:label=&quot;@string\/app_name&quot;\n        android:supportsRtl=&quot;true&quot;\n        android:theme=&quot;@style\/AppTheme&quot;&gt;\n        &lt;activity\n            android:name=&quot;.activity.MainActivity&quot;\n            android:label=&quot;@string\/app_name&quot;\n            android:theme=&quot;@style\/AppTheme.NoActionBar&quot;&gt;\n            &lt;intent-filter&gt;\n                &lt;action android:name=&quot;android.intent.action.MAIN&quot; \/&gt;\n \n                &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; \/&gt;\n            &lt;\/intent-filter&gt;\n        &lt;\/activity&gt;\n    &lt;\/application&gt;\n&lt;\/manifest&gt;\n<\/pre>\n\n\n<p><strong>7<\/strong>. Create five packages named&nbsp;<strong>activity<\/strong>,&nbsp;<strong>adapter<\/strong>,&nbsp;<strong>helper<\/strong>,&nbsp;<strong>model<\/strong>&nbsp;and&nbsp;<strong>network<\/strong>. We use these packages to keep the project organized. Once the packages are created, move your&nbsp;<strong>MainActivity<\/strong>&nbsp;to&nbsp;<strong>activity<\/strong>&nbsp;package.<\/p>\n\n\n\n<h2><span id=\"4_Adding_Retrofit_Fetching_JSON\">4. Adding Retrofit \u2013 Fetching JSON<\/span><\/h2>\n\n\n\n<p>Now our project have the basic resources ready. Let\u2019s add the network layer by using the Retrofit library. If you are new to Retrofit, I strongly suggest you go through my previous article about&nbsp;<a href=\"https:\/\/infotheme.net\/blog\/android-working-with-retrofit-http-library\/\" data-type=\"URL\" data-id=\"https:\/\/infotheme.net\/blog\/android-working-with-retrofit-http-library\/\" target=\"_blank\" rel=\"noreferrer noopener\">Retrofit<\/a>.<\/p>\n\n\n\n<p><strong>8<\/strong>. Under&nbsp;<strong>model<\/strong>&nbsp;package, create a class named&nbsp;<strong>Message.java<\/strong>. This POJO class is used to deserialize the json while parsing.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nMessage.java\npackage net.infotheme.gmail.model;\n \npublic class Message {\n    private int id;\n    private String from;\n    private String subject;\n    private String message;\n    private String timestamp;\n    private String picture;\n    private boolean isImportant;\n    private boolean isRead;\n    private int color = -1;\n \n    public Message() {\n    }\n \n    public int getId() {\n        return id;\n    }\n \n    public void setId(int id) {\n        this.id = id;\n    }\n \n    public String getFrom() {\n        return from;\n    }\n \n    public void setFrom(String from) {\n        this.from = from;\n    }\n \n    public String getSubject() {\n        return subject;\n    }\n \n    public void setSubject(String subject) {\n        this.subject = subject;\n    }\n \n    public String getMessage() {\n        return message;\n    }\n \n    public void setMessage(String message) {\n        this.message = message;\n    }\n \n    public String getTimestamp() {\n        return timestamp;\n    }\n \n    public void setTimestamp(String timestamp) {\n        this.timestamp = timestamp;\n    }\n \n    public boolean isImportant() {\n        return isImportant;\n    }\n \n    public void setImportant(boolean important) {\n        isImportant = important;\n    }\n \n    public String getPicture() {\n        return picture;\n    }\n \n    public void setPicture(String picture) {\n        this.picture = picture;\n    }\n \n    public boolean isRead() {\n        return isRead;\n    }\n \n    public void setRead(boolean read) {\n        isRead = read;\n    }\n \n    public int getColor() {\n        return color;\n    }\n \n    public void setColor(int color) {\n        this.color = color;\n    }\n}\n<\/pre>\n\n\n<p><strong>9<\/strong>. Under&nbsp;<strong>network<\/strong>&nbsp;package, create a new class named&nbsp;<strong>ApiClient.java<\/strong>. This class creates the static retrofit instance.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nApiClient.java\npackage net.infotheme.gmail.network;\n \nimport retrofit2.Retrofit;\nimport retrofit2.converter.gson.GsonConverterFactory;\n \n \npublic class ApiClient {\n    public static final String BASE_URL = &quot;https:\/\/demoapi.infotheme.net\/gmail\/json\/&quot;;\n    private static Retrofit retrofit = null;\n     \n    public static Retrofit getClient() {\n        if (retrofit == null) {\n            retrofit = new Retrofit.Builder()\n                    .baseUrl(BASE_URL)\n                    .addConverterFactory(GsonConverterFactory.create())\n                    .build();\n        }\n        return retrofit;\n    }\n}\n<\/pre>\n\n\n<p><strong>10<\/strong>. Under&nbsp;<strong>network<\/strong>&nbsp;package, create a new class named&nbsp;<strong>ApiInterface.java<\/strong>. This class contains the rest api endpoints and the type of response it is expecting. In our case we have only one endpoint i.e&nbsp;<strong>inbox.json<\/strong><\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nApiInterface.java\npackage net.infotheme.gmail.network;\n \nimport java.util.List;\n \nimport net.infotheme.gmail.model.Message;\nimport retrofit2.Call;\nimport retrofit2.http.GET;\n \npublic interface ApiInterface {\n \n    @GET(&quot;inbox.json&quot;)\n    Call&lt;List&lt;Message&gt;&gt; getInbox();\n \n}\n<\/pre>\n\n\n<p>This completes the retrofit integration. Now let\u2019s add few helper classes those helps in rendering the list.<\/p>\n\n\n\n<p><strong>11<\/strong>. Under&nbsp;<strong>helper<\/strong>&nbsp;package, create a class named&nbsp;<strong>CircleTransform.java<\/strong>. This class is used to display the thumbnail image in&nbsp;<strong>circular shape<\/strong>&nbsp;using the&nbsp;<strong>Glide<\/strong>&nbsp;library.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nCircleTransform.java\npackage net.infotheme.gmail.helper;\n \nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapShader;\nimport android.graphics.Canvas;\nimport android.graphics.Paint;\n \nimport com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;\nimport com.bumptech.glide.load.resource.bitmap.BitmapTransformation;\n \npublic class CircleTransform  extends BitmapTransformation {\n    public CircleTransform(Context context) {\n        super(context);\n    }\n \n    @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {\n        return circleCrop(pool, toTransform);\n    }\n \n    private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {\n        if (source == null) return null;\n \n        int size = Math.min(source.getWidth(), source.getHeight());\n        int x = (source.getWidth() - size) \/ 2;\n        int y = (source.getHeight() - size) \/ 2;\n \n        \/\/ TODO this could be acquired from the pool too\n        Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);\n \n        Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);\n        if (result == null) {\n            result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);\n        }\n \n        Canvas canvas = new Canvas(result);\n        Paint paint = new Paint();\n        paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));\n        paint.setAntiAlias(true);\n        float r = size \/ 2f;\n        canvas.drawCircle(r, r, r, paint);\n        return result;\n    }\n \n    @Override public String getId() {\n        return getClass().getName();\n    }\n}\n<\/pre>\n\n\n<p><strong>12<\/strong>. Under&nbsp;<strong>helper<\/strong>&nbsp;package, create another class named&nbsp;<strong>DividerItemDecoration.java<\/strong>. This helps in adding divider lines in recycler view.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nDividerItemDecoration\npackage net.infotheme.gmail.helper;\n \nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\n \npublic class DividerItemDecoration extends RecyclerView.ItemDecoration {\n \n    private static final int[] ATTRS = new int[]{\n            android.R.attr.listDivider\n    };\n \n    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;\n    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;\n    private Drawable mDivider;\n    private int mOrientation;\n \n    public DividerItemDecoration(Context context, int orientation) {\n        final TypedArray a = context.obtainStyledAttributes(ATTRS);\n        mDivider = a.getDrawable(0);\n        a.recycle();\n        setOrientation(orientation);\n    }\n \n    public void setOrientation(int orientation) {\n        if (orientation != HORIZONTAL_LIST &amp;amp;&amp;amp; orientation != VERTICAL_LIST) {\n            throw new IllegalArgumentException(&quot;invalid orientation&quot;);\n        }\n        mOrientation = orientation;\n    }\n \n    @Override\n    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {\n        if (mOrientation == VERTICAL_LIST) {\n            drawVertical(c, parent);\n        } else {\n            drawHorizontal(c, parent);\n        }\n    }\n \n    public void drawVertical(Canvas c, RecyclerView parent) {\n        final int left = parent.getPaddingLeft();\n        final int right = parent.getWidth() - parent.getPaddingRight();\n \n        final int childCount = parent.getChildCount();\n        for (int i = 0; i &lt; childCount; i++) {\n            final View child = parent.getChildAt(i);\n            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child\n                    .getLayoutParams();\n            final int top = child.getBottom() + params.bottomMargin;\n            final int bottom = top + mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(c);\n        }\n    }\n \n    public void drawHorizontal(Canvas c, RecyclerView parent) {\n        final int top = parent.getPaddingTop();\n        final int bottom = parent.getHeight() - parent.getPaddingBottom();\n \n        final int childCount = parent.getChildCount();\n        for (int i = 0; i &lt; childCount; i++) {\n            final View child = parent.getChildAt(i);\n            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child\n                    .getLayoutParams();\n            final int left = child.getRight() + params.rightMargin;\n            final int right = left + mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(c);\n        }\n    }\n \n    @Override\n    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n        if (mOrientation == VERTICAL_LIST) {\n            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());\n        } else {\n            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);\n        }\n    }\n}\n<\/pre>\n\n\n<h2><span id=\"5_Generating_Random_Material_Color\">5. Generating Random Material Color<\/span><\/h2>\n\n\n\n<p>Another exciting thing you would come across here is assigning a random background color to each row icon. To achieve this we need to predefine set of material colors in an array and choose a random color while RecyclerView is prepared. Thanks to&nbsp;<a href=\"https:\/\/gist.github.com\/daniellevass\/b0b8cfa773488e138037\" data-type=\"URL\" data-id=\"https:\/\/gist.github.com\/daniellevass\/b0b8cfa773488e138037\" target=\"_blank\" rel=\"noreferrer noopener nofollow\">daniellevass<\/a>&nbsp;for providing such an useful color codes.<\/p>\n\n\n\n<p><strong>13<\/strong>. Create an xml named&nbsp;<strong>array.xml<\/strong>&nbsp;under&nbsp;<strong>res \u21d2 values<\/strong>. This xml contains few material colors thouse would be loaded randomly in list.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\narray.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;resources&gt;\n    &lt;array name=&quot;mdcolor_400&quot;&gt;\n        &lt;item name=&quot;red_400&quot; type=&quot;color&quot;&gt;#e84e40&lt;\/item&gt;\n        &lt;item name=&quot;pink_400&quot; type=&quot;color&quot;&gt;#ec407a&lt;\/item&gt;\n        &lt;item name=&quot;purple_400&quot; type=&quot;color&quot;&gt;#ab47bc&lt;\/item&gt;\n        &lt;item name=&quot;deep_purple_400&quot; type=&quot;color&quot;&gt;#7e57c2&lt;\/item&gt;\n        &lt;item name=&quot;indigo_400&quot; type=&quot;color&quot;&gt;#5c6bc0&lt;\/item&gt;\n        &lt;item name=&quot;blue_400&quot; type=&quot;color&quot;&gt;#738ffe&lt;\/item&gt;\n        &lt;item name=&quot;light_blue_400&quot; type=&quot;color&quot;&gt;#29b6f6&lt;\/item&gt;\n        &lt;item name=&quot;cyan_400&quot; type=&quot;color&quot;&gt;#26c6da&lt;\/item&gt;\n        &lt;item name=&quot;teal_400&quot; type=&quot;color&quot;&gt;#26a69a&lt;\/item&gt;\n        &lt;item name=&quot;green_400&quot; type=&quot;color&quot;&gt;#2baf2b&lt;\/item&gt;\n        &lt;item name=&quot;light_green_400&quot; type=&quot;color&quot;&gt;#9ccc65&lt;\/item&gt;\n        &lt;item name=&quot;lime_400&quot; type=&quot;color&quot;&gt;#d4e157&lt;\/item&gt;\n        &lt;item name=&quot;yellow_400&quot; type=&quot;color&quot;&gt;#ffee58&lt;\/item&gt;\n        &lt;item name=&quot;orange_400&quot; type=&quot;color&quot;&gt;#ffa726&lt;\/item&gt;\n        &lt;item name=&quot;deep_orange_400&quot; type=&quot;color&quot;&gt;#ff7043&lt;\/item&gt;\n        &lt;item name=&quot;brown_400&quot; type=&quot;color&quot;&gt;#8d6e63&lt;\/item&gt;\n        &lt;item name=&quot;grey_400&quot; type=&quot;color&quot;&gt;#bdbdbd&lt;\/item&gt;\n        &lt;item name=&quot;blue_grey_400&quot; type=&quot;color&quot;&gt;#78909c&lt;\/item&gt;\n    &lt;\/array&gt;\n    &lt;array name=&quot;mdcolor_500&quot;&gt;\n        &lt;item name=&quot;red_500&quot; type=&quot;color&quot;&gt;#e51c23&lt;\/item&gt;\n        &lt;item name=&quot;pink_500&quot; type=&quot;color&quot;&gt;#e91e63&lt;\/item&gt;\n        &lt;item name=&quot;purple_500&quot; type=&quot;color&quot;&gt;#9c27b0&lt;\/item&gt;\n        &lt;item name=&quot;deep_purple_500&quot; type=&quot;color&quot;&gt;#673ab7&lt;\/item&gt;\n        &lt;item name=&quot;indigo_500&quot; type=&quot;color&quot;&gt;#3f51b5&lt;\/item&gt;\n        &lt;item name=&quot;blue_500&quot; type=&quot;color&quot;&gt;#5677fc&lt;\/item&gt;\n        &lt;item name=&quot;light_blue_500&quot; type=&quot;color&quot;&gt;#03a9f4&lt;\/item&gt;\n        &lt;item name=&quot;cyan_500&quot; type=&quot;color&quot;&gt;#00bcd4&lt;\/item&gt;\n        &lt;item name=&quot;teal_500&quot; type=&quot;color&quot;&gt;#009688&lt;\/item&gt;\n        &lt;item name=&quot;green_500&quot; type=&quot;color&quot;&gt;#259b24&lt;\/item&gt;\n        &lt;item name=&quot;light_green_500&quot; type=&quot;color&quot;&gt;#8bc34a&lt;\/item&gt;\n        &lt;item name=&quot;lime_500&quot; type=&quot;color&quot;&gt;#cddc39&lt;\/item&gt;\n        &lt;item name=&quot;yellow_500&quot; type=&quot;color&quot;&gt;#ffeb3b&lt;\/item&gt;\n        &lt;item name=&quot;orange_500&quot; type=&quot;color&quot;&gt;#ff9800&lt;\/item&gt;\n        &lt;item name=&quot;deep_orange_500&quot; type=&quot;color&quot;&gt;#ff5722&lt;\/item&gt;\n        &lt;item name=&quot;brown_500&quot; type=&quot;color&quot;&gt;#795548&lt;\/item&gt;\n        &lt;item name=&quot;grey_500&quot; type=&quot;color&quot;&gt;#9e9e9e&lt;\/item&gt;\n        &lt;item name=&quot;blue_grey_500&quot; type=&quot;color&quot;&gt;#607d8b&lt;\/item&gt;\n    &lt;\/array&gt;\n&lt;\/resources&gt;\n<\/pre>\n\n\n<p>To load these colors randomly, the following function can be used. You will see how to use this function shortly.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nprivate int getRandomMaterialColor(String typeColor) {\n    int returnColor = Color.GRAY;\n    int arrayId = getResources().getIdentifier(&quot;mdcolor_&quot; + typeColor, &quot;array&quot;, getPackageName());\n \n    if (arrayId != 0) {\n        TypedArray colors = getResources().obtainTypedArray(arrayId);\n        int index = (int) (Math.random() * colors.length());\n        returnColor = colors.getColor(index, Color.GRAY);\n        colors.recycle();\n    }\n    return returnColor;\n}\n<\/pre>\n\n\n<h2><span id=\"6_Flip_Animation_using_Object_Animators\">6. Flip Animation using Object Animators<\/span><\/h2>\n\n\n\n<p>If you observe the gmail app, when you long press and selects a row, the thumbnail icon will be animated in a flip motion showing other side of the icon. We can do the same using the&nbsp;<strong>ObjectAnimator<\/strong>&nbsp;concepts. Carefully create the below mentioned files in your project.<\/p>\n\n\n\n<p><strong>14<\/strong>. Under&nbsp;<strong>res \u21d2 values<\/strong>, create an xml named&nbsp;<strong>integer.xml<\/strong>. Here we define the animation durations.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ninteger.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;resources&gt;\n    &lt;integer name=&quot;card_flip_time_full&quot;&gt;500&lt;\/integer&gt;\n    &lt;integer name=&quot;card_flip_time_half&quot;&gt;200&lt;\/integer&gt;\n&lt;\/resources&gt;\n<\/pre>\n\n\n<p><strong>15<\/strong>. Create a folder named&nbsp;<strong>animator<\/strong>&nbsp;under&nbsp;<strong>res<\/strong>&nbsp;folder. In this directory we place all the xml resources related to animations.<\/p>\n\n\n\n<p><strong>16<\/strong>. Under\u00a0<strong>animator<\/strong>, create four xml files named\u00a0<strong>card_flip_left_in.xml<\/strong>,\u00a0<\/p>\n\n\n\n<p><strong>card_flip_left_out.xml<\/strong>,\u00a0<\/p>\n\n\n\n<p><strong>card_flip_right_in.xml<\/strong>\u00a0<\/p>\n\n\n\n<p>and\u00a0<strong>card_flip_right_out.xml<\/strong>.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncard_flip_left_in.xml\n&lt;set xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;&gt;\n    &lt;!-- Before rotating, immediately set the alpha to 0. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;1.0&quot;\n        android:valueTo=&quot;0.0&quot;\n        android:propertyName=&quot;alpha&quot;\n        android:duration=&quot;0&quot; \/&gt;\n \n    &lt;!-- Rotate. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;-180&quot;\n        android:valueTo=&quot;0&quot;\n        android:propertyName=&quot;rotationY&quot;\n        android:interpolator=&quot;@android:interpolator\/accelerate_decelerate&quot;\n        android:duration=&quot;@integer\/card_flip_time_full&quot; \/&gt;\n \n    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;0.0&quot;\n        android:valueTo=&quot;1.0&quot;\n        android:propertyName=&quot;alpha&quot;\n        android:startOffset=&quot;@integer\/card_flip_time_half&quot;\n        android:duration=&quot;1&quot; \/&gt;\n&lt;\/set&gt;\n<\/pre>\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncard_flip_left_out.xml\n&lt;set xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;&gt;\n    &lt;!-- Rotate. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;0&quot;\n        android:valueTo=&quot;180&quot;\n        android:propertyName=&quot;rotationY&quot;\n        android:interpolator=&quot;@android:interpolator\/accelerate_decelerate&quot;\n        android:duration=&quot;@integer\/card_flip_time_full&quot; \/&gt;\n \n    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;1.0&quot;\n        android:valueTo=&quot;0.0&quot;\n        android:propertyName=&quot;alpha&quot;\n        android:startOffset=&quot;@integer\/card_flip_time_half&quot;\n        android:duration=&quot;1&quot; \/&gt;\n&lt;\/set&gt;\n<\/pre>\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncard_flip_right_in.xml\n&lt;set xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;&gt;\n    &lt;!-- Before rotating, immediately set the alpha to 0. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;1.0&quot;\n        android:valueTo=&quot;0.0&quot;\n        android:propertyName=&quot;alpha&quot;\n        android:duration=&quot;0&quot; \/&gt;\n \n    &lt;!-- Rotate. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;180&quot;\n        android:valueTo=&quot;0&quot;\n        android:propertyName=&quot;rotationY&quot;\n        android:interpolator=&quot;@android:interpolator\/accelerate_decelerate&quot;\n        android:duration=&quot;@integer\/card_flip_time_full&quot; \/&gt;\n \n    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 1. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;0.0&quot;\n        android:valueTo=&quot;1.0&quot;\n        android:propertyName=&quot;alpha&quot;\n        android:startOffset=&quot;@integer\/card_flip_time_half&quot;\n        android:duration=&quot;1&quot; \/&gt;\n&lt;\/set&gt;\n<\/pre>\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncard_flip_right_out.xml\n&lt;set xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;&gt;\n    &lt;!-- Rotate. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;0&quot;\n        android:valueTo=&quot;-180&quot;\n        android:propertyName=&quot;rotationY&quot;\n        android:interpolator=&quot;@android:interpolator\/accelerate_decelerate&quot;\n        android:duration=&quot;@integer\/card_flip_time_full&quot; \/&gt;\n \n    &lt;!-- Half-way through the rotation (see startOffset), set the alpha to 0. --&gt;\n    &lt;objectAnimator\n        android:valueFrom=&quot;1.0&quot;\n        android:valueTo=&quot;0.0&quot;\n        android:propertyName=&quot;alpha&quot;\n        android:startOffset=&quot;@integer\/card_flip_time_half&quot;\n        android:duration=&quot;1&quot; \/&gt;\n&lt;\/set&gt;\n<\/pre>\n\n\n<p><strong>17<\/strong>. Under&nbsp;<strong>helper<\/strong>&nbsp;package, create a class named&nbsp;<strong>FlipAnimator.java<\/strong>. This class has a static method&nbsp;<strong>flipView()<\/strong>&nbsp;which performs the flip animation.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nFlipAnimator.java\npackage net.infotheme.gmail.helper;\n \nimport android.animation.AnimatorInflater;\nimport android.animation.AnimatorSet;\nimport android.content.Context;\nimport android.view.View;\n \nimport net.infotheme.gmail.R;\n \npublic class FlipAnimator {\n    private static String TAG = FlipAnimator.class.getSimpleName();\n    private static AnimatorSet leftIn, rightOut, leftOut, rightIn;\n \n    \/**\n     * Performs flip animation on two views\n     *\/\n    public static void flipView(Context context, final View back, final View front, boolean showFront) {\n        leftIn = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.animator.card_flip_left_in);\n        rightOut = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.animator.card_flip_right_out);\n        leftOut = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.animator.card_flip_left_out);\n        rightIn = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.animator.card_flip_right_in);\n \n        final AnimatorSet showFrontAnim = new AnimatorSet();\n        final AnimatorSet showBackAnim = new AnimatorSet();\n \n        leftIn.setTarget(back);\n        rightOut.setTarget(front);\n        showFrontAnim.playTogether(leftIn, rightOut);\n \n        leftOut.setTarget(back);\n        rightIn.setTarget(front);\n        showBackAnim.playTogether(rightIn, leftOut);\n \n        if (showFront) {\n            showFrontAnim.start();\n        } else {\n            showBackAnim.start();\n        }\n    }\n}\n<\/pre>\n\n\n<h2><span id=\"7_Finally_Rendering_the_Inbox_in_RecyclerView\">7. Finally Rendering the Inbox in RecyclerView<\/span><\/h2>\n\n\n\n<p>Oooh, finally we have reached to core part of the article i.e rendering the actual list data. Have some more patience \ud83d\ude42 and follow along with me.<\/p>\n\n\n\n<p>Now let\u2019s create the few remaining files required for the RecyclerView. All we need is xml layouts for main activity, list row, background drawables and an adapter class.<\/p>\n\n\n\n<p><strong>18<\/strong>. Under&nbsp;<strong>res \u21d2 drawable<\/strong>, create two drawable resources named&nbsp;<strong>bg_circle.xml<\/strong>&nbsp;and&nbsp;<strong>bg_list_row.xml<\/strong>.<\/p>\n\n\n\n<p><strong>bg_circle.xml<\/strong>&nbsp;(This provides solid circular background color to thumbnail icon)<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nbg_circle.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;shape\n    xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    android:shape=&quot;oval&quot;&gt;\n \n    &lt;solid\n        android:color=&quot;@color\/bg_circle_default&quot;\/&gt;\n \n    &lt;size\n        android:width=&quot;120dp&quot;\n        android:height=&quot;120dp&quot;\/&gt;\n&lt;\/shape&gt;\n<\/pre>\n\n\n<p><strong>bg_list_row.xml<\/strong>&nbsp;(Provides background color to list item with normal and active states)<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nbg_list_row.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;selector xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;&gt;\n    &lt;item android:drawable=&quot;@color\/row_activated&quot; android:state_activated=&quot;true&quot; \/&gt;\n    &lt;item android:drawable=&quot;@android:color\/transparent&quot; \/&gt;\n&lt;\/selector&gt;\n<\/pre>\n\n\n<p><strong>19<\/strong>. Open the layout file of your main activity (<strong>content_main.xml<\/strong>) and add the RecyclerView element.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nactivity_main.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;android.support.design.widget.CoordinatorLayout xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:app=&quot;http:\/\/schemas.android.com\/apk\/res-auto&quot;\n    xmlns:tools=&quot;http:\/\/schemas.android.com\/tools&quot;\n    android:layout_width=&quot;match_parent&quot;\n    android:layout_height=&quot;match_parent&quot;\n    tools:mContext=&quot;net.infotheme.gmail.activity.MainActivity&quot;&gt;\n \n    &lt;android.support.design.widget.AppBarLayout\n        android:layout_width=&quot;match_parent&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:theme=&quot;@style\/AppTheme.AppBarOverlay&quot;&gt;\n \n        &lt;android.support.v7.widget.Toolbar\n            android:id=&quot;@+id\/toolbar&quot;\n            android:layout_width=&quot;match_parent&quot;\n            android:layout_height=&quot;?attr\/actionBarSize&quot;\n            android:background=&quot;?attr\/colorPrimary&quot;\n            app:popupTheme=&quot;@style\/AppTheme.PopupOverlay&quot; \/&gt;\n \n    &lt;\/android.support.design.widget.AppBarLayout&gt;\n \n    &lt;include layout=&quot;@layout\/content_main&quot; \/&gt;\n \n    &lt;android.support.design.widget.FloatingActionButton\n        android:id=&quot;@+id\/fab&quot;\n        android:layout_width=&quot;wrap_content&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:layout_gravity=&quot;bottom|end&quot;\n        android:layout_margin=&quot;@dimen\/fab_margin&quot;\n        app:backgroundTint=&quot;@color\/colorPrimary&quot;\n        app:srcCompat=&quot;@drawable\/ic_edit_white_24dp&quot; \/&gt;\n \n&lt;\/android.support.design.widget.CoordinatorLayout&gt;\n<\/pre>\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncontent_main.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;android.support.constraint.ConstraintLayout xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:app=&quot;http:\/\/schemas.android.com\/apk\/res-auto&quot;\n    xmlns:tools=&quot;http:\/\/schemas.android.com\/tools&quot;\n    android:layout_width=&quot;match_parent&quot;\n    android:layout_height=&quot;match_parent&quot;\n    app:layout_behavior=&quot;@string\/appbar_scrolling_view_behavior&quot;\n    tools:mContext=&quot;net.infotheme.gmail.activity.MainActivity&quot;\n    tools:showIn=&quot;@layout\/activity_main&quot;&gt;\n \n    &lt;android.support.v4.widget.SwipeRefreshLayout\n        android:id=&quot;@+id\/swipe_refresh_layout&quot;\n        android:layout_width=&quot;match_parent&quot;\n        android:layout_height=&quot;wrap_content&quot;&gt;\n \n        &lt;android.support.v7.widget.RecyclerView\n            android:id=&quot;@+id\/recycler_view&quot;\n            android:layout_width=&quot;match_parent&quot;\n            android:layout_height=&quot;match_parent&quot;\n            android:scrollbars=&quot;vertical&quot; \/&gt;\n \n    &lt;\/android.support.v4.widget.SwipeRefreshLayout&gt;\n \n&lt;\/android.support.constraint.ConstraintLayout&gt;\n<\/pre>\n\n\n<p><strong>20<\/strong>. Under&nbsp;<strong>res \u21d2 layout<\/strong>, create an xml layout named&nbsp;<strong>message_list_row.xml<\/strong>&nbsp;with below code. This layout will renders each row in recycler view. The actual customization of recycler view row happens here.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmessage_list_row.xml\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;RelativeLayout xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    android:layout_width=&quot;match_parent&quot;\n    android:layout_height=&quot;wrap_content&quot;\n    android:background=&quot;@drawable\/bg_list_row&quot;\n    android:clickable=&quot;true&quot;\n    android:focusable=&quot;true&quot;\n    android:orientation=&quot;vertical&quot;\n    android:paddingBottom=&quot;@dimen\/padding_list_row&quot;\n    android:paddingLeft=&quot;?listPreferredItemPaddingLeft&quot;\n    android:paddingRight=&quot;?listPreferredItemPaddingRight&quot;\n    android:paddingTop=&quot;@dimen\/padding_list_row&quot;&gt;\n \n    &lt;LinearLayout\n        android:id=&quot;@+id\/message_container&quot;\n        android:layout_width=&quot;wrap_content&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:clickable=&quot;true&quot;\n        android:orientation=&quot;vertical&quot;\n        android:paddingLeft=&quot;72dp&quot;\n        android:paddingRight=&quot;@dimen\/padding_list_row&quot;&gt;\n \n        &lt;TextView\n            android:id=&quot;@+id\/from&quot;\n            android:layout_width=&quot;match_parent&quot;\n            android:layout_height=&quot;wrap_content&quot;\n            android:ellipsize=&quot;end&quot;\n            android:lines=&quot;1&quot;\n            android:textColor=&quot;@color\/from&quot;\n            android:textSize=&quot;@dimen\/msg_text_primary&quot;\n            android:textStyle=&quot;bold&quot; \/&gt;\n \n        &lt;TextView\n            android:id=&quot;@+id\/txt_primary&quot;\n            android:layout_width=&quot;match_parent&quot;\n            android:layout_height=&quot;wrap_content&quot;\n            android:ellipsize=&quot;end&quot;\n            android:lines=&quot;1&quot;\n            android:textColor=&quot;@color\/subject&quot;\n            android:textSize=&quot;@dimen\/msg_text_secondary&quot;\n            android:textStyle=&quot;bold&quot; \/&gt;\n \n        &lt;TextView\n            android:id=&quot;@+id\/txt_secondary&quot;\n            android:layout_width=&quot;match_parent&quot;\n            android:layout_height=&quot;wrap_content&quot;\n            android:ellipsize=&quot;end&quot;\n            android:lines=&quot;1&quot;\n            android:textColor=&quot;@color\/message&quot;\n            android:textSize=&quot;@dimen\/msg_text_secondary&quot; \/&gt;\n \n    &lt;\/LinearLayout&gt;\n \n    &lt;RelativeLayout\n        android:id=&quot;@+id\/icon_container&quot;\n        android:layout_width=&quot;wrap_content&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:orientation=&quot;vertical&quot;&gt;\n \n        &lt;RelativeLayout\n            android:id=&quot;@+id\/icon_back&quot;\n            android:layout_width=&quot;wrap_content&quot;\n            android:layout_height=&quot;wrap_content&quot;&gt;\n \n            &lt;ImageView\n                android:layout_width=&quot;@dimen\/icon_width_height&quot;\n                android:layout_height=&quot;@dimen\/icon_width_height&quot;\n                android:src=&quot;@drawable\/bg_circle&quot; \/&gt;\n \n            &lt;ImageView\n                android:layout_width=&quot;25dp&quot;\n                android:layout_height=&quot;wrap_content&quot;\n                android:layout_centerInParent=&quot;true&quot;\n                android:src=&quot;@drawable\/ic_done_white_24dp&quot; \/&gt;\n        &lt;\/RelativeLayout&gt;\n \n        &lt;RelativeLayout\n            android:id=&quot;@+id\/icon_front&quot;\n            android:layout_width=&quot;wrap_content&quot;\n            android:layout_height=&quot;wrap_content&quot;&gt;\n \n            &lt;ImageView\n                android:id=&quot;@+id\/icon_profile&quot;\n                android:layout_width=&quot;@dimen\/icon_width_height&quot;\n                android:layout_height=&quot;@dimen\/icon_width_height&quot; \/&gt;\n \n            &lt;TextView\n                android:id=&quot;@+id\/icon_text&quot;\n                android:layout_width=&quot;wrap_content&quot;\n                android:layout_height=&quot;wrap_content&quot;\n                android:layout_centerInParent=&quot;true&quot;\n                android:textColor=&quot;@android:color\/white&quot;\n                android:textSize=&quot;@dimen\/icon_text&quot; \/&gt;\n        &lt;\/RelativeLayout&gt;\n \n    &lt;\/RelativeLayout&gt;\n \n    &lt;TextView\n        android:id=&quot;@+id\/timestamp&quot;\n        android:layout_width=&quot;wrap_content&quot;\n        android:layout_height=&quot;wrap_content&quot;\n        android:layout_alignParentRight=&quot;true&quot;\n        android:textColor=&quot;@color\/timestamp&quot;\n        android:textSize=&quot;@dimen\/timestamp&quot;\n        android:textStyle=&quot;bold&quot; \/&gt;\n \n    &lt;ImageView\n        android:id=&quot;@+id\/icon_star&quot;\n        android:layout_width=&quot;@dimen\/icon_star&quot;\n        android:layout_height=&quot;@dimen\/icon_star&quot;\n        android:layout_alignParentBottom=&quot;true&quot;\n        android:layout_alignParentRight=&quot;true&quot;\n        android:tint=&quot;@color\/icon_tint_normal&quot; \/&gt;\n \n&lt;\/RelativeLayout&gt;\n<\/pre>\n\n\n<p>We also need two menu files to render the toolbar icons. One is to display the icons when Toolbar is in&nbsp;<strong>normal mode<\/strong>. The other is to display the icons when&nbsp;<strong>ActionMode<\/strong>&nbsp;is enabled.<\/p>\n\n\n\n<p><strong>21<\/strong>. Under&nbsp;<strong>res \u21d2 menu<\/strong>&nbsp;folder, create two menu files named&nbsp;<strong>menu_main.xml<\/strong>&nbsp;and&nbsp;<strong>menu_action_mode.xml<\/strong>.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmenu_main.xml\n&lt;menu xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:app=&quot;http:\/\/schemas.android.com\/apk\/res-auto&quot;\n    xmlns:tools=&quot;http:\/\/schemas.android.com\/tools&quot;\n    tools:mContext=&quot;net.infotheme.gmail.activity.MainActivity&quot;&gt;\n    &lt;item\n        android:id=&quot;@+id\/action_search&quot;\n        android:icon=&quot;@drawable\/ic_search_white_24dp&quot;\n        android:orderInCategory=&quot;100&quot;\n        android:title=&quot;@string\/action_search&quot;\n        app:showAsAction=&quot;always&quot; \/&gt;\n&lt;\/menu&gt;\n<\/pre>\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nmenu_action_mode.xml\n&lt;menu xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:app=&quot;http:\/\/schemas.android.com\/apk\/res-auto&quot;\n    xmlns:tools=&quot;http:\/\/schemas.android.com\/tools&quot;\n    tools:mContext=&quot;net.infotheme.gmail.activity.MainActivity&quot;&gt;\n    &lt;item\n        android:id=&quot;@+id\/action_delete&quot;\n        android:icon=&quot;@drawable\/ic_delete_white_24dp&quot;\n        android:orderInCategory=&quot;100&quot;\n        android:title=&quot;@string\/action_delete&quot;\n        app:showAsAction=&quot;always&quot; \/&gt;\n&lt;\/menu&gt;\n<\/pre>\n\n\n<p>One more important class you will have to take care is the adapter class. The functionality of the RecyclerView completely depends on how efficiently you are managing the adapter class.<\/p>\n\n\n\n<p><strong>22<\/strong>. Under&nbsp;<strong>adapter<\/strong>&nbsp;package, create a class named&nbsp;<strong>MessagesAdapter.java<\/strong>&nbsp;and paste the below code. This class is very important, take your own time to explore and understand the code. All the magic happens in&nbsp;<strong>onBindViewHolder()<\/strong>&nbsp;method.<\/p>\n\n\n\n<p><strong>&gt; applyReadStatus()<\/strong>&nbsp;method make the text bold depending on read status. If the message is unread, it will be kept in bold.<\/p>\n\n\n\n<p><strong>&gt; applyImportant()<\/strong>&nbsp;\u2013 Displays the star icon in yellow color if the message is marked as important.<\/p>\n\n\n\n<p><strong>&gt; applyIconAnimation()<\/strong>&nbsp;\u2013 Method performs the flip animation on thumbnail icon.<\/p>\n\n\n\n<p><strong>&gt; applyProfilePicture()<\/strong>&nbsp;\u2013 Displays the thumbnail icon profile picture \/ background in circular fashion.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nMessagesAdapter.java\npackage net.infotheme.gmail.adapter;\n \nimport android.content.Context;\nimport android.graphics.Typeface;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.widget.RecyclerView;\nimport android.text.TextUtils;\nimport android.util.SparseBooleanArray;\nimport android.view.HapticFeedbackConstants;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n \nimport com.bumptech.glide.Glide;\nimport com.bumptech.glide.load.engine.DiskCacheStrategy;\n \nimport java.util.ArrayList;\nimport java.util.List;\n \nimport net.infotheme.gmail.R;\nimport net.infotheme.gmail.helper.CircleTransform;\nimport net.infotheme.gmail.helper.FlipAnimator;\nimport net.infotheme.gmail.model.Message;\n \npublic class MessagesAdapter extends RecyclerView.Adapter&lt;MessagesAdapter.MyViewHolder&gt; {\n    private Context mContext;\n    private List&lt;Message&gt; messages;\n    private MessageAdapterListener listener;\n    private SparseBooleanArray selectedItems;\n \n    \/\/ array used to perform multiple animation at once\n    private SparseBooleanArray animationItemsIndex;\n    private boolean reverseAllAnimations = false;\n \n    \/\/ index is used to animate only the selected row\n    \/\/ dirty fix, find a better solution\n    private static int currentSelectedIndex = -1;\n \n    public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {\n        public TextView from, subject, message, iconText, timestamp;\n        public ImageView iconImp, imgProfile;\n        public LinearLayout messageContainer;\n        public RelativeLayout iconContainer, iconBack, iconFront;\n \n        public MyViewHolder(View view) {\n            super(view);\n            from = (TextView) view.findViewById(R.id.from);\n            subject = (TextView) view.findViewById(R.id.txt_primary);\n            message = (TextView) view.findViewById(R.id.txt_secondary);\n            iconText = (TextView) view.findViewById(R.id.icon_text);\n            timestamp = (TextView) view.findViewById(R.id.timestamp);\n            iconBack = (RelativeLayout) view.findViewById(R.id.icon_back);\n            iconFront = (RelativeLayout) view.findViewById(R.id.icon_front);\n            iconImp = (ImageView) view.findViewById(R.id.icon_star);\n            imgProfile = (ImageView) view.findViewById(R.id.icon_profile);\n            messageContainer = (LinearLayout) view.findViewById(R.id.message_container);\n            iconContainer = (RelativeLayout) view.findViewById(R.id.icon_container);\n            view.setOnLongClickListener(this);\n        }\n \n        @Override\n        public boolean onLongClick(View view) {\n            listener.onRowLongClicked(getAdapterPosition());\n            view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);\n            return true;\n        }\n    }\n \n \n    public MessagesAdapter(Context mContext, List&lt;Message&gt; messages, MessageAdapterListener listener) {\n        this.mContext = mContext;\n        this.messages = messages;\n        this.listener = listener;\n        selectedItems = new SparseBooleanArray();\n        animationItemsIndex = new SparseBooleanArray();\n    }\n \n    @Override\n    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        View itemView = LayoutInflater.from(parent.getContext())\n                .inflate(R.layout.message_list_row, parent, false);\n \n        return new MyViewHolder(itemView);\n    }\n \n    @Override\n    public void onBindViewHolder(final MyViewHolder holder, final int position) {\n        Message message = messages.get(position);\n \n        \/\/ displaying text view data\n        holder.from.setText(message.getFrom());\n        holder.subject.setText(message.getSubject());\n        holder.message.setText(message.getMessage());\n        holder.timestamp.setText(message.getTimestamp());\n \n        \/\/ displaying the first letter of From in icon text\n        holder.iconText.setText(message.getFrom().substring(0, 1));\n \n        \/\/ change the row state to activated\n        holder.itemView.setActivated(selectedItems.get(position, false));\n \n        \/\/ change the font style depending on message read status\n        applyReadStatus(holder, message);\n \n        \/\/ handle message star\n        applyImportant(holder, message);\n \n        \/\/ handle icon animation\n        applyIconAnimation(holder, position);\n \n        \/\/ display profile image\n        applyProfilePicture(holder, message);\n \n        \/\/ apply click events\n        applyClickEvents(holder, position);\n    }\n \n    private void applyClickEvents(MyViewHolder holder, final int position) {\n        holder.iconContainer.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                listener.onIconClicked(position);\n            }\n        });\n \n        holder.iconImp.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                listener.onIconImportantClicked(position);\n            }\n        });\n \n        holder.messageContainer.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                listener.onMessageRowClicked(position);\n            }\n        });\n \n        holder.messageContainer.setOnLongClickListener(new View.OnLongClickListener() {\n            @Override\n            public boolean onLongClick(View view) {\n                listener.onRowLongClicked(position);\n                view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);\n                return true;\n            }\n        });\n    }\n \n    private void applyProfilePicture(MyViewHolder holder, Message message) {\n        if (!TextUtils.isEmpty(message.getPicture())) {\n            Glide.with(mContext).load(message.getPicture())\n                    .thumbnail(0.5f)\n                    .crossFade()\n                    .transform(new CircleTransform(mContext))\n                    .diskCacheStrategy(DiskCacheStrategy.ALL)\n                    .into(holder.imgProfile);\n            holder.imgProfile.setColorFilter(null);\n            holder.iconText.setVisibility(View.GONE);\n        } else {\n            holder.imgProfile.setImageResource(R.drawable.bg_circle);\n            holder.imgProfile.setColorFilter(message.getColor());\n            holder.iconText.setVisibility(View.VISIBLE);\n        }\n    }\n \n    private void applyIconAnimation(MyViewHolder holder, int position) {\n        if (selectedItems.get(position, false)) {\n            holder.iconFront.setVisibility(View.GONE);\n            resetIconYAxis(holder.iconBack);\n            holder.iconBack.setVisibility(View.VISIBLE);\n            holder.iconBack.setAlpha(1);\n            if (currentSelectedIndex == position) {\n                FlipAnimator.flipView(mContext, holder.iconBack, holder.iconFront, true);\n                resetCurrentIndex();\n            }\n        } else {\n            holder.iconBack.setVisibility(View.GONE);\n            resetIconYAxis(holder.iconFront);\n            holder.iconFront.setVisibility(View.VISIBLE);\n            holder.iconFront.setAlpha(1);\n            if ((reverseAllAnimations &amp;amp;&amp;amp; animationItemsIndex.get(position, false)) || currentSelectedIndex == position) {\n                FlipAnimator.flipView(mContext, holder.iconBack, holder.iconFront, false);\n                resetCurrentIndex();\n            }\n        }\n    }\n \n \n    \/\/ As the views will be reused, sometimes the icon appears as\n    \/\/ flipped because older view is reused. Reset the Y-axis to 0\n    private void resetIconYAxis(View view) {\n        if (view.getRotationY() != 0) {\n            view.setRotationY(0);\n        }\n    }\n \n    public void resetAnimationIndex() {\n        reverseAllAnimations = false;\n        animationItemsIndex.clear();\n    }\n \n    @Override\n    public long getItemId(int position) {\n        return messages.get(position).getId();\n    }\n \n    private void applyImportant(MyViewHolder holder, Message message) {\n        if (message.isImportant()) {\n            holder.iconImp.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_star_black_24dp));\n            holder.iconImp.setColorFilter(ContextCompat.getColor(mContext, R.color.icon_tint_selected));\n        } else {\n            holder.iconImp.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_star_border_black_24dp));\n            holder.iconImp.setColorFilter(ContextCompat.getColor(mContext, R.color.icon_tint_normal));\n        }\n    }\n \n    private void applyReadStatus(MyViewHolder holder, Message message) {\n        if (message.isRead()) {\n            holder.from.setTypeface(null, Typeface.NORMAL);\n            holder.subject.setTypeface(null, Typeface.NORMAL);\n            holder.from.setTextColor(ContextCompat.getColor(mContext, R.color.subject));\n            holder.subject.setTextColor(ContextCompat.getColor(mContext, R.color.message));\n        } else {\n            holder.from.setTypeface(null, Typeface.BOLD);\n            holder.subject.setTypeface(null, Typeface.BOLD);\n            holder.from.setTextColor(ContextCompat.getColor(mContext, R.color.from));\n            holder.subject.setTextColor(ContextCompat.getColor(mContext, R.color.subject));\n        }\n    }\n \n    @Override\n    public int getItemCount() {\n        return messages.size();\n    }\n \n    public void toggleSelection(int pos) {\n        currentSelectedIndex = pos;\n        if (selectedItems.get(pos, false)) {\n            selectedItems.delete(pos);\n            animationItemsIndex.delete(pos);\n        } else {\n            selectedItems.put(pos, true);\n            animationItemsIndex.put(pos, true);\n        }\n        notifyItemChanged(pos);\n    }\n \n    public void clearSelections() {\n        reverseAllAnimations = true;\n        selectedItems.clear();\n        notifyDataSetChanged();\n    }\n \n    public int getSelectedItemCount() {\n        return selectedItems.size();\n    }\n \n    public List&lt;Integer&gt; getSelectedItems() {\n        List&lt;Integer&gt; items =\n                new ArrayList&lt;&gt;(selectedItems.size());\n        for (int i = 0; i &lt; selectedItems.size(); i++) {\n            items.add(selectedItems.keyAt(i));\n        }\n        return items;\n    }\n \n    public void removeData(int position) {\n        messages.remove(position);\n        resetCurrentIndex();\n    }\n \n    private void resetCurrentIndex() {\n        currentSelectedIndex = -1;\n    }\n \n    public interface MessageAdapterListener {\n        void onIconClicked(int position);\n \n        void onIconImportantClicked(int position);\n \n        void onMessageRowClicked(int position);\n \n        void onRowLongClicked(int position);\n    }\n}\n<\/pre>\n\n\n<p><strong>23<\/strong>. Finally open your main activity (<strong>MainActivity.java<\/strong>) and modify the code as below.<\/p>\n\n\n\n<p><strong>&gt;<\/strong>&nbsp;<strong>SwipeRefreshLayout<\/strong>&nbsp;is added to fetch the json messages on refresh.<\/p>\n\n\n\n<p><strong>&gt; getInbox()<\/strong>&nbsp;Method fetches and parses the JSON appending it to array list.<\/p>\n\n\n\n<p><strong>&gt;<\/strong>&nbsp;<strong>Adapter<\/strong>&nbsp;instance is created and attached to&nbsp;<strong>RecyclerView<\/strong>.<\/p>\n\n\n\n<p><strong>&gt;<\/strong>&nbsp;<strong>ActionMode<\/strong>&nbsp;is enabled when the list item is long pressed.<\/p>\n\n\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nMainActivity.java\npackage net.infotheme.gmail.activity;\n \nimport android.content.res.TypedArray;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport android.support.design.widget.FloatingActionButton;\nimport android.support.design.widget.Snackbar;\nimport android.support.v4.widget.SwipeRefreshLayout;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.view.ActionMode;\nimport android.support.v7.widget.DefaultItemAnimator;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.Toolbar;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Toast;\n \nimport java.util.ArrayList;\nimport java.util.List;\n \nimport net.infotheme.gmail.R;\nimport net.infotheme.gmail.adapter.MessagesAdapter;\nimport net.infotheme.gmail.helper.DividerItemDecoration;\nimport net.infotheme.gmail.model.Message;\nimport net.infotheme.gmail.network.ApiClient;\nimport net.infotheme.gmail.network.ApiInterface;\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\n \npublic class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener, MessagesAdapter.MessageAdapterListener {\n    private List&lt;Message&gt; messages = new ArrayList&lt;&gt;();\n    private RecyclerView recyclerView;\n    private MessagesAdapter mAdapter;\n    private SwipeRefreshLayout swipeRefreshLayout;\n    private ActionModeCallback actionModeCallback;\n    private ActionMode actionMode;\n \n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n \n        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);\n        fab.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                Snackbar.make(view, &quot;Replace with your own action&quot;, Snackbar.LENGTH_LONG)\n                        .setAction(&quot;Action&quot;, null).show();\n            }\n        });\n \n        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);\n        swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);\n        swipeRefreshLayout.setOnRefreshListener(this);\n \n        mAdapter = new MessagesAdapter(this, messages, this);\n        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());\n        recyclerView.setLayoutManager(mLayoutManager);\n        recyclerView.setItemAnimator(new DefaultItemAnimator());\n        recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));\n        recyclerView.setAdapter(mAdapter);\n \n        actionModeCallback = new ActionModeCallback();\n \n        \/\/ show loader and fetch messages\n        swipeRefreshLayout.post(\n                new Runnable() {\n                    @Override\n                    public void run() {\n                        getInbox();\n                    }\n                }\n        );\n    }\n \n    \/**\n     * Fetches mail messages by making HTTP request\n     * url: https:\/\/demoapi.infotheme.net\/gmail\/json\/inbox.json\n     *\/\n    private void getInbox() {\n        swipeRefreshLayout.setRefreshing(true);\n \n        ApiInterface apiService =\n                ApiClient.getClient().create(ApiInterface.class);\n \n        Call&lt;List&lt;Message&gt;&gt; call = apiService.getInbox();\n        call.enqueue(new Callback&lt;List&lt;Message&gt;&gt;() {\n            @Override\n            public void onResponse(Call&lt;List&lt;Message&gt;&gt; call, Response&lt;List&lt;Message&gt;&gt; response) {\n                \/\/ clear the inbox\n                messages.clear();\n \n                \/\/ add all the messages\n                \/\/ messages.addAll(response.body());\n \n                \/\/ TODO - avoid looping\n                \/\/ the loop was performed to add colors to each message\n                for (Message message : response.body()) {\n                    \/\/ generate a random color\n                    message.setColor(getRandomMaterialColor(&quot;400&quot;));\n                    messages.add(message);\n                }\n \n                mAdapter.notifyDataSetChanged();\n                swipeRefreshLayout.setRefreshing(false);\n            }\n \n            @Override\n            public void onFailure(Call&lt;List&lt;Message&gt;&gt; call, Throwable t) {\n                Toast.makeText(getApplicationContext(), &quot;Unable to fetch json: &quot; + t.getMessage(), Toast.LENGTH_LONG).show();\n                swipeRefreshLayout.setRefreshing(false);\n            }\n        });\n    }\n \n    \/**\n     * chooses a random color from array.xml\n     *\/\n    private int getRandomMaterialColor(String typeColor) {\n        int returnColor = Color.GRAY;\n        int arrayId = getResources().getIdentifier(&quot;mdcolor_&quot; + typeColor, &quot;array&quot;, getPackageName());\n \n        if (arrayId != 0) {\n            TypedArray colors = getResources().obtainTypedArray(arrayId);\n            int index = (int) (Math.random() * colors.length());\n            returnColor = colors.getColor(index, Color.GRAY);\n            colors.recycle();\n        }\n        return returnColor;\n    }\n \n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        \/\/ Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n \n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        \/\/ Handle action bar item clicks here. The action bar will\n        \/\/ automatically handle clicks on the Home\/Up button, so long\n        \/\/ as you specify a parent activity in AndroidManifest.xml.\n        int id = item.getItemId();\n \n        \/\/noinspection SimplifiableIfStatement\n        if (id == R.id.action_search) {\n            Toast.makeText(getApplicationContext(), &quot;Search...&quot;, Toast.LENGTH_SHORT).show();\n            return true;\n        }\n \n        return super.onOptionsItemSelected(item);\n    }\n \n    @Override\n    public void onRefresh() {\n        \/\/ swipe refresh is performed, fetch the messages again\n        getInbox();\n    }\n \n    @Override\n    public void onIconClicked(int position) {\n        if (actionMode == null) {\n            actionMode = startSupportActionMode(actionModeCallback);\n        }\n \n        toggleSelection(position);\n    }\n \n    @Override\n    public void onIconImportantClicked(int position) {\n        \/\/ Star icon is clicked,\n        \/\/ mark the message as important\n        Message message = messages.get(position);\n        message.setImportant(!message.isImportant());\n        messages.set(position, message);\n        mAdapter.notifyDataSetChanged();\n    }\n \n    @Override\n    public void onMessageRowClicked(int position) {\n        \/\/ verify whether action mode is enabled or not\n        \/\/ if enabled, change the row state to activated\n        if (mAdapter.getSelectedItemCount() &gt; 0) {\n            enableActionMode(position);\n        } else {\n            \/\/ read the message which removes bold from the row\n            Message message = messages.get(position);\n            message.setRead(true);\n            messages.set(position, message);\n            mAdapter.notifyDataSetChanged();\n \n            Toast.makeText(getApplicationContext(), &quot;Read: &quot; + message.getMessage(), Toast.LENGTH_SHORT).show();\n        }\n    }\n \n    @Override\n    public void onRowLongClicked(int position) {\n        \/\/ long press is performed, enable action mode\n        enableActionMode(position);\n    }\n \n    private void enableActionMode(int position) {\n        if (actionMode == null) {\n            actionMode = startSupportActionMode(actionModeCallback);\n        }\n        toggleSelection(position);\n    }\n \n    private void toggleSelection(int position) {\n        mAdapter.toggleSelection(position);\n        int count = mAdapter.getSelectedItemCount();\n \n        if (count == 0) {\n            actionMode.finish();\n        } else {\n            actionMode.setTitle(String.valueOf(count));\n            actionMode.invalidate();\n        }\n    }\n \n \n    private class ActionModeCallback implements ActionMode.Callback {\n        @Override\n        public boolean onCreateActionMode(ActionMode mode, Menu menu) {\n            mode.getMenuInflater().inflate(R.menu.menu_action_mode, menu);\n \n            \/\/ disable swipe refresh if action mode is enabled\n            swipeRefreshLayout.setEnabled(false);\n            return true;\n        }\n \n        @Override\n        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {\n            return false;\n        }\n \n        @Override\n        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {\n            switch (item.getItemId()) {\n                case R.id.action_delete:\n                    \/\/ delete all the selected messages\n                    deleteMessages();\n                    mode.finish();\n                    return true;\n \n                default:\n                    return false;\n            }\n        }\n \n        @Override\n        public void onDestroyActionMode(ActionMode mode) {\n            mAdapter.clearSelections();\n            swipeRefreshLayout.setEnabled(true);\n            actionMode = null;\n            recyclerView.post(new Runnable() {\n                @Override\n                public void run() {\n                    mAdapter.resetAnimationIndex();\n                    \/\/ mAdapter.notifyDataSetChanged();\n                }\n            });\n        }\n    }\n \n    \/\/ deleting the messages from recycler view\n    private void deleteMessages() {\n        mAdapter.resetAnimationIndex();\n        List&lt;Integer&gt; selectedItemPositions =\n                mAdapter.getSelectedItems();\n        for (int i = selectedItemPositions.size() - 1; i &gt;= 0; i--) {\n            mAdapter.removeData(selectedItemPositions.get(i));\n        }\n        mAdapter.notifyDataSetChanged();\n    }\n}\n<\/pre>\n\n\n<p>Run the project and see the output in action. Make sure that your device is having good internet connection. If you have any queries, feel free to ask them in the comment section below.<\/p>\n\n\n\n<p>Happy Coding \ud83d\ude00<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-like-inbox-recycler-view-tutorial-1.png\"><img loading=\"lazy\" width=\"720\" height=\"624\" src=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-like-inbox-recycler-view-tutorial-1.png\" alt=\"\" class=\"wp-image-1519\" srcset=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-like-inbox-recycler-view-tutorial-1.png 720w, https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/android-gmail-like-inbox-recycler-view-tutorial-1-300x260.png 300w\" sizes=\"(max-width: 720px) 100vw, 720px\" \/><\/a><\/figure>\n\n\n\n<h2><span id=\"Whats_Next\">Whats Next?<\/span><\/h2>\n\n\n\n<p>This article covers everything but one thing is missing i.e adding Swipe to delete and undo functionalities. But don\u2019t worry, the next article explains adding&nbsp;<strong>Swipe Delete and Undo<\/strong>&nbsp;to RecyclerView.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/recycler-view-swipe-delete-undo-example.png\"><img loading=\"lazy\" width=\"720\" height=\"600\" src=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/recycler-view-swipe-delete-undo-example.png\" alt=\"\" class=\"wp-image-1520\" srcset=\"https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/recycler-view-swipe-delete-undo-example.png 720w, https:\/\/infotheme.net\/blog\/wp-content\/uploads\/2022\/10\/recycler-view-swipe-delete-undo-example-300x250.png 300w\" sizes=\"(max-width: 720px) 100vw, 720px\" \/><\/a><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Introduction of RecyclerView is the best thing ever happen to android world. You can create stunningly beautiful lists and grids using the RecyclerView. Lot of [&hellip;] <span class=\"read-more-link\"><a class=\"read-more\" href=\"https:\/\/infotheme.net\/blog\/android-creating-gmail-like-inbox-using-recyclerview\/\">Read More<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":1521,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[157,135,1],"tags":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/posts\/1515"}],"collection":[{"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/comments?post=1515"}],"version-history":[{"count":5,"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/posts\/1515\/revisions"}],"predecessor-version":[{"id":1549,"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/posts\/1515\/revisions\/1549"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/media\/1521"}],"wp:attachment":[{"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/media?parent=1515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/categories?post=1515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/infotheme.net\/blog\/wp-json\/wp\/v2\/tags?post=1515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}