Android RecyclerView Filter Animation Java

In the previous series of tutorial we have seen how to Filter RecyclerView Using SearchView In ToolBar in which the filtering of items in recyclerView is pretty simple without any animation , in this tutorial we will how to animate the items of RecyclerView whiling filtering . the advantage of RecyclerView is it comes up with more notifying calls notifyItemRemoved , notifyItemInserted , notifyItemMoved  for notifying item remove , insert and moved  for item animations apart from notifyDatasetChanged  which is default notifier.

androidrecyclerviewanimatefiltering


1. Build Gradle


Add the following lines to the dependencies section in your project’s build.grade file and sync . It includes design support library , recyclerView library .

file : build.gradle
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

2. Menu


Open menu XML layout inside res/menu/ and add SearchView widget as menu item.

file :main_ menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_search"
        android:icon="@android:drawable/ic_menu_search"
        android:title="@string/action_search"
        app:actionViewClass="androidx.appcompat.widget.SearchView"
        app:showAsAction="always|collapseActionView" />

</menu>


I this tutorial we will create RecyclerView in one of Tabs of TabLayout , Creating TabLayout is outside the scope of this tutorial , In the previous tutorial I have covered how to create TabLayout you can refer for it .

3. XML Layout


Create XML layout file in res/layout and name it tab_one_fragment.xml and Add RecyclerView inside RelativeLayout.

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

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</RelativeLayout>

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

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

    <TextView
        android:id="@+id/country_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Name"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/country_iso"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ISo"
        android:textSize="16sp"/>

</LinearLayout>

4. Java Model


Create a Bean Class CountryModel which defines the data for items of recyclerview .

file : CountryModel.java
package com.tutorialsbuzz.recyclerview.TabFragments;

public class CountryModel {

    String name;
    String isocode;

    CountryModel(String name, String isocode){
        this.name=name;
        this.isocode=isocode;
    }

    public String getName() {
        return name;
    }

    public String getisoCode() {
        return isocode;
    }
}

5. RecylerViewAdapter


file : RVAdapter.java
package com.tutorialsbuzz.recyclerview.TabFragments;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.tutorialsbuzz.recyclerview.R;
import java.util.ArrayList;
import java.util.List;

public class RVAdapter extends RecyclerView.Adapter<ItemViewHolder> {

    private List<CountryModel> mCountryModel;

    public RVAdapter(List<CountryModel> countryModel) {
        mCountryModel = new ArrayList<>(countryModel);

    }

    @Override
    public void onBindViewHolder(ItemViewHolder itemViewHolder, int i) {
        final CountryModel model = mCountryModel.get(i);
        itemViewHolder.bind(model);
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_row, viewGroup, false);
        return new ItemViewHolder(view);
    }

    @Override
    public int getItemCount() {
        return mCountryModel.size();
    }

    /** Filter Logic**/
    public void animateTo(List<CountryModel> models) {
        applyAndAnimateRemovals(models);
        applyAndAnimateAdditions(models);
        applyAndAnimateMovedItems(models);

    }

    private void applyAndAnimateRemovals(List<CountryModel> newModels) {


        for (int i = mCountryModel.size() - 1; i >= 0; i--) {
            final CountryModel model = mCountryModel.get(i);
            if (!newModels.contains(model)) {
                removeItem(i);
            }
        }
    }

    private void applyAndAnimateAdditions(List<CountryModel> newModels) {

        for (int i = 0, count = newModels.size(); i < count; i++) {
            final CountryModel model = newModels.get(i);
            if (!mCountryModel.contains(model)) {
                addItem(i, model);
            }
        }
    }

    private void applyAndAnimateMovedItems(List<CountryModel> newModels) {
        for (int toPosition = newModels.size() - 1; toPosition >= 0; toPosition--) {
            final CountryModel model = newModels.get(toPosition);
            final int fromPosition = mCountryModel.indexOf(model);
            if (fromPosition >= 0 && fromPosition != toPosition) {
                moveItem(fromPosition, toPosition);
            }
        }
    }

    public CountryModel removeItem(int position) {
        final CountryModel model = mCountryModel.remove(position);
        notifyItemRemoved(position);
        return model;
    }

    public void addItem(int position, CountryModel model) {
        mCountryModel.add(position, model);
        notifyItemInserted(position);
    }

    public void moveItem(int fromPosition, int toPosition) {
        final CountryModel model = mCountryModel.remove(fromPosition);
        mCountryModel.add(toPosition, model);
        notifyItemMoved(fromPosition, toPosition);
    }

}

We have already familiar with RecyclerView Adapter Creation ,So in this part we will concentrate on Filter logic.

You can filter items of ListView and RecyclerView By calling notifyDatasetChanged() on the Adapter , But the RecyclerView brings a whole new element to the table which we can take advantage off: out-of-the-box support for item animations!

Define three helper methods removeItem() , addItem() and moveItem() which enable us to add, remove or move items respectively around in the Adapter and Inside These methods we will call the required notify method on the adapter notifyItemRemoved() , notifyItemInserted() and notifyItemMoved() Respectively to trigger the item animation that goes along with it.

Create a method animateTo() that takes the filtered list of data object as parameter and makes call to three methods applyAndAnimateRemovals() , applyAndAnimateAdditions() and applyAndAnimateMovedItems() by passing the filtered list as parameter. three methods do all the work here, but the order is important .

What animateTo() method do ?
The first step is to remove all items which do not exist in the filtered List anymore.
The next step is to add all items which did not exist in the original List but do in the filtered List.
The final step is to move all items which exist in both Lists. This is exactly what the three methods in animateTo() do.

  • applyAndAnimateRemovals() : Inside this method iterate through the internal List of the Adapter backwards and checks if each item is contained in the new filtered List. If it is not it calls removeItem() .

  • applyAndAnimateAdditions() : Inside this method iterate through the internal List of the Adapter it iterates through the filtered List and checks if the item exists in the internal List. If it does not it calls addItem().

  • applyAndAnimateMovedItems() : Inside this method iterate through the filtered List backwards and looks up the index of each item in the internal List. If it detects a difference in the index it calls moveItem() to bring the internal List of the Adapter in line with the filtered List .

6. View Holder

file : ItemViewHolder.java
package com.tutorialsbuzz.recyclerview.TabFragments;

import android.view.View;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.tutorialsbuzz.recyclerview.R;

public class ItemViewHolder extends RecyclerView.ViewHolder {

    private TextView name_TextView;
    private TextView iso_TextView;

    public ItemViewHolder(View itemView) {
        super(itemView);
        name_TextView = (TextView) itemView.findViewById(R.id.country_name);
        iso_TextView = (TextView) itemView.findViewById(R.id.country_iso);

    }

    public void bind(CountryModel countryModel) {
        name_TextView.setText(countryModel.getName());
        iso_TextView.setText(countryModel.getisoCode());
    }
}

6. MainActivity

  • Create a TabOneFragment which extends Fragment .

  • Inside the onCreateView method get the reference to recyclerview and set the LayoutManager as LayoutLayout.

  • Inside the OnActivityCreate method call to setAdpter on recyclerview instance and pass the instance of above define RVAdapter as argument.

7. Filtering Logic

  • Inside the onCreateOptionMenu get the reference of searchView widget and set onQueryTextListener on it .

  • Upon setting onQueryTextListener on SearchView override the onQueryTextChange and onQueryTextSubmit method.

  • I have Created a method filter which takes list of CountryModel and query string as parameter , inside this method we will filter the CountryModel list on one of the constraints of CountryModel (here in this example i am filter the list on name constrain , if you want you can filter on isocode ) and return the filtered list .

  • Inside the onQueryTextChange we make a call to filter method and get the return filtered list this which in-turn passed as a argument while make call to setFilter of RVAdapter .

Note : When you perform filter and then Switch between tabs in TabLayout In such scenario the recyclerview won't return the original set of data so to overcome this you have set the onActionExapndListener on MenuItem and then inside the onMenuItemActionCollapse make a call to animateTo() method on adapter instance by passing the original list as argument .

file : TabOneFragment.java

package com.tutorialsbuzz.recyclerview.TabFragments;

import android.os.Bundle;

import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.appcompat.widget.SearchView;
import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.tutorialsbuzz.recyclerview.R;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class TabOneFragment extends Fragment implements SearchView.OnQueryTextListener {

    private RecyclerView recyclerview;
    private List<CountryModel> mCountryModel;
    private RVAdapter adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.tab_one_fragment, container, false);

        recyclerview = (RecyclerView) view.findViewById(R.id.recyclerview);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerview.setLayoutManager(layoutManager);

        return view;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        setHasOptionsMenu(true);
        String[] locales = Locale.getISOCountries();
        mCountryModel = new ArrayList<>();

        for (String countryCode : locales) {
            Locale obj = new Locale("", countryCode);
            mCountryModel.add(new CountryModel(obj.getDisplayCountry(), obj.getISO3Country()));
        }

        adapter = new RVAdapter(mCountryModel);
        recyclerview.setAdapter(adapter);
    }


    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_main, menu);

        final MenuItem item = menu.findItem(R.id.action_search);
        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
        searchView.setOnQueryTextListener(this);

        item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                // Do something when collapsed
                adapter.animateTo(mCountryModel);
                return true; // Return true to collapse action view
            }

            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                // Do something when expanded
                return true; // Return true to expand action view
            }
        });
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        final List<CountryModel> filteredModelList = filter(mCountryModel, newText);
        adapter.animateTo(filteredModelList);
        recyclerview.scrollToPosition(0);
        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }

    private List<CountryModel> filter(List<CountryModel> models, String query) {
        query = query.toLowerCase();

        final List<CountryModel> filteredModelList = new ArrayList<>();
        for (CountryModel model : models) {
            final String text = model.getName().toLowerCase();
            if (text.contains(query)) {
                filteredModelList.add(model);
            }
        }
        return filteredModelList;
    }

}

RecyclerView Related
  1. RecyclerView
  2. RecyclerView Item Click Ripple Effect
  3. RecyclerView With Item Divider
  4. RecyclerView With CardView
  5. RecyclerView GridLayout
  6. RecyclerView StaggeredGrid Layout
  7. RecyclerView Swipe To Delete
  8. Android RecyclerView Swipe to Delete Undo
  9. Android RecyclerView Interactive Swipe Like Gmail
  10. Android RecyclerView Buttons Under Swipe
  11. Android RecyclerView MultiViewType
  12. Android RecyclerView Item Drag and Drop

Read More »

Android Filter RecyclerView Using SearchView In Java

There might be requirement in your application where you want to implement the search functionality by having SearchView widget inside the toolbar / actionBar for filtering the items of recyclerview . In the previous series of tutorial we have seen the basics of a RecyclerView  , In this tutorial we will see the logic for filtering the items in  RecyclerView .





1. Build Gradle

Add the following lines to the dependencies section in your project's build.grade file and sync . It includes design support library , recyclerView library .

file : build.gradle
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

2. Menu

Open menu XML layout inside res/menu/ and add SearchView widget as  menu item

file : menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_search"
        android:icon="@android:drawable/ic_menu_search"
        android:title="@string/action_search"
        app:actionViewClass="androidx.appcompat.widget.SearchView"
        app:showAsAction="always|collapseActionView" />

</menu>

In this tutorial we will create RecyclerView in one of Tabs of TabLayout , Creating TabLayout is outside the scope of this tutorial , In the previous tutorial I have covered how to create TabLayout you can refer for it .

3. XML Layout

Create  XML layout file in res/layout and name it tab_one_fragment.xml and Add RecyclerView inside RelativeLayout.

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

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

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

file : list_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:padding="16dp"
    android:background="@drawable/selector_row"
    android:clickable="true"
    android:focusableInTouchMode="true"
    android:focusable="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/country_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Name"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/country_iso"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ISo"
        android:textSize="16sp"/>
    
</LinearLayout>

4. Java Model

Create a Bean Class CountryModel which defines the data for items of recyclerview .
file : CountryModel.java

package com.tutorialsbuzz.recyclerview.TabFragments;

public class CountryModel {

    String name;
    String isocode;

    CountryModel(String name, String isocode){
        this.name=name;
        this.isocode=isocode;
    }

    public String getName() {
        return name;
    }

    public String getisoCode() {
        return isocode;
    }
}

5. Adapter

  • Create A Class RVAdapter and this class Must Extend RecyclerView.Adapter. This adapter follows the view holder design pattern, which means that you have to define a custom class that extends RecyclerView.ViewHolder  (This pattern minimizes the number of calls to the costly findViewById method.) .

  • Add a constructor to the custom adapter so that it has a handle to the data that the RecyclerView displays , As our data is in the form of a List<String> .

  • RecyclerView.Adapter has three abstract methods that we must override .

    1. getItemCount() :  This method return the number of items present in the data

    2. onCreateViewHolder() : Inside this method we specify the layout that each item of the RecyclerView should use. This is done by inflating the layout using LayoutInflater, passing the output to the constructor of the custom ViewHolder.

    3. onBindViewHolder() : This Method This method is very similar to the getView method of a ListView's adapter. In our example, here's where you have to set the String values to TextView.

  • I have created a method setFilter , we call this method inside onQueryTextChange  method of SearchView.onQueryTextListener interface by passing filtered data list and inside this method make a call to notifyDataSetChanged() api which will do the filtering magic .

file : RVAdapter.java

package com.tutorialsbuzz.recyclerview.TabFragments;

import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.tutorialsbuzz.recyclerview.R;
import java.util.ArrayList;
import java.util.List;

public class RVAdapter extends RecyclerView.Adapter<ItemViewHolder> {

    private List<CountryModel> mCountryModel;
    private List<CountryModel> mOriginalCountryModel;

    public RVAdapter(List<CountryModel> mCountryModel) {
        this.mCountryModel = mCountryModel;
        this.mOriginalCountryModel = mCountryModel;
    }

    @Override
    public void onBindViewHolder(ItemViewHolder itemViewHolder, int i) {
        final CountryModel model = mCountryModel.get(i);
        itemViewHolder.bind(model);
    }

    @Override
    public ItemViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_row, viewGroup, false);
        return new ItemViewHolder(view);
    }

    @Override
    public int getItemCount() {
        return mCountryModel.size();
    }

    public void setFilter(List<CountryModel> countryModels){
        mCountryModel = new ArrayList<>();
        mCountryModel.addAll(countryModels);
        notifyDataSetChanged();
    }

}

6. View Holder

  • ItemViewHolder class and this class Must Extend  RecyclerView.ViewHolder

  • We already defined the XML layout list_row.xml , We are going to reuse that layout now. Inside the constructor of our custom ViewHolder, initialize the views that belong to the items of our RecyclerView.

  • I have created method bind , we this method this method inside onBindViewHolder() by passing country data model class object.
file : ItemViewHolder.java

package com.tutorialsbuzz.recyclerview.TabFragments;

import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.tutorialsbuzz.recyclerview.R;

public class ItemViewHolder extends RecyclerView.ViewHolder {

    public TextView name_TextView;
    public TextView iso_TextView;

    public ItemViewHolder(View itemView) {
        super(itemView);
        itemView.setClickable(true);
        name_TextView = (TextView) itemView.findViewById(R.id.country_name);
        iso_TextView = (TextView) itemView.findViewById(R.id.country_iso);
    }

    public void bind(CountryModel countryModel) {
        name_TextView.setText(countryModel.getName());
        iso_TextView.setText(countryModel.getisoCode());
    }

}


6. Fragment

  • Create a TabOneFragment which extends Fragment .

  • Inside the onCreateView method get the reference to recyclerview and set the LayoutManager as LayoutLayout.

  • Inside the OnActivityCreate method call to setAdpter on recyclerview instance and pass the instance of above define RVAdapter as argument.

7. Filtering Logic
  • Inside the onCreateOptionMenu get the reference of searchView widget and set onQueryTextListener on it .

  • Upon setting onQueryTextListener on SearchView override the onQueryTextChange and onQueryTextSubmit method.

  • I have Created a method filter which takes list of CountryModel and query string as parameter , inside this method we will filter the CountryModel list on one of the constraints of CountryModel (here in this example i am filter the list on name constrain , if you want you can filter on isocode ) and return the filtered list .

  • Inside the onQueryTextChange we make a call to filter method and get the return filtered list this which in-turn passed as a argument while make call to setFilter of RVAdapter .

Note : When you perform filter and then Switch between tabs in TabLayout In such scenario the recyclerview won't return the original set of data  so to overcome this you have set the onActionExapndListener on MenuItem and then inside the onMenuItemActionCollapse make a call to setFilter method on adapter instance by passing the original list as argument . 


file : TabOneFragment.java

package com.tutorialsbuzz.recyclerview.TabFragments;

import android.os.Bundle;

import androidx.fragment.app.Fragment;
import androidx.core.view.MenuItemCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.appcompat.widget.SearchView;

import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;

import com.tutorialsbuzz.recyclerview.R;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class TabOneFragment extends Fragment implements SearchView.OnQueryTextListener {

    private RecyclerView recyclerview;
    private List<CountryModel> mCountryModel;
    private RVAdapter adapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.tab_one_fragment, container, false);

        recyclerview = (RecyclerView) view.findViewById(R.id.recyclerview);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerview.setLayoutManager(layoutManager);

        return view;
    }


    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        setHasOptionsMenu(true);
        String[] locales = Locale.getISOCountries();
        mCountryModel = new ArrayList<>();

        for (String countryCode : locales) {
            Locale obj = new Locale("", countryCode);
            mCountryModel.add(new CountryModel(obj.getDisplayCountry(), obj.getISO3Country()));
        }

        adapter = new RVAdapter(mCountryModel);
        recyclerview.setAdapter(adapter);
    }


    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_main, menu);

        final MenuItem item = menu.findItem(R.id.action_search);
        final SearchView searchView = (SearchView) MenuItemCompat.getActionView(item);
        searchView.setOnQueryTextListener(this);


        item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                // Do something when collapsed
                adapter.setFilter(mCountryModel);
                return true; // Return true to collapse action view
            }

            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                // Do something when expanded
                return true; // Return true to expand action view
            }
        });

    }

    @Override
    public boolean onQueryTextChange(String newText) {
        final List<CountryModel> filteredModelList = filter(mCountryModel, newText);
        adapter.setFilter(filteredModelList);
        return true;
    }

    @Override
    public boolean onQueryTextSubmit(String query) {
        return false;
    }

    private List<CountryModel> filter(List<CountryModel> models, String query) {
        query = query.toLowerCase();

        final List<CountryModel> filteredModelList = new ArrayList<>();
        for (CountryModel model : models) {
            final String text = model.getName().toLowerCase();
            if (text.contains(query)) {
                filteredModelList.add(model);
            }
        }
        return filteredModelList;
    }
}


 

RecyclerView Related
  1. RecyclerView
  2. RecyclerView Item Click Ripple Effect
  3. RecyclerView With Item Divider
  4. RecyclerView With CardView
  5. RecyclerView GridLayout
  6. RecyclerView StaggeredGrid Layout
  7. RecyclerView Swipe To Delete
  8. Android RecyclerView Swipe to Delete Undo
  9. Android RecyclerView Interactive Swipe Like Gmail
  10. Android RecyclerView Buttons Under Swipe
  11. Android RecyclerView MultiViewType
  12. Android RecyclerView Item Drag and Drop

Read More »

Android Adding SearchView To ActionBar

 The app bar, also known as the action bar, is one of the most important design elements in your app's activities, it allows users to quickly understand how to operate your app and have a great experience .

Android SearchView widget provides a user interface for the user to enter a search query and submit a request to a search provider .

In this tutorial we will see adding SearchView widget to actionBar thru option menu .



Vector Search Icon


Create Vector Search Icon in Android Studio



1. Menu

  • Inside the res/menu/ create a xml file "menu_main.xml" .
  • Inside menu for item tag set the actionViewClass property with androidx.appcompat.widget.SearchView class and set showAsAction property with ifRoom or collapseActionView .
file : main_menu.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">

    <item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_search_24"
        android:title="@string/app_name"
        app:actionViewClass="androidx.appcompat.widget.SearchView"
        app:showAsAction="ifRoom|collapseActionView" />

</menu>

2. XML Layout


Just and empty xml layout 

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"
    tools:context=".MainActivity" />


3. MainActivity


  1. Inside onCreate function set the above defined XML Layout 
  2. Override the onCreateOptionsMenu fun of Activity and set the above defined xml layout using menuInflater .
  3. Get the reference of menu item and call getView and then type cast it androidx.appcompat.widget.SearchView.
  4. SearchView  OnQueryTextListener
    Set the OnQueryTextListener and override onQueryTextSubmit and onQueryTextChange
    // search queryTextChange Listener
    searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
    	override fun onQueryTextSubmit(p0: String?): Boolean {
    		return true
    	}
    
    	override fun onQueryTextChange(query: String?): Boolean {
    		Log.d("onQueryTextChange", "query: " + query)
    		return true
    	}
    })
     

  5. Search Menu Item Expand Collapse Callback

    Note : MenuItemCompat.setOnActionExpandListener is deprecated instead MenuItem.setOnActionExpandListener

    On Menu Item reference call setOnActionExpandListener and override onMenuItemActionCollapse and onMenuItemActionExpand
    //Expand Collapse listener
    item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
    	override fun onMenuItemActionCollapse(p0: MenuItem?): Boolean {
    		showToast("Action Collapse")
    		return true
    	}
    
    	override fun onMenuItemActionExpand(p0: MenuItem?): Boolean {
    		showToast("Action Expand")
    		return true
    	}
    })

Complete MainActivity 

file : MainActivity.kt
package com.tutorialsbuzz.toolbarsearch

import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView

class MainActivity : AppCompatActivity() {

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

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)

        val item = menu?.findItem(R.id.action_search);
        val searchView = item?.actionView as SearchView

        // search queryTextChange Listener
        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(p0: String?): Boolean {
                return true
            }

            override fun onQueryTextChange(query: String?): Boolean {
                Log.d("onQueryTextChange", "query: " + query)
                return true
            }
        })

       //Expand Collapse listener
        item.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {
            override fun onMenuItemActionCollapse(p0: MenuItem?): Boolean {
                showToast("Action Collapse")
                return true
            }

            override fun onMenuItemActionExpand(p0: MenuItem?): Boolean {
                showToast("Action Expand")
                return true
            }
        })
        return super.onCreateOptionsMenu(menu)
    }

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


Read More »

Android Adding Spinner To ActionBar

The app bar, also known as the action bar, is one of the most important design elements in your app's activities, it allows users to quickly understand how to operate your app and have a great experience .

Android Spinner Widget Provides a quick way to select one value from a set , Touching the spinner displays all available value from which user can select one .

In this tutorial we will see adding spinner widget to ActionBar thru option menu 




1. Menu


Inside menu for item tag set the actionViewClass property with AppCompatSpinner class and set showAsAction property with always(Always show this item in an actionbar) .

file : main_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/spinner"
        android:title="@string/action_spinner"
        app:actionViewClass="androidx.appcompat.widget.AppCompatSpinner"
        app:showAsAction="always" />
</menu>


2. XML Layout


Just an empty layout for MainActivity

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"
    tools:context=".MainActivity"/>


XML Layout for drop down XML layout for spinner items which will be set to adapter

file : layout_drop_title . 
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    style="?attr/spinnerDropDownItemStyle"
    android:singleLine="true"
    android:layout_width="wrap_content"
    android:layout_height="?attr/actionBarSize"
    android:textColor="@android:color/white"
    android:ellipsize="marquee"/>

XML Layout for Spinner Checked Item this layout set by calling setDropDownViewResource on adapter reference . 

file : layout_drop_list . 
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    style="?android:attr/spinnerDropDownItemStyle"
    android:singleLine="true"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:ellipsize="marquee"
    android:background="@color/colorPrimary"
    android:textColor="@android:color/white"/>


3. MainActivity


  1. Inside onCreate function set the above defined XML Layout 
  2. Override the onCreateOptionsMenu fun of Activity and set the above defined xml layout using menuInflater .
  3. Get the reference of menu item and call getView and then type cast it AppCompatSpinner.
  4. Create an ArrayAdapter and set it to spinner .
  5. Set the onItemSelectedListener for spinner widget , when uses selects the items perform the action .  


file : MainActivity.kt
package com.tutorialsbuzz.toolbarspinner

import android.os.Bundle
import android.view.Menu
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatSpinner
import java.util.*

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

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.main_menu, menu)

        val item = menu?.findItem(R.id.spinner)
        val spinner = item?.actionView as AppCompatSpinner

        var arrayAdapter =
            ArrayAdapter(this@MainActivity, R.layout.layout_drop_title, getCountries())
        arrayAdapter.setDropDownViewResource(R.layout.layout_drop_list)
        spinner.adapter = arrayAdapter

        spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {

            override fun onItemSelected(p0: AdapterView<*>?, view: View?, position: Int, id: Long) {
                showToast(getCountries()[position] + " selected")
            }

            override fun onNothingSelected(p0: AdapterView<*>?) {}
        }
        return true
    }

    private fun getCountries(): MutableList<String> {
        val mdList = mutableListOf<String>()
        for (countryISO in Locale.getISOCountries()) {
            val locale = Locale("", countryISO)
            if (!locale.displayCountry.isEmpty()) {
                mdList.add(locale.displayCountry + "  " + counrtyFlag(countryISO))
            }
        }
        return mdList
    }

    private fun counrtyFlag(countryCode: String): String {
        val flagOffset = 0x1F1E6
        val asciiOffset = 0x41
        val firstChar = Character.codePointAt(countryCode, 0) - asciiOffset + flagOffset
        val secondChar = Character.codePointAt(countryCode, 1) - asciiOffset + flagOffset
        return (String(Character.toChars(firstChar)) + String(Character.toChars(secondChar)))
    }

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





Read More »

Android RecyclerView GridLayout Drag and Drop

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 series of tutorials we have seen Basic Implementation of Android Recyclerview , In this tutorial we will see drag drop of RecyclerView items .


Lets see example of drag & drop for items of recyclerView GridLayout

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 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:id="@+id/rcv"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Data Class


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

file : src/main/assets/android_version.json



Image Resource 

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

Folder : app\src\main\res\drawable


file : Model.kt
package com.tutorialsbuzz.recyclerviewgridlayoutdragdrop

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

ItemTouchHelper CallBack For RecyclerView 


Create an abstract class ItemMoveCallback 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).

when using 
  1. LinearLayoutManager : Set direction to  ItemTouchHelper.UP or ItemTouchHelper.DOWN .
  2. GridLayoutManager    : Set direction to  ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT .

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 .call onRowMoved fun of ItemTouchHelperContract which is implements by CustomAdapter  just swap the dragged position and call notifydatasetchange .


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: ItemMoveCallback.kt
package com.tutorialsbuzz.recyclerviewgridlayoutdragdrop

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

class ItemMoveCallback(private val mAdapter: ItemTouchHelperContract) :
    ItemTouchHelper.Callback() {

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}

    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        return makeMovementFlags(dragFlags, 0)
    }

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        mAdapter.onRowMoved(viewHolder.adapterPosition, target.adapterPosition)
        return true
    }

    interface ItemTouchHelperContract {
        fun onRowMoved(fromPosition: Int, toPosition: Int)
    }
}

Interface CallBack 


Create an interface ItemTouchHelperContract which is implements by CustomAdapter , inside onRowMoved just swap the dragged position and call notifydatasetchanged
  • onRowMoved called inside onMove of ItemMoveCallback .
  • Inside CustomAdapter onRowMoved which is overriden gets callback.
    interface ItemTouchHelperContract {
        fun onRowMoved(fromPosition: Int, toPosition: Int)
    }

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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="18dp">

    <com.tutorialsbuzz.recyclerviewgridlayoutdragdrop.CirleImageView
        android:id="@+id/img"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:contentDescription="@string/app_name"
        app:civ_border_color="#e5e5e5"
        app:civ_border_width="8dp" />

    <TextView
        android:id="@+id/txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:text="Title"
        android:textSize="14sp"
        android:textStyle="bold" />

</LinearLayout>
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.recyclerviewgridlayoutdragdrop

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.row_item.view.*
import java.util.*

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

    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;
    }

    interface ClickListener {
        fun onClick(pos: Int, aView: View)
    }

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

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

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

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

        notifyItemMoved(fromPosition, toPosition)
    }

}

1. 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). 
  • Read JSON Data From Asset Folder , this will be used for binding data into RecyclerView .
  • Set GridLayoutManager to RecyclerView.
  • Create Instance of Adapter and set to RecyclerView .
  • Add CustomItemDecortion to RecyclerView .
  • Create an instance of ItemTouchHelper by passing ItemMoveCallback as parameter to its constructor.
  • Attach ItemTouchHelper to RecyclerView .
file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewgridlayoutdragdrop

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONArray
import org.json.JSONObject

class MainActivity : AppCompatActivity() {

    var touchHelper: ItemTouchHelper? = null

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

        val model = readFromAsset();

        val adapter = CustomAdapter(model, this)

        recyclerView.layoutManager = GridLayoutManager(this, 4)
        recyclerView.adapter = adapter;
        
        val callback: ItemTouchHelper.Callback = ItemMoveCallback(adapter)
        touchHelper = ItemTouchHelper(callback)
        touchHelper!!.attachToRecyclerView(recyclerView)
    }

    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
    }

}



RecyclerView Related
  1. RecyclerView
  2. RecyclerView Item Click Ripple Effect
  3. RecyclerView With Item Divider
  4. RecyclerView With CardView
  5. RecyclerView GridLayout
  6. RecyclerView StaggeredGrid Layout
  7. RecyclerView Swipe To Delete
  8. Android RecyclerView Swipe to Delete Undo
  9. Android RecyclerView Interactive Swipe Like Gmail
  10. Android RecyclerView Buttons Under Swipe
  11. Android RecyclerView MultiViewType
  12. Android RecyclerView Item Drag and Drop

Read More »

Android RecyclerView Items Drag and Drop

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 series of tutorials we have seen Basic Implementation of Android Recyclerview , In this tutorial we will see drag drop of RecyclerView items .



Lets see example of drag & drop for items of recyclerView

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 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:id="@+id/rcv"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Data Class


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

file : src/main/assets/android_version.json



file : Model.kt
package com.tutorialsbuzz.recyclerviewdragdrop

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

ItemTouchHelper CallBack For RecyclerView 


Create an abstract class ItemMoveCallback 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 drag for UP and DOWN . 


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 .call onRowMoved fun of ItemTouchHelperContract which is implements by CustomAdapter  just swap the dragged position and call notifydatasetchange .


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: ItemMoveCallback.kt
package com.tutorialsbuzz.recyclerviewdragdrop

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

class ItemMoveCallback(private val mAdapter: ItemTouchHelperContract) :
    ItemTouchHelper.Callback() {

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}

    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        return makeMovementFlags(dragFlags, 0)
    }

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        mAdapter.onRowMoved(viewHolder.adapterPosition, target.adapterPosition)
        return true
    }

    interface ItemTouchHelperContract {
        fun onRowMoved(fromPosition: Int, toPosition: Int)
    }
}

Interface CallBack 


1. Create an interface ItemTouchHelperContract which is implements by CustomAdapter , inside onRowMoved just swap the dragged position and call notifydatasetchanged
  • onRowMoved called inside onMove of ItemMoveCallback .
  • Inside CustomAdapter onRowMoved which is overriden gets callback.
    interface ItemTouchHelperContract {
        fun onRowMoved(fromPosition: Int, toPosition: Int)
    }

2. Create an StartDragListener which is implements by MainActivity , inside requestDrag fun on touchHelper reference call startDrag fun by passing viewholder.
  • requestDrag called onTouch of imageView of recyclerView item .
  • Inside MainActivity requestDrag which is overriden gets callback.
	interface StartDragListener {
		fun requestDrag(viewHolder: RecyclerView.ViewHolder?)
	}

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:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:paddingLeft="20dp"
    android:paddingTop="16dp"
    android:paddingRight="20dp"
    android:paddingBottom="16dp">

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

    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_baseline_reorder_24" />

</LinearLayout>
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.recyclerviewdragdrop

import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.row_item.view.*
import java.util.*

class CustomAdapter(
    val modelList: List<Model>,
    val mStartDragListener: StartDragListener?
) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>(),
    ItemMoveCallback.ItemTouchHelperContract {

    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.iv.setOnTouchListener(OnTouchListener { v, event ->
                if (event.action == MotionEvent.ACTION_DOWN)
                    mStartDragListener!!.requestDrag(this)

                false
            })
        }
    }

    override fun onRowMoved(fromPosition: Int, toPosition: Int) {

        if (fromPosition < toPosition) {
            for (i in fromPosition until toPosition) {
                Collections.swap(modelList, i, i + 1)
            }
        } else {
            for (i in fromPosition downTo toPosition + 1) {
                Collections.swap(modelList, i, i - 1)
            }
        }

        notifyItemMoved(fromPosition, toPosition)
    }
}


1. 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). 
  • Read JSON Data From Asset Folder , this will be used for binding data into RecyclerView .
  • Set Vertical Linear LayoutManager to RecyclerView.
  • Create Instance of Adapter and set to RecyclerView .
  • Add CustomItemDecortion to RecyclerView .
  • Create an instance of ItemTouchHelper by passing ItemMoveCallback as parameter to its constructor.
  • Attach ItemTouchHelper to RecyclerView .
file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewdragdrop

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 kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONArray
import org.json.JSONObject

class MainActivity : AppCompatActivity(), StartDragListener {

    var touchHelper: ItemTouchHelper? = null

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

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

        rcv.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        rcv.adapter = adapter;
        rcv.addItemDecoration(SimpleDividerItemDecoration(this@MainActivity))

        val callback: ItemTouchHelper.Callback = ItemMoveCallback(adapter)
        touchHelper = ItemTouchHelper(callback)
        touchHelper!!.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
    }

    override fun requestDrag(viewHolder: RecyclerView.ViewHolder?) {
        touchHelper!!.startDrag(viewHolder!!)
    }
}


RecyclerView Related
  1. RecyclerView
  2. RecyclerView Item Click Ripple Effect
  3. RecyclerView With Item Divider
  4. RecyclerView With CardView
  5. RecyclerView GridLayout
  6. RecyclerView StaggeredGrid Layout
  7. RecyclerView Swipe To Delete
  8. Android RecyclerView Swipe to Delete Undo
  9. Android RecyclerView Interactive Swipe Like Gmail
  10. Android RecyclerView Buttons Under Swipe
  11. Android RecyclerView MultiViewType
Read More »