Android Option Menu In Kotlin

Menus are a common user interface component in many types of applications. The options menu is the primary collection of menu items for an activity. It's where you should place actions that have a global impact on the app,  such as "Search," "Compose email," and "Settings."



Lets See How to Create Option Menu .

Creating an Options Menu


1. Menu XML .

Inside the res/menu/ create a xml file "menu_main.xml" .

file: : menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu 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"
    tools:context="com.tutorialsbuzz.androidoptionmenu.MainActivity">

    <item
        android:id="@+id/action_item1"
        android:orderInCategory="100"
        android:title="@string/action_item1"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_item2"
        android:icon="@mipmap/ic_launcher"
        android:orderInCategory="100"
        android:title="@string/action_item2"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_item3"
        android:orderInCategory="100"
        android:title="@string/action_item3"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_item4"
        android:orderInCategory="100"
        android:title="@string/action_item4"
        app:showAsAction="never" />
    
</menu>

2. XML Layout .

Add the app bar (also known as the action bar) and toolbar  widget to your xml layout .

file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>


3. Setup ActionBar
Inside the onCreate method call setSupportActionBar by passing toolbar reference .

setSupportActionBar(toolbar)

4. onCreateOptionsMenu .

Override the onCreateOptionsMenu callback inside your activity class and call inflater method on menuInflater object by passing main_menu.xml which is defined above .

 override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }


Handling click events


Override the onOptionsItemSelected callback of activity to handle click event of option menu .

override fun onOptionsItemSelected(item: MenuItem): Boolean {
 return when (item.itemId) {
  R.id.action_item1 -> {
    // handing on click of item with id action_item1
   true
  }

  R.id.action_item2 -> {
   // handing on click of item with id action_item2
   true
  }

  R.id.action_item3 -> {
    // handing on click of item with id action_item3
   true
  }

  R.id.action_item4 -> {
    // handing on click of item with id action_item4
   true
  }

  else -> super.onOptionsItemSelected(item)
 }
}


How Item's Should display in Action Bar


Using "showAsAction" attritube of item tag we can specify how the items of option menu should display in the Action Bar .
  • never : Never show this item in an action bar, show it in the overflow menu instead. Mutually exclusive with "ifRoom" and "always" .
  • ifRoom : Show this item in an action bar if there is room for it as determined by the system. Favor this option over "always" where possible. Mutually exclusive with "never" and "always".
  • always : Always show this item in an actionbar, even if it would override the system's limits of how much stuff to put there. This may make your action bar look bad on some screens. In most cases you should use "ifRoom" instead. Mutually exclusive with "ifRoom" and "never" .
  • withText : When this item is shown as an action in the action bar, show a text label with it even if it has an icon representation. 
  • collapseActionView : This item's action view collapses to a normal menu item. When expanded, the action view takes over a larger segment of its container.

SubMenu


To Create SubMenu Add Menu Tag inside Item Tag .

<item
  android:id="@+id/action_item1"
  android:orderInCategory="100"
  android:title="@string/action_item1"
  app:showAsAction="never">

 <menu>
  <item
    android:id="@+id/action_sub_item01"
    android:orderInCategory="100"
    android:title="@string/action_sub_item01"
    app:showAsAction="never" />

  <item
   android:id="@+id/action_sub_item02"
   android:orderInCategory="100"
   android:title="@string/action_sub_item02"
   app:showAsAction="never" />
 </menu>

</item>


Styling Option Menu 


You can customize the option menu by setting it to light and dark ,also you can change the background color , text color .

In the xml layout for Toolbar widget use the "app:popupTheme" to set the theme for option menu .
<com.google.android.material.appbar.AppBarLayout
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:theme="@style/AppTheme.AppBarOverlay">

 <androidx.appcompat.widget.Toolbar
  android:id="@+id/toolbar"
  android:layout_width="match_parent"
  android:layout_height="?attr/actionBarSize"
  android:background="?attr/colorPrimary"
  app:popupTheme="@style/AppTheme.PopupOverlay" />

</com.google.android.material.appbar.AppBarLayout>

1. Light Theme

<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />


2. Dark Theme

<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Dark" />

3 Custom Styling


<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light">
 <item name="android:colorBackground">@color/colorAccent</item>
 <item name="android:textColorPrimary">#ffffff</item>
 <item name="android:textColorSecondary">#ffffff</item>
</style>


MainActivity



file : MainActivity.kt
package com.tutorialsbuzz.androidoptionmenu

import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_item1 -> {
                showMessage(resources.getString(R.string.action_item1) + " clicked")
                true
            }

            R.id.action_item2 -> {
                showMessage(resources.getString(R.string.action_item2) + " clicked")
                true
            }

            R.id.action_item3 -> {
                showMessage(resources.getString(R.string.action_item3) + " clicked")
                true
            }

            R.id.action_item4 -> {
                showMessage(resources.getString(R.string.action_item4) + " clicked")
                true
            }

            else -> super.onOptionsItemSelected(item)
        }
    }

    private fun showMessage(msg: String) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
    }
}


Read More »

Android ViewModel LiveData Kotlin

The ViewModel class is designed to store and manage Ui-related data in a life-cycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations .

Android Framework manages the life-cycle of activity and fragment , whenever there is configuration changes such as screen rotation or applying/toggling dark mode then the framework decides to recreate and any transient Ui-related data you store in them will be lost .

Save and Restore Data On Configuration Change


