Android Drag and Drop Item in RecycleView.


1.Create an android application project in Studio

Create an android application project in eclipse. To drag and drop list items in list view, we need a custom list view. So first create a class for custom list view and extend it to ListView super class.

2.OnItemLongClickListener

Set OnItemLongClick listener in adapter view and create a hover cell for the selected item.

holder.rowView.setOnLongClickListener(View.OnLongClickListener {    
mStartDragListener.requestDrag(holder)
    true})

3. activity_main.xml file

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

    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/contactsRecycleView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent">
    </androidx.recyclerview.widget.RecyclerView>

</androidx.constraintlayout.widget.ConstraintLayout>

4. StartDragListener.kt  Interface

package android.com.androiditemdraganddrop

import androidx.recyclerview.widget.RecyclerView

interface StartDragListener {
    fun requestDrag(viewHolder: RecyclerView.ViewHolder)
}

5. MainNavigator.kt Interface

package android.com.androiditemdraganddrop

interface MainNavigator {

    fun onRecentActivityClick(accountName: String)
}

6. ItemMoveCallback.kt

package android.com.androiditemdraganddrop

interface MainNavigator {

    fun onRecentActivityClick(accountName: String)
}

Here is main adapter this adapter is use-full  to drag Item from one position to another position

7. MainAdapter.xml file

package android.com.androiditemdraganddrop

import android.content.Context
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_favorites.view.*
import java.util.*
import kotlin.collections.ArrayList
import kotlin.com.androiditemdraganddrop.R

class MainAdapter(
    private val context: Context,
    private val favoritesList: ArrayList<String>,
    private val mStartDragListener: StartDragListener
) :
    RecyclerView.Adapter<MainAdapter.MyViewHolder>(),
    ItemMoveCallback.ItemTouchHelperContract {

    lateinit var mListener: MainNavigator

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(
            LayoutInflater.from(context).inflate(
                R.layout.item_favorites,
                parent,
                false
            )
        )
    }

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

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.accountName?.text = favoritesList.get(position)

        holder.rowView.setOnLongClickListener(View.OnLongClickListener {
            mStartDragListener.requestDrag(holder)
            true
        })

        holder.itemView.setOnClickListener {
            mListener.onRecentActivityClick(favoritesList[position])
        }
    }

    class MyViewHolder(internal var rowView: View) : RecyclerView.ViewHolder(rowView) {
        val accountName = rowView.account_name
        val imgProfilePic = rowView.img_profilePic
    }

    override fun onRowMoved(fromPosition: Int, toPosition: Int) {
        if (fromPosition < toPosition) {
            for (i in fromPosition until toPosition) {
                Collections.swap(favoritesList, i, i + 1)
            }
        } else {
            for (i in fromPosition downTo toPosition + 1) {
                Collections.swap(favoritesList, i, i - 1)
            }
        }
        notifyItemMoved(fromPosition, toPosition)
    }

    override fun onRowSelected(myViewHolder: MyViewHolder) {
        myViewHolder.rowView.setBackgroundColor(Color.WHITE)
    }

    override fun onRowClear(myViewHolder: MyViewHolder) {
        myViewHolder.rowView.setBackgroundColor(Color.WHITE)
    }
}


Here is the  screen shorts.





Finally MainActivity.kt file to send data to adapter 

8.MainActivity.kt file

package android.com.androiditemdraganddrop

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.com.androiditemdraganddrop.R

class MainActivity : AppCompatActivity(), StartDragListener, MainNavigator {

    private var mFavoritesAdapter: MainAdapter? = null
    private val mFavoriteList: ArrayList<String> = ArrayList()
    internal var touchHelper: ItemTouchHelper? = null

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

        mFavoriteList.add("Account 1")
        mFavoriteList.add("Account 2")
        mFavoriteList.add("Account 3")
        mFavoriteList.add("Account 4")
        mFavoriteList.add("Account 5")
        mFavoriteList.add("Account 6")
        mFavoriteList.add("Account 7")
        mFavoriteList.add("Account 8")
        mFavoriteList.add("Account 9")
        mFavoriteList.add("Account 10")

        //Favorites list
        val layoutManager = LinearLayoutManager(this)
        contactsRecycleView!!.layoutManager = (layoutManager)
        mFavoritesAdapter = MainAdapter(this, mFavoriteList, this)

        val callback = ItemMoveCallback(mFavoritesAdapter!!)
        touchHelper = ItemTouchHelper(callback)
        touchHelper!!.attachToRecyclerView(contactsRecycleView)

        contactsRecycleView!!.adapter = mFavoritesAdapter
        mFavoritesAdapter!!.mListener = this
    }

    /*
    * This method used for requesting drag items.
    * */
    override fun requestDrag(viewHolder: RecyclerView.ViewHolder) {
        touchHelper!!.startDrag(viewHolder)
    }

    /*
    * This method used for fetching item details.
    * */
    override fun onRecentActivityClick(accountName: String) {
        Toast.makeText(this, accountName, Toast.LENGTH_LONG).show()

    }
}

Android RecyclerView adding Contacts Search Filter in Kotlin

In this article we are going to learn how to add search filter functionality to RecyclerView. Adding search is very simple task, we’ll use Toolbar’s search widget to input the search query. To demonstrate I am taking an example of contacts list and search for a contact by name or phone number or MailId


1. RecyclerView Search Filter – getFilter()

Android provides Filterable class to filter the data by a filter (condition). Usually the getFilter() method has to be overridden in the adapter class in which the filter condition is provided to search through a list. Below is an example of getFilter() method to search a contact by name or phone number from a list of contacts.



override fun getFilter(): Filter {
    return object : Filter() {
        override fun performFiltering(charSequence: CharSequence): FilterResults {
            val charString = charSequence.toString()
            if (charString.isEmpty()) {
                contactListFiltered = mMaincontactsList            } else {
                val filteredList = ArrayList<ContactVO>()
                for (row in mMaincontactsList) {
                    // name match condition. this might differ depending on your requirement                    // here we are looking for name or phone number match                    if (row.contactName.toLowerCase().contains(charString.toLowerCase())
                        || row.contactEmail.toLowerCase().contains(charString.toLowerCase())
                        || row.contactNumber.contains(charSequence)
                    ) {
                        filteredList.add(row)
                    }
                }
                contactListFiltered = filteredList
            }
            val filterResults = FilterResults()
            filterResults.values = contactListFiltered            return filterResults
        }

        override fun publishResults(charSequence: CharSequence, filterResults: FilterResults) {
            contactListFiltered = filterResults.values as ArrayList<ContactVO>
            notifyDataSetChanged()
        }
    }
}

2. Creating New Project

1. Create the MainActivity.kt and add the code as shown below..




package kotlin.com.contactssearch

import android.Manifest
import android.app.AlertDialog
import android.content.DialogInterface
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.provider.ContactsContract
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private val contactVOList: ArrayList<ContactVO> = ArrayList()
    private var mContactsRecyclerView: RecyclerView? = null    private var mSearchContactsAdapter: ContactsAdapter? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val mContactsRecyclerView = contacts_recyclerView
        if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_CONTACTS)
            == PackageManager.PERMISSION_GRANTED) {
            readContacts()
        } else {
            requestReadContactsPermission()
        }

        /**         * Enabling Search Filter         * */        search_editText.addTextChangedListener(object : TextWatcher {
            override fun onTextChanged(cs: CharSequence, arg1: Int, arg2: Int, arg3: Int) {
                mSearchContactsAdapter!!.getFilter().filter(cs.toString().trim())
            }

            override fun beforeTextChanged(arg0: CharSequence, arg1: Int, arg2: Int, arg3: Int) {
            }

            override fun afterTextChanged(arg0: Editable) {

            }
        })
    }

    private fun readContacts() {
        val cr = this.contentResolver        val cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
        if (cursor!!.getCount() > 0) {
            while (cursor.moveToNext()) {
                val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
                val name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {

                    val mContactVO = ContactVO()

                    mContactVO.contactName = name
                    // get the phone number                    val phoneCursor = cr.query(
                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
                        arrayOf<String>(id), null                    )
                    while (phoneCursor!!.moveToNext()) {
                        val phone = phoneCursor.getString(
                            phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
                        )
                        Log.i("phone : => ", phone)
                        mContactVO.contactNumber = phone
                    }
                    phoneCursor.close()

                    // get email and type                    val emailCursor = cr.query(
                        ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
                        ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?",
                        arrayOf<String>(id), null                    )
                    while (emailCursor!!.moveToNext()) {
                        // This would allow you get several email addresses                        // if the email addresses were stored in an array                        val email = emailCursor.getString(
                            emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)
                        )
                        val emailType = emailCursor.getString(
                            emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE)
                        )
                        mContactVO.contactEmail = email
                    }
                    emailCursor.close()

                    contactVOList.add(mContactVO)
                }
            }

            //Contacts list            val layoutManager = LinearLayoutManager(this)
            contacts_recyclerView!!.layoutManager = (layoutManager)
            mSearchContactsAdapter = ContactsAdapter(this, contactVOList)
            contacts_recyclerView!!.adapter = mSearchContactsAdapter            
mSearchContactsAdapter!!.notifyDataSetChanged()
        }
    }

    private fun requestReadContactsPermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(
                this, android.Manifest.permission.READ_CONTACTS            )
        ) {
            // show UI part if you want here to show some rationale !!!        } else {
            ActivityCompat.requestPermissions(
                this, arrayOf(android.Manifest.permission.READ_CONTACTS),
                REQUEST_READ_CONTACTS            )
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        when (requestCode) {
            REQUEST_READ_CONTACTS -> {
                if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    readContacts()
                } else {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(
                            this, Manifest.permission.READ_CONTACTS                        )
                    ) {
                        // now, user has denied permission (but not permanently!)
                    } else {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            if (shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)) {
                                showMessageOKCancel("You need to allow access to both the permissions",
                                    DialogInterface.OnClickListener { dialog, which ->                                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                            requestReadContactsPermission()
                                        }
                                    })
                                return                            }
                        }
                    }
                }
                return            }
        }
    }

    private fun showMessageOKCancel(message: String, okListener: DialogInterface.OnClickListener) {
        AlertDialog.Builder(this)
            .setMessage(message)
            .setPositiveButton("OK", okListener)
            .setNegativeButton("Cancel", null)
            .create()
            .show()
    }

    companion object {
        private var REQUEST_READ_CONTACTS: Int = 1    }
}

2. Create the ContactsAdapter.kt and add the code as shown below.




package kotlin.com.contactssearch

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.item_contacts.view.*

class ContactsAdapter(
    private val context: Context,
    private var mMaincontactsList: ArrayList<ContactVO>) :
    RecyclerView.Adapter<ContactsAdapter.ViewHolder>(), Filterable {

    private var contactListFiltered: List<ContactVO>? = mMaincontactsList
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(LayoutInflater.from(context).inflate(R.layout.item_contacts, parent, false))
    }

    override fun getItemCount(): Int {
        return contactListFiltered!!.size    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.contactName?.text = contactListFiltered!![position].contactName        holder.contactEmail?.text = contactListFiltered!![position].contactNumber    }

    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val contactName = view.name_textView
        val contactEmail = view.email_textView
    }

    override fun getFilter(): Filter {
        return object : Filter() {
            override fun performFiltering(charSequence: CharSequence): FilterResults {
                val charString = charSequence.toString()
                if (charString.isEmpty()) {
                    contactListFiltered = mMaincontactsList                } else {
                    val filteredList = ArrayList<ContactVO>()
                    for (row in mMaincontactsList) {
                        // name match condition. this might differ depending on your requirement                        // here we are looking for name or phone number match                        if (row.contactName.toLowerCase().contains(charString.toLowerCase())
                            || row.contactEmail.toLowerCase().contains(charString.toLowerCase())
                            || row.contactNumber.contains(charSequence)
                        ) {
                            filteredList.add(row)
                        }
                    }
                    contactListFiltered = filteredList
                }
                val filterResults = FilterResults()
                filterResults.values = contactListFiltered                return filterResults
            }

            override fun publishResults(charSequence: CharSequence, filterResults: FilterResults) {
                contactListFiltered = filterResults.values as ArrayList<ContactVO>
                notifyDataSetChanged()
            }
        }
    }
}


3. Open build.gradle under app folder and add Androidx, RecyclerView dependencies.
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"    implementation "androidx.core:core-ktx:$rootProject.androidXCoreVersion"
    implementation "androidx.appcompat:appcompat:$rootProject.androidXVersion"    implementation "androidx.recyclerview:recyclerview:$rootProject.androidXVersion"    implementation "androidx.constraintlayout:constraintlayout:$rootProject.constraintLayoutXVersion"    implementation "com.google.android.material:material:$rootProject.androidXVersion"

    testImplementation 'junit:junit:4.12'    androidTestImplementation 'com.android.support.test:runner:1.0.2'    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'}
4. Add the below resources to respective strings.xml, dimens.xml, colors.xml files.




<resources>
    <string name="app_name">ContactsSearch</string>
</resources>


<?xml version="1.0" encoding="utf-8"?><resources>
    <!--Margin Values-->    <dimen name="margin_200dp">200dp</dimen>
    <dimen name="margin_85dp">85dp</dimen>
    <dimen name="margin_60dp">60dp</dimen>
    <dimen name="margin_50dp">50dp</dimen>
    <dimen name="margin_40dp">40dp</dimen>
    <dimen name="margin_20dp">20dp</dimen>
    <dimen name="margin_30dp">30dp</dimen>
    <dimen name="margin_10dp">10dp</dimen>
    <dimen name="margin_5dp">5dp</dimen>
    <dimen name="margin_2dp">2dp</dimen>


    <!--Padding Values-->    <dimen name="padding_90dp">90dp</dimen>
    <dimen name="padding_80dp">80dp</dimen>
    <dimen name="padding_85dp">85dp</dimen>
    <dimen name="padding_60dp">60dp</dimen>
    <dimen name="padding_40dp">40dp</dimen>
    <dimen name="padding_10dp">10dp</dimen>
    <dimen name="padding_5dp">5dp</dimen>

    <!--Text Size-->    <dimen name="textsize_40sp">40sp</dimen>
    <dimen name="textsize_30sp">30sp</dimen>
    <dimen name="textsize_24sp">24sp</dimen>
    <dimen name="textsize_20sp">20sp</dimen>
    <dimen name="textsize_16sp">16sp</dimen>
    <dimen name="textsize_15sp">15sp</dimen>
    <dimen name="textsize_14sp">14sp</dimen>
    <dimen name="textsize_10sp">10sp</dimen>

    <!--Text Spacing-->    <dimen name="payment_description_text_spacing">3dp</dimen>

    <!--menu drawer-->    <dimen name="drawer_menu_profile_width">99dp</dimen>
    <dimen name="drawer_menu_profile_height">99dp</dimen>
    <dimen name="drawer_menu_width">340dp</dimen>
    <dimen name="menu_text_padding">8dp</dimen>
    <dimen name="drawer_padding_top">30dp</dimen>
    <dimen name="drawer_padding_left">20dp</dimen>
    <dimen name="menu_home_margin">50dp</dimen>
    <dimen name="user_profile_name_text_size">24sp</dimen>
    <dimen name="user_name_text_size">16sp</dimen>
    <dimen name="menu_text_size">22sp</dimen>
    <dimen name="menu_bottom_text_size">12sp</dimen>
    <dimen name="line_spacing_extra">2sp</dimen>

    
</resources>


<?xml version="1.0" encoding="utf-8"?><resources>
    <color name="colorPrimary">#008577</color>
    <color name="colorPrimaryDark">#00574B</color>
    <color name="colorAccent">#D81B60</color>
</resources>
5. Open AndroidManifest.xml and add Also add the READ_CONTACTS permission as we are going to get all contacts




<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"          package="kotlin.com.contactssearch">


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

    <application            android:allowBackup="true"            android:icon="@mipmap/ic_launcher"            android:label="@string/app_name"            android:roundIcon="@mipmap/ic_launcher_round"            android:supportsRtl="true"            android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

6. As we need to parse the Contacts, we need a POJO class to serialize the json. Create a class named ContactVO.kt and add name, image and phone number.


package kotlin.com.contactssearch

class ContactVO() {
    var contactName = ""    var contactNumber = ""    var contactEmail = ""}
7. Open the layout files of main activity activity_main.xml and item_contacts.xml add RecyclerView element.




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

    <androidx.constraintlayout.widget.ConstraintLayout            android:layout_width="match_parent"            android:layout_height="match_parent">

        <EditText                android:id="@+id/search_editText"                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:cursorVisible="true"                android:focusable="true"                android:singleLine="true"                android:imeOptions="actionDone"                app:layout_constraintStart_toStartOf="parent"                app:layout_constraintTop_toTopOf="parent"/>


        <androidx.recyclerview.widget.RecyclerView                android:id="@+id/contacts_recyclerView"                android:layout_width="match_parent"                android:layout_height="wrap_content"                app:layout_constraintTop_toBottomOf="@+id/search_editText"                app:layout_constraintStart_toStartOf="parent">

        </androidx.recyclerview.widget.RecyclerView>

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>


<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout        xmlns:android="http://schemas.android.com/apk/res/android"        xmlns:app="http://schemas.android.com/apk/res-auto"        android:orientation="vertical"        android:layout_marginStart="@dimen/margin_10dp"        android:layout_width="match_parent"        android:padding="@dimen/padding_10dp"        android:layout_height="wrap_content">

    <ImageView            android:id="@+id/profile_imageView"            android:layout_width="40dp"            android:layout_height="40dp"            app:layout_constraintTop_toTopOf="parent"            app:layout_constraintLeft_toLeftOf="parent"            android:src="@drawable/profile_placeholder"/>

    <TextView            android:id="@+id/name_textView"            android:layout_width="wrap_content"            android:textSize="@dimen/textsize_16sp"            android:paddingStart="@dimen/margin_10dp"            app:layout_constraintLeft_toRightOf="@+id/profile_imageView"            android:textColor="@android:color/black"            app:layout_constraintTop_toTopOf="parent"            android:textStyle="bold"            android:layout_height="wrap_content"/>

    <TextView            android:id="@+id/email_textView"            android:layout_width="wrap_content"            android:textSize="@dimen/textsize_14sp"            android:paddingStart="@dimen/margin_10dp"            app:layout_constraintTop_toBottomOf="@+id/name_textView"            app:layout_constraintLeft_toRightOf="@+id/profile_imageView"            android:textColor="@android:color/black"            android:layout_height="wrap_content"/>


</androidx.constraintlayout.widget.ConstraintLayout>



Select DateRange UsingRangePicker.

  /* * This Method is for select range from picker. * */ private fun selectDateRangeUsingRangePicker () { pageNumber = 1 val displ...