For simple data, the activity can use the onSaveInstanceState() method and restore its data from the bundle in onCreate(), but this approach is only suitable for small amounts of data that can be serialized then deserialized, not for potentially large amounts of data like a list of users or bitmaps ViewModel is best suitable save and restore large anount of data .

ViewModel LifeCycle


The above diagram illustrates the various life-cycle states of an activity as it undergoes a rotation and then is finished  and the ViewModel which is associated with the activity will exist until the activity is fully destroyed .

LiveData 


LiveData is an observable data holder class. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the life-cycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active life-cycle state.

Activities and fragments safely observe LiveData objects and not worry about leaks activities and fragments are instantly unsubscribed to livedata when their lifecycles are destroyed.

The advantages of using LiveData
  • Ensures your UI matches your data state
  • No memory leaks
  • No crashes due to stopped activities
  • No more manual lifecycle handling
  • Always up to date data
  • Proper configuration changes
  • Sharing resources

Create LiveData objects


LiveData is a wrapper that can be used with any data, including objects that implement Collections,
such as List. A LiveData object is usually stored within a ViewModel object
Inside Your ViewModel declare and initialize viewmodel
 var myRandomNumber = MutableLiveData<String>().apply {
        value = (0..10).random().toString()
    }

Observer LiveData Changes .


Generally, LiveData delivers updates only when data changes, and only to active observers(Activities or fragments) . Inside the onCreate method of activity call observe on livedata which defined inside the viewmodel.
 mainActivityViewModel.myRandomNumber.observe(this, Observer {
            textview.text = it
        })

In this tutorial we will see a simple example , where we will generate random number inside the onCreate method and set it to textview and when the device is rotate the random value should not change , also we will have button widget and inside the onclick listener of button make changes to livedata which is defined inside viewmodel , change in livedata notifies the observe(Activity) and upadte UI with newly changed value

ViewModel


  • Create a MainActivityViewModel by extending ViewModel class .
  • Define a LiveData by wrapping to the String data variable .
  • Add createRandomNumber which modifies the livedata , when will be called on button click event  .
file : MainActivityViewModel.kt
package com.tutorialsbuzz.viewmodelsample

import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import java.util.concurrent.atomic.AtomicInteger

class MainActivityViewModel : ViewModel() {

    val TAG: String = MainActivityViewModel::class.java.simpleName

    var myRandomNumber = MutableLiveData<String>().apply {
        value = (0..10).random().toString()
    }

    fun createRandomNumber() {
        Log.d(TAG, "createRandomNumber");
        myRandomNumber.value = (0..10).random().toString()
    }

    override fun onCleared() {
        super.onCleared()
        Log.d(TAG, "onCleared");
    }
}

XML Layout


Create XML Layout activity_main.xml , this will be set as content view for launcher Activity (MainActivity.kt) and add TextView and Button to your layout file

file : activity_main.xml
<?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"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tvNumber"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:text=""
        android:textSize="30sp" />

    <Button
        android:id="@+id/update"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="30dp"
        android:gravity="center"
        android:text="update"
        android:textSize="30sp" />

</LinearLayout>

MainActivity 

  • Create a koltin class MainActivity.kt and extend it to AppCompatActivity class .
  • Override onCreate function and set the content of this MainActivity with above defined xml layout (activity_main.xml). 
  • Setup the activity with the above define MainActivityViewModel .
  • Set the random number to TextView .
  • On Button click call createRandomNumber method of MainActivityViewModel .

file : MainActivity.kt
package com.tutorialsbuzz.viewmodelsample

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        val mainActivityViewModel =
            ViewModelProviders.of(this).get(MainActivityViewModel::class.java)

        mainActivityViewModel.myRandomNumber.observe(this, Observer {
            tvNumber.text = it
        })

        update.setOnClickListener({
            mainActivityViewModel.createRandomNumber()
        })

    }
}

Portrait
Landscape



Observe LiveData Changes :



Read More »

Android Studio IDE Shortcut

If your an android developer you must know android studio shortcuts , knowing the shortcuts makes developer life easier , makes you familiar  with the environment and  increase the productivity , Android Studio includes keyboard shortcuts for many common actions , below are the some of the shortcuts which are used more.

1. Shortcut for log TAG


    Type "logt" android studio ide we show helper select and enter


2. Optimize Imports


remove unused imports "Ctrl + Alt + O"


3. Generate Getter and Setter Method


To Generate getter and setter method used the shortcut key "ALT + Insert" > Select Getter and Setter >PopUp Dialog apprears select member variable for which you want to generate method .


4. Find/Search File Name .


To Find the file name in the project use the shortcut key "Ctrl +Shift + N" , A dialog appears start typing filename which you want a drop down appears select the click which opens the files


5. Recent Files


To Know about Recent file modified use the shortcut key "Crtl +E"


6. Search Everywhere


If your looking for particular keyword or terms in the entire project use the shortcut key "Ctrl + Shift + F" , A dialog appears type the keyword it will show file contains that keyword.



7. Fold /Unfold .



Fold  : Use the shortcut "Ctrl +shift+('-')) to fold or collapse
Unfold : Use the shortcut "Ctrl+shift +('+')) to unfold or expand



8. Refactor Code .


To Refactor code use the shortcut "Ctrl+Alt+L"



9. Single Line Comment


To Comment Single line ,first place the cursor at the particular line and use the shortcut key "Ctrl+/"


10.  Block Comment or Comment Multiple Line


To Comment more the one lines ,Select the piece of code which you want to comment and use the shortcut key "Ctrl + Shift + / "


Read More »

Android RecyclerView Interactive Swipe Like Gmail

RecyclerView is a ViewGroup ,that display a scrolling list of elements based on large data sets (or data that frequently changes) .  RecyclerView widget is more flexible and efficient version of ListView.

In the previous tutorial we have seen example
In this tutorial we will see how to fill swiped Portion using canvas by drawing text and svg image resource .



Project Detail
Project Name RecyclerViewSwipeCanvas
Package com.tutorialsbuzz.recyclerviewswipecanvas
Min Sdk Version 22
Target Sdk Version 29
Compile Sdk Version 29
Theme Theme.AppCompat.Light.DarkActionBar

Add RecyclerView to your layout



file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/rcv"/>

Data Class


file : Model.kt
package com.tutorialsbuzz.recyclerview

data class Model(val name: String, val version: String) {}

To Load Data Into RecyclerView , We will read JSON File Kept Inside Asset folder and map it to above defined data class .

file : main\assets\android_version.json
[
  {
    "name": "Cupcake",
    "version": "Android 1.5"
  },
  {
    "name": "Donut",
    "version": "Android 1.6"
  },
  {
    "name": "Eclairs",
    "version": "Android 2.0-2.1"
  },
  {
    "name": "Froyo",
    "version": "Android 2.2-2.3"
  },
  {
    "name": "Gingerbread",
    "version": "Android 2.3-2.3.7"
  },
  {
    "name": "Honeycomb",
    "version": "Android 3.0-3.2.6"
  },
  {
    "name": "Icecream",
    "version": "Android 4.0-4.0.4"
  },
  {
    "name": "Jellybean",
    "version": "Android 4.1-4.3.1"
  },
  {
    "name": "Kitkat",
    "version": "Android 4.4-4.4.4"
  },
  {
    "name": "Lolipop",
    "version": "Android 5.0-5.1.1"
  },
  {
    "name": "Marshmallow",
    "version": "Android 6.0-6.0.1"
  },
  {
    "name": "Nougat",
    "version": "Android 7.0-7.1.2"
  },
  {
    "name": "Oreo",
    "version": "Android 8.0-8.1"
  },
  {
    "name": "Pie",
    "version": "Android 9.0"
  }
]


Adapter and ViewHolder For RecyclerView .


1. XML Layout For RecyclerView Item

Create  XML Layout file in res/layout and name it row_item.xml , This Layout defines the layout for Items of RecyclerView . Here In this example we have two TextView inside LinearLayout

file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:padding="20dp"
        android:layout_height="wrap_content">

    <TextView
            android:id="@+id/txt"
            android:textSize="22sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textStyle="bold"/>

    <TextView
            android:id="@+id/sub_txt"
            android:textSize="18sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textStyle="italic"/>

</LinearLayout>

2.  Adapter and ViewHolder 
  • Create a Adapter That RecyclerView Can Use , Create a class CustomAdapter extend it to RecyclerView.Adapter .
  • Create Inner class ViewHolder extend it to RecyclerView.ViewHolder
file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerview

import android.content.Context
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.tutorialsbuzz.recyclerviewswipecanvas.R
import kotlinx.android.synthetic.main.row_item.view.*

class CustomAdapter(val modelList: List<Model>, val context: Context) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    var handler: Handler? = Handler()

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as ViewHolder).bind(modelList.get(position));
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return ViewHolder(layoutInflater.inflate(R.layout.row_item, parent, false))
    }

    override fun getItemCount(): Int {
        return modelList.size;
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bind(model: Model): Unit {
            itemView.txt.text = model.name
            itemView.sub_txt.text = model.version
        }

    }

    fun undoView(position: Int) {
        handler?.postDelayed({
            notifyItemChanged(position)
        }, 1000)
    }

}

ItemTouchHelper CallBack For RecyclerView


Create an abstract class SwipeToDeleteCallback extend this class to ItemTouchHelper.SimpleCallback .
  1. onSwiped :  Called when a ViewHolder is swiped by the user. At this point, you should update your adapter (e.g. remove the item) and call related Adapter notify event.
  2. getSwipeDirs : This callback Returns the swipe directions for the provided ViewHolder.
    (To disable swipe at particular viewholder return to ItemTouchHelper.ACTION_STATE_IDLE)
  3. onChildDraw : Called by ItemTouchHelper on RecyclerView's onDraw callback.If you would like to customize how your View's respond to user interactions, this is a good place to override.

Drawing to Canvas On Left Right Swipe


Inside onChildDraw Callback of ItemTouchHelper.SimpleCallback , We will fill swiped portion using canvas .
override fun onChildDraw( c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,   dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean){
        ................
        ...........
}

We will use the below two parameter for drawing to canvas and to decide on which direction (left or right).

  1. If dx < 0 then it is considered as left swipe .
  2. If dx > 0 then it is considered as right swipe .
  3. colorCanavas : call this to add background to canvas by passing colorCode and proper bound co-ordinates(left,top,right,bottom).
  4. drawTextOnCanvas : call this to add text to canvas by passing string and x-coordinate,y-coordinate 
  5. drawIconOnCanVas : call this to add icon to canvas by passing bitmap and and proper bound co-ordinates(left,top,right,bottom).
file : SwipeToDeleteCallback.kt
package com.tutorialsbuzz.recylerviewswipetodelete

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.util.Log
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

abstract class SwipeToDeleteCallback : ItemTouchHelper.SimpleCallback {

    //configure left swipe params
    var leftBG: Int = Color.LTGRAY
    var leftLabel: String = ""
    var leftIcon: Drawable? = null

    //configure right swipe params
    var rightBG: Int = Color.LTGRAY;
    var rightLabel: String = ""
    var rightIcon: Drawable? = null

    var context: Context;

    constructor(context: Context, dragDir: Int, swipeDir: Int) : super(dragDir, swipeDir) {
        this.context = context

    }

    private lateinit var background: Drawable

    var initiated: Boolean = false
    //Setting Swipe Text
    val paint = Paint()

    fun initSwipeView(): Unit {
        paint.setColor(Color.WHITE)
        paint.setTextSize(48f)
        paint.setTextAlign(Paint.Align.CENTER)
        background = ColorDrawable();
        initiated = true;
    }

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }


    override fun onChildDraw(
        c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
        dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean
    ) {

        Log.d("onChildDraw", "dx: " + dX)

        val itemView = viewHolder.itemView
        if (!initiated) {
            initSwipeView()
        }


        if (dX != 0.0f) {

            if (dX > 0) {
                //right swipe
                val intrinsicHeight = (rightIcon?.getIntrinsicWidth() ?: 0)
                val xMarkTop = itemView.top + ((itemView.bottom - itemView.top) - intrinsicHeight) / 2
                val xMarkBottom = xMarkTop + intrinsicHeight

                colorCanavas(c, rightBG, itemView.left + dX.toInt(), itemView.top, itemView.left, itemView.bottom)
                drawTextOnCanvas(c, rightLabel, (itemView.left + 200).toFloat(), (xMarkTop + 10).toFloat())
                drawIconOnCanVas(
                    c, rightIcon, itemView.left + (rightIcon?.getIntrinsicWidth() ?: 0) + 50,
                    xMarkTop + 20,
                    itemView.left + 2 * (rightIcon?.getIntrinsicWidth() ?: 0) + 50,
                    xMarkBottom + 20
                )

            } else {
                //left swipe
                val intrinsicHeight = (leftIcon?.getIntrinsicWidth() ?: 0)
                val xMarkTop = itemView.top + ((itemView.bottom - itemView.top) - intrinsicHeight) / 2
                val xMarkBottom = xMarkTop + intrinsicHeight

                colorCanavas(
                    c,
                    leftBG,
                    itemView.right + dX.toInt(),
                    itemView.top,
                    itemView.right,
                    itemView.bottom
                )
                drawTextOnCanvas(c, leftLabel, (itemView.right - 200).toFloat(), (xMarkTop + 10).toFloat())
                drawIconOnCanVas(
                    c, leftIcon, itemView.right - 2 * (leftIcon?.getIntrinsicWidth() ?: 0) - 70,
                    xMarkTop + 20,
                    itemView.right - (leftIcon?.getIntrinsicWidth() ?: 0) - 70,
                    xMarkBottom + 20
                )
            }
        }

        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
    }


    fun colorCanavas(canvas: Canvas, canvasColor: Int, left: Int, top: Int, right: Int, bottom: Int): Unit {
        (background as ColorDrawable).color = canvasColor
        background.setBounds(left, top, right, bottom)
        background.draw(canvas)
    }

    fun drawTextOnCanvas(canvas: Canvas, label: String, x: Float, y: Float) {
        canvas.drawText(label, x, y, paint)
    }

    fun drawIconOnCanVas(
        canvas: Canvas, icon: Drawable?, left: Int, top: Int, right: Int, bottom: Int
    ): Unit {
        icon?.setBounds(left, top, right, bottom)
        icon?.draw(canvas)

    }
}

Configure left swipe and right swipe


Inside SwipeToDeleteCallback declare variable for which is responsible drawing background ,label  ,icon inside canvas .

1. Left Swipe Configuration
//configure left swipe params
var leftBG: Int = Color.LTGRAY
var leftLabel: String = ""
var leftIcon: Drawable? = null

//configure left swipe
swipeToDeleteCallback.leftBG = ContextCompat.getColor(this,
                                       R.color.leftSwipeBG)
swipeToDeleteCallback.leftLabel = "Thumbs UP"
swipeToDeleteCallback.leftIcon = AppCompatResources.
      getDrawable(this, R.drawable.ic_thumb_up_black_24dp)


2. Right Swipe Configuration
//configure right swipe params
var rightBG: Int = Color.LTGRAY;
var rightLabel: String = ""
var rightIcon: Drawable? = null

//configure right swipe
swipeToDeleteCallback.rightBG = ContextCompat.getColor(this,
                                       R.color.rightSwipeBG)
swipeToDeleteCallback.rightLabel = "Thumbs Down"
swipeToDeleteCallback.rightIcon = AppCompatResources.
       getDrawable(this, R.drawable.ic_thumb_down_black_24dp)


Activity

  1. Create a koltin class MainActivity.kt and extend it to AppCompatActivity class .
  2. Override onCreate function and set the content of this MainActivity with above defined xml layout (activity_main.xml). 
  3. Read JSON Data From Asset Folder , this will be used for binding data into RecyclerView .
  4. Set Vertical Linear LayoutManager to RecyclerView.
  5. Create Instance of Adapter and set to RecyclerView .
  6. Add CustomItemDecortion to RecyclerView .
  7. Create an instance of ItemTouchHelper by passing SwipeToDeleteCallback as parameter to its constructor.
  8. Attach ItemTouchHelper to RecyclerView .

file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewswipecanvas

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tutorialsbuzz.recyclerview.CustomAdapter
import com.tutorialsbuzz.recyclerview.Model
import com.tutorialsbuzz.recylerviewswipetodelete.SwipeToDeleteCallback
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONArray
import org.json.JSONObject

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        rcv.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)

        val model = readFromAsset();
        val adapter = CustomAdapter(model, this)
        rcv.adapter = adapter;

        rcv.addItemDecoration(SimpleDividerItemDecoration(this))

        val swipeToDeleteCallback =
            object : SwipeToDeleteCallback(this, 0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
                override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                    adapter.undoView(viewHolder.adapterPosition)
                }
            }

        //configure left swipe
        swipeToDeleteCallback.leftBG = ContextCompat.getColor(this, R.color.leftSwipeBG)
        swipeToDeleteCallback.leftLabel = "Thumbs UP"
        swipeToDeleteCallback.leftIcon = AppCompatResources.getDrawable(this, R.drawable.ic_thumb_up_black_24dp)

        //configure right swipe
        swipeToDeleteCallback.rightBG = ContextCompat.getColor(this, R.color.rightSwipeBG)
        swipeToDeleteCallback.rightLabel = "Thumbs Down"
        swipeToDeleteCallback.rightIcon = AppCompatResources.getDrawable(this, R.drawable.ic_thumb_down_black_24dp)

        val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback)
        itemTouchHelper.attachToRecyclerView(rcv)
    }


    private fun readFromAsset(): List<Model> {

        val modeList = mutableListOf<Model>()

        val bufferReader = application.assets.open("android_version.json").bufferedReader()
        val json_string = bufferReader.use {
            it.readText()
        }

        val jsonArray = JSONArray(json_string);
        for (i in 0..jsonArray.length() - 1) {
            val jsonObject: JSONObject = jsonArray.getJSONObject(i)
            val model = Model(jsonObject.getString("name"), jsonObject.getString("version"))
            modeList.add(model)
        }
        return modeList
    }

}



Read More »

Android RecyclerView Swipe to Delete Undo

RecyclerView is a ViewGroup ,that display a scrolling list of elements based on large data sets (or data that frequently changes) . RecyclerView widget is more flexible and efficient version of ListView .

In the previous tutorial we have seen example of deleting items of recyclerView on swiping ,In this tutorial we will how to do for swiped items of recyclerView  .



Project Detail
Project Name RecyclerViewSwiptToDeleteUndo
Package com.tutorialsbuzz.recyclerviewswipetodeleteundo
Min Sdk Version 22
Target Sdk Version 29
Compile Sdk Version 29
Theme Theme.AppCompat.Light.DarkActionBar


ItemTouchHelper


ItemTouchHelper is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

Add RecyclerView to your layout


Create XML Layout activity_main.xml , this will be set as content view for launcher Activity (MainActivity.kt) and add RecyclerView to your layout file .



file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/recyclerView"/>

Data Class


file : Model.kt
package com.tutorialsbuzz.recyclerviewswipetodeleteundo

data class Model(val name: String, val version:String) {}

To Load Data Into RecyclerView , We will read JSON File Kept Inside Asset folder and map it to above defined data class .

file : main\assets\android_version.json
[
  {
    "name": "Cupcake",
    "version": "Android 1.5"
  },
  {
    "name": "Donut",
    "version": "Android 1.6"
  },
  {
    "name": "Eclairs",
    "version": "Android 2.0-2.1"
  },
  {
    "name": "Froyo",
    "version": "Android 2.2-2.3"
  },
  {
    "name": "Gingerbread",
    "version": "Android 2.3-2.3.7"
  },
  {
    "name": "Honeycomb",
    "version": "Android 3.0-3.2.6"
  },
  {
    "name": "Icecream",
    "version": "Android 4.0-4.0.4"
  },
  {
    "name": "Jellybean",
    "version": "Android 4.1-4.3.1"
  },
  {
    "name": "Kitkat",
    "version": "Android 4.4-4.4.4"
  },
  {
    "name": "Lolipop",
    "version": "Android 5.0-5.1.1"
  },
  {
    "name": "Marshmallow",
    "version": "Android 6.0-6.0.1"
  },
  {
    "name": "Nougat",
    "version": "Android 7.0-7.1.2"
  },
  {
    "name": "Oreo",
    "version": "Android 8.0-8.1"
  },
  {
    "name": "Pie",
    "version": "Android 9.0"
  }
]


Image Resource 

Name field from json matches to respective png file name kept inside drawable .

Folder : app\src\main\res\drawable

Adapter and ViewHolder For RecyclerView


1. XML Layout For RecyclerView Item 

Create XML Layout file in res/layout and name it row_item.xml , This Layout defines the layout for Items of RecyclerView . Here In this example we have two TextView inside LinearLayout .

Each Row Item Of RecyclerView Includes Regular-layout and swipe layout wrapped inside FrameLayout Please find below diagram and xml code .
  • Regular Layout is shown when items of recyclerView is loaded and when user clicks undo .
  • Swipe layout is shown when user swipes items of recyclerView.

file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="wrap_content">

    <!-- Swipe Layout-->
    <include layout="@layout/swipe_row_item" />

    <!-- Regular Layout-->
    <include layout="@layout/regular_row_item" />

</FrameLayout>

file : swipe_row_item.xml
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/swipeLayout"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:orientation="horizontal"
              android:visibility="visible"
              android:background="@android:color/holo_red_dark"
              android:padding="30dp"
              android:weightSum="3">

    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="archived"
            android:textColor="@android:color/white"
            android:textSize="24sp"/>


    <TextView
            android:id="@+id/undo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:gravity="end"
            android:paddingBottom="5dp"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:paddingTop="5dp"
            android:text="undo"
            android:textColor="@android:color/white"
            android:textSize="22sp"
            android:textStyle="bold"/>

</LinearLayout>


file : regular_row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:id="@+id/regularLayout"
        android:orientation="vertical">

    <ImageView android:layout_width="80dp"
               android:layout_height="80dp"
               android:id="@+id/img"
               android:contentDescription="@string/app_name"/>

    <TextView
            android:id="@+id/txt"
            android:textSize="22sp"
            android:text="Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:layout_toEndOf="@+id/img"
            android:layout_marginStart="20dp"/>

    <TextView
            android:id="@+id/sub_txt"
            android:textSize="18sp"
            android:text="Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="italic"
            android:layout_toEndOf="@+id/img"
            android:layout_below="@+id/txt"
            android:layout_marginStart="20dp"/>

</RelativeLayout>

2. Adapter 

Create a Adapter That RecyclerView Can Use , Create a class CustomAdapter extend it to RecyclerView.Adapter .

3. ViewHolder 

Create Inner class ViewHolder extend it to RecyclerView.ViewHolder

file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerviewswipetodeleteundo

import android.content.Context
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.tutorialsbuzz.recyclerviewswipetodeleteundo.R
import kotlinx.android.synthetic.main.regular_row_item.view.*
import kotlinx.android.synthetic.main.swipe_row_item.view.*


class CustomAdapter(val modelList: MutableList<Model>, val context: Context) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private var itemsPendingRemoval: MutableList<Model>?
    var PENDING_REMOVAL_TIMEOUT: Long = 3000
    var handler: Handler? = Handler()
    var pendingRunnables: HashMap<Model, Runnable>? = HashMap()

    init {
        itemsPendingRemoval = mutableListOf<Model>()
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as ViewHolder).bind(modelList.get(position));
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return ViewHolder(layoutInflater.inflate(R.layout.row_item, parent, false))
    }


    override fun getItemCount(): Int {
        return modelList.size;
    }


    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bind(model: Model): Unit {

            if (itemsPendingRemoval!!.contains(model)) {
                //show swipe layout
                itemView.swipeLayout.visibility = View.VISIBLE
                itemView.regularLayout.visibility = View.GONE

                itemView.undo.setOnClickListener({ view ->
                    undoOpt(model)
                })

            } else {
                //show regular layout
                itemView.swipeLayout.visibility = View.GONE
                itemView.regularLayout.visibility = View.VISIBLE

                itemView.txt.text = model.name
                itemView.sub_txt.text = model.version
                val id = context.resources.getIdentifier(model.name.toLowerCase(), "drawable", context.packageName)
                itemView.img.setBackgroundResource(id)
            }
        }

        private fun undoOpt(model: Model) {
            val pendingRemovalRunnable: Runnable? = pendingRunnables?.get(model)
            pendingRunnables?.remove(model)
            if (pendingRemovalRunnable != null)
                handler?.removeCallbacks(pendingRemovalRunnable)
            itemsPendingRemoval?.remove(model)
            // this will rebind the row in "normal" state
            notifyItemChanged(modelList.indexOf(model))
        }

    }


    fun pendingRemoval(position: Int) {

        val data = modelList.get(position)
        if (!itemsPendingRemoval!!.contains(data)) {
            itemsPendingRemoval?.add(data)
            // this will redraw row in "undo" state
            notifyItemChanged(position)
            // let's create, store and post a runnable to remove the data
            val pendingRemovalRunnable = Runnable {
                remove(modelList.indexOf(data))
            }

            handler?.postDelayed(pendingRemovalRunnable, PENDING_REMOVAL_TIMEOUT)
            // pendingRunnables!![data] = pendingRemovalRunnable
            pendingRunnables?.put(data, pendingRemovalRunnable)
        }
    }

    fun remove(position: Int) {
        val data = modelList.get(position)
        if (itemsPendingRemoval!!.contains(data)) {
            itemsPendingRemoval?.remove(data)
        }
        if (modelList.contains(data)) {
            //dataList.remove(position)
            modelList.removeAt(position)
            notifyItemRemoved(position)
        }
    }

    fun isPendingRemoval(position: Int): Boolean {
        val data = modelList.get(position)
        return itemsPendingRemoval!!.contains(data)
    }

}

ItemTouchHelper CallBack For RecyclerView


Create an abstract class SwipeToDeleteCallback extend this class to ItemTouchHelper.SimpleCallback
  • onSwiped :  Called when a ViewHolder is swiped by the user. At this point, you should update your adapter (e.g. remove the item) and call related Adapter notify event.
  • getSwipeDirs : This callback Returns the swipe directions for the provided ViewHolder.    (Note : To disable swipe at particular viewholder return to ItemTouchHelper.ACTION_STATE_IDLE ) .
file : SwipeToDeleteCallback.kt
package com.tutorialsbuzz.recylerviewswipetodelete

import android.content.Context
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView


abstract class SwipeToDeleteCallback(var context: Context, dragDir: Int, swipeDir: Int) :
    ItemTouchHelper.SimpleCallback(dragDir, swipeDir) {


    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }
    
}

Attaching ItemTouchHelper to RecyclerView .


Creates an instance of ItemTouchHelper that will work with the given Callback.
Call attachToRecyclerView on ItemTouchHelper instance by passing recyclerView .

val swipeToDeleteCallback =
 object : SwipeToDeleteCallback(this, 0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
  override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
   adapter.pendingRemoval(viewHolder.adapterPosition)
  }

  override fun getSwipeDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
   if (adapter.isPendingRemoval(viewHolder.adapterPosition)) {
    return ItemTouchHelper.ACTION_STATE_IDLE
   }
   return super.getSwipeDirs(recyclerView, viewHolder)
  }
 }

val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)

Swipe Undo


Pending Removals 
  • When user swipes add item to pending removal list  and show swipe layout and hide regular layout.
  • Using Handler Add Runnable to message queue , Runnable will execute after the specified amount of time elapses ,
  • Inside runnable remove the item at the swiped position .

Undo Remove / delete 
  • when undo is clicked remove the handler Callback  at swiped postion and hide swipe layout and show regular layout .

Disable Swipe at Position in RecyclerView
  • If user has already swiped once then disable swipe for that particular item of recyclerView , to do that Inside getSwipeDirs we will check if item is already added to pendingremoval list by return ItemTouchHelper.ACTION_STATE_IDLE else return super.getSwipeDirs(recyclerView, viewHolder)

Activity


file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewswipetodeleteundo

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tutorialsbuzz.recyclerview.CustomAdapter
import com.tutorialsbuzz.recyclerview.Model
import com.tutorialsbuzz.recylerviewswipetodelete.SwipeToDeleteCallback
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONArray
import org.json.JSONObject

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val layoutManager: RecyclerView.LayoutManager? = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        recyclerView.layoutManager = layoutManager

        val modelList = readFromAsset();
        val adapter = CustomAdapter(modelList, this)
        recyclerView.adapter = adapter;

        recyclerView.addItemDecoration(SimpleDividerItemDecoration(this))

        val swipeToDeleteCallback =
            object : SwipeToDeleteCallback(this, 0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
                override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                    adapter.pendingRemoval(viewHolder.adapterPosition)
                }

                override fun getSwipeDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
                    if (adapter.isPendingRemoval(viewHolder.adapterPosition)) {
                        return ItemTouchHelper.ACTION_STATE_IDLE
                    }
                    return super.getSwipeDirs(recyclerView, viewHolder)
                }
            }

        val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

    private fun readFromAsset(): MutableList<Model> {
        val modeList = mutableListOf<Model>()
        val bufferReader = application.assets.open("android_version.json").bufferedReader()
        val json_string = bufferReader.use {
            it.readText()
        }
        val jsonArray = JSONArray(json_string);

        for (i in 0..jsonArray.length() - 1) {
            val jsonObject: JSONObject = jsonArray.getJSONObject(i)
            val model = Model(jsonObject.getString("name"), jsonObject.getString("version"))
            modeList.add(model)
        }
        return modeList
    }
}


Read More »

Android RecyclerView Swipe To Delete

RecyclerView is a ViewGroup ,that display a scrolling list of elements based on large data sets (or data that frequently changes) . RecyclerView widget is more flexible and efficient version of ListView .

In the previous tutorial we have seen basic example of recyclerView , In this tutorial deleting items of recyclerView on swiping .




Project Detail
Project Name RecylerViewSwipeToDelete
Package com.tutorialsbuzz.recylerviewswipetodelete
Min Sdk Version 22
Target Sdk Version 29
Compile Sdk Version 29
Theme Theme.AppCompat.Light.DarkActionBar

ItemTouchHelper :

ItemTouchHelper is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

Lets see example of swiping (LEFT and RIGHT) items of recyclerView

Add RecyclerView To XML Layout


Create XML Layout activity_main.xml , this will be set as content view for launcher Activity (MainActivity.kt) and add RecyclerView to your layout file .


file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/recyclerView"/>


Data Class


file : Model.kt
package com.tutorialsbuzz.recyclerview

data class Model(val name: String, val version:String) {}

To Load Data Into RecyclerView , We will read JSON File Kept Inside Asset folder and map it to above defined data class

file : main\assets\android_version.json
[
  {
    "name": "Cupcake",
    "version": "Android 1.5"
  },
  {
    "name": "Donut",
    "version": "Android 1.6"
  },
  {
    "name": "Eclairs",
    "version": "Android 2.0-2.1"
  },
  {
    "name": "Froyo",
    "version": "Android 2.2-2.3"
  },
  {
    "name": "Gingerbread",
    "version": "Android 2.3-2.3.7"
  },
  {
    "name": "Honeycomb",
    "version": "Android 3.0-3.2.6"
  },
  {
    "name": "Icecream",
    "version": "Android 4.0-4.0.4"
  },
  {
    "name": "Jellybean",
    "version": "Android 4.1-4.3.1"
  },
  {
    "name": "Kitkat",
    "version": "Android 4.4-4.4.4"
  },
  {
    "name": "Lolipop",
    "version": "Android 5.0-5.1.1"
  },
  {
    "name": "Marshmallow",
    "version": "Android 6.0-6.0.1"
  },
  {
    "name": "Nougat",
    "version": "Android 7.0-7.1.2"
  },
  {
    "name": "Oreo",
    "version": "Android 8.0-8.1"
  },
  {
    "name": "Pie",
    "version": "Android 9.0"
  }
]


Image Resource 

Name field from json matches to respective png file name kept inside drawable .

Folder : app\src\main\res\drawable

Adapter and ViewHolder For RecyclerView


1. XML Layout For RecyclerView Item

Create  XML Layout file in res/layout and name it row_item.xml .This Layout defines the layout for
Items of RecyclerView . Here In this example we have two TextView inside LinearLayout .

file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:orientation="vertical">

    <ImageView android:layout_width="80dp"
               android:layout_height="80dp"
               android:id="@+id/img"
               android:contentDescription="@string/app_name"/>

    <TextView
            android:id="@+id/txt"
            android:textSize="22sp"
            android:text="Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:layout_toEndOf="@+id/img"
            android:layout_marginStart="20dp"/>

    <TextView
            android:id="@+id/sub_txt"
            android:textSize="18sp"
            android:text="Title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="italic"
            android:layout_toEndOf="@+id/img"
            android:layout_below="@+id/txt"
            android:layout_marginStart="20dp"/>

</RelativeLayout>

2. Adapter

Create a Adapter That RecyclerView Can Use , Create a class CustomAdapter extend it to RecyclerView.Adapter .

3. ViewHolder 

Create Inner class ViewHolder extend it to RecyclerView.ViewHolder

file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerview

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.tutorialsbuzz.recylerviewswipetodelete.R
import kotlinx.android.synthetic.main.row_item.view.*

class CustomAdapter(val modelList: List<Model>, val context: Context) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as ViewHolder).bind(modelList.get(position));
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return ViewHolder(layoutInflater.inflate(R.layout.row_item, parent, false))
    }


    override fun getItemCount(): Int {
        return modelList.size;
    }

    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bind(model: Model): Unit {
            itemView.txt.text = model.name
            itemView.sub_txt.text = model.version

            val id = context.resources.getIdentifier(model.name.toLowerCase(), "drawable", context.packageName)
            itemView.img.setBackgroundResource(id)
        }

    }

}


ItemTouchHelper CallBack For RecyclerView 


Create an abstract class SwipeToDeleteCallback extend this class to ItemTouchHelper.Callback override the getMovementFlags , onMove method .
  1. getMovementFlags : Should return a composite flag which defines the enabled move directions in each state (idle, swiping, dragging).Instead of composing this flag manually, you can use makeMovementFlags(int, int) or makeFlag(int, int).For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to swipe by swiping RIGHT, you can return

    override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
     val swipeFlag = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
     return makeMovementFlags(0, swipeFlag)
    }

  2. onMove : Called when onMove(RecyclerView, ViewHolder, ViewHolder) returns true , Upon receiving this callback, you should move the item from the old position to target position .
  3. onSwiped :  Called when a ViewHolder is swiped by the user. At this point, you should update your adapter (e.g. remove the item) and call related Adapter notify event.

file : SwipeToDeleteCallback.kt
package com.tutorialsbuzz.recylerviewswipetodelete

import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

abstract class SwipeToDeleteCallback : ItemTouchHelper.Callback() {

    override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
        val swipeFlag = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
        return makeMovementFlags(0, swipeFlag)
    }

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        return false
    }
}


Attaching ItemTouchHelper to RecyclerView .

  1. Creates an instance of ItemTouchHelper that will work with the given Callback.
  2. Call attachToRecyclerView on ItemTouchHelper instance by passing recyclerView.attachToRecyclerView Attaches the ItemTouchHelper to the provided RecyclerView.
val swipeToDeleteCallback = object : SwipeToDeleteCallback() {
 override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
  val pos = viewHolder.adapterPosition
  modelList.removeAt(pos)
  adapter.notifyItemRemoved(pos)
 }
}

val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)

Activity

  1. Create a koltin class MainActivity.kt and extend it to AppCompatActivity class .
  2. Override onCreate function and set the content of this MainActivity with above defined xml layout (activity_main.xml). 
  3. Read JSON Data From Asset Folder , this will be used for binding data into RecyclerView .
  4. Set Vertical Linear LayoutManager to RecyclerView.
  5. Create Instance of Adapter and set to RecyclerView .
  6. Add CustomItemDecortion to RecyclerView .
  7. Create an instance of ItemTouchHelper by passing SwipeToDeleteCallback as parameter to its constructor.
  8. Attach ItemTouchHelper to RecyclerView .
file : MainActivity.kt
package com.tutorialsbuzz.recylerviewswipetodelete

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tutorialsbuzz.recyclerview.CustomAdapter
import com.tutorialsbuzz.recyclerview.Model
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONArray
import org.json.JSONObject

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val modelList = readFromAsset();
        val adapter = CustomAdapter(modelList, this)

        recyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        recyclerView.adapter = adapter;
        recyclerView.addItemDecoration(SimpleDividerItemDecoration(this))

        val swipeToDeleteCallback = object : SwipeToDeleteCallback() {
            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                val pos = viewHolder.adapterPosition
                modelList.removeAt(pos)
                adapter.notifyItemRemoved(pos)
            }
        }

        val itemTouchHelper = ItemTouchHelper(swipeToDeleteCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

    private fun readFromAsset(): MutableList<Model> {

        val modeList = mutableListOf<Model>()
        val bufferReader = application.assets.open("android_version.json").bufferedReader()
        val json_string = bufferReader.use {
            it.readText()
        }

        val jsonArray = JSONArray(json_string);

        for (i in 0..jsonArray.length() - 1) {
            val jsonObject: JSONObject = jsonArray.getJSONObject(i)
            val model = Model(jsonObject.getString("name"), jsonObject.getString("version"))
            modeList.add(model)
        }

        return modeList
    }

}



Read More »