Android RecyclerView OnScroll Show Hide Toolbar BottomNavigationView

In the previous tutorial we have seen how to show hide toolbar on scrolling reyclerview , In this tutorial we will see how to show hide both toolbar and bottomnavigationview on scrolling reyclerview . 
Here we will use CoordinatorLayout as the parent layout and place Toolbar , RecyclerView and BottomNavigationView as child view. 
 



CoordinatorLayout:


CoordinatorLayout is a super-powered FrameLayout , CoordinatorLayout is intended for two primary use cases .
  • As a top-level application decor layout .
  • As a container for a specific interaction with one or more child views .


XML Layout


file : activity_main.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout 
    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">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways" />

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

    <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" />
    
    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="?android:attr/windowBackground"
        app:layout_behavior="com.tutorialsbuzz.recyclerviewbottomnavshowhide.BottomNavigationBehavior"
        app:menu="@menu/bottom_nav_menu" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Specifying Scroll Behaviors


1. For Toolbar widget set the layout scroll flag  
       app:layout_scrollFlags="scroll|enterAlways" .

2. For RecyclerView Set the layout behavior 
   app:layout_behavior="@string/appbar_scrolling_view_behavior" .

3. For BottomNavigationView Set the custom layout behavior 
        app:layout_behavior="pkg.BottomNavigationBehavior" .

Custom Layout Behaviors For BottomNavigationBehavior


file : BottomNavigationBehavior.kt
package com.tutorialsbuzz.recyclerviewbottomnavshowhide

import android.content.Context
import android.util.AttributeSet
import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
import kotlin.math.max
import kotlin.math.min

class BottomNavigationBehavior<V : View>(context: Context, attrs: AttributeSet) :
    CoordinatorLayout.Behavior<V>(context, attrs) {

    override fun onStartNestedScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        directTargetChild: View,
        target: View,
        axes: Int,
        type: Int
    ): Boolean {
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL
    }

    override fun onNestedPreScroll(
        coordinatorLayout: CoordinatorLayout,
        child: V,
        target: View,
        dx: Int,
        dy: Int,
        consumed: IntArray,
        type: Int
    ) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
        child.translationY = max(0f, min(child.height.toFloat(), child.translationY + dy))
    }
}


Setting Up RecyclerView


1. XML Layout for RecyclerView Row Item 
 
file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/txt"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:paddingTop="16dp"
    android:paddingRight="8dp"
    android:paddingBottom="16dp"
    android:textSize="20sp" />

2. Data class 

file : Model.kt
package com.tutorialsbuzz.recyclerviewbottomnavshowhide

data class Model(val name: String) {}

3. ViewHolder Adapter for RecyclerView 

file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerviewbottomnavshowhide

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.*

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

}

Style


Add the below style property for the Activity theme in which your implementing scroll behavior
 <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>

And call setSupportActionBar(toolbar) inside activity onCreate by passing toolbar reference.

Activity


In MainActivity we will initialize and set adapter to reyclerview . 

file : MainActivity.kt  
package com.tutorialsbuzz.recyclerviewbottomnavshowhide

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*

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

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

        val adapter = CustomAdapter(getCountries(), this)
        recyclerView.adapter = adapter;

    }

    private fun getCountries(): MutableList<Model> {
        val mdList = mutableListOf<Model>()
        for (countryISO in Locale.getISOCountries()) {
            val locale = Locale("", countryISO)
            if (!locale.displayCountry.isEmpty()) {
                mdList.add(Model(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
        val flag =
            (String(Character.toChars(firstChar)) + String(Character.toChars(secondChar)))

        return flag
    }

}

Read More »

Android RecyclerView OnScroll Show hide Toolbar

In the previous series of tutorial we have seen implementation of recyclerview , In this tutorial we will how to show hide toolbar on scrolling reyclerview , i.e on scroll-up hide toolbar 
and scroll down show toolbar .

Here we will use CoordinatorLayout as the parent layout and place toolbar and recyclerview as child view.  and set the scroll flag and behavior .




CoordinatorLayout


CoordinatorLayout is a super-powered FrameLayout , CoordinatorLayout is intended for two primary use cases.
  • As a top-level application decor layout .
  • As a container for a specific interaction with one or more child views .

XML Layout



  1.  Add CoordinatorLayout as parent or root layout 
  2.  Add Toolbar and RecyclerView as child view to CoordinatorLayout
Specifying Scroll Behaviors  

1. For Toolbar widget set the layout scroll flag  
       app:layout_scrollFlags="scroll|enterAlways"

2. For RecyclerView Set the layout behavior 
    app:layout_behavior="@string/appbar_scrolling_view_behavior"

file : activity_main.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout 
    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">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways" />

    </com.google.android.material.appbar.AppBarLayout>
    
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingTop="6dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    
</androidx.coordinatorlayout.widget.CoordinatorLayout>


Setting Up RecyclerView


1 XML Layout for RecyclerView Row Item 

file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/txt"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:paddingTop="16dp"
    android:paddingRight="8dp"
    android:paddingBottom="16dp"
    android:textSize="20sp" />

2. Data class

file : Model.kt
package com.tutorialsbuzz.recyclerviewtoolbarshowhide

data class Model(val name: String) {}

3 ViewHolder Adapter for RecyclerView

file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerviewtoolbarshowhide

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.*

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

}

Activity


In MainActivity we will initialize and set adapter to reyclerview .

file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewtoolbarshowhide

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*

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

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

        val adapter = CustomAdapter(getCountries(), this)
        recyclerView.adapter = adapter;
    }

    private fun getCountries(): MutableList<Model> {
        val mdList = mutableListOf<Model>()
        for (countryISO in Locale.getISOCountries()) {
            val locale = Locale("", countryISO)
            if (!locale.displayCountry.isEmpty()) {
                mdList.add(Model(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
        val flag =
            (String(Character.toChars(firstChar)) + String(Character.toChars(secondChar)))

        return flag
    }

}

Read More »

Android BottomNavigationView Kotlin

Android BottomNavigationView Represents a standard bottom navigation bar for application. It is an implementation of  material design bottom navigation Bottom navigation bars make it easy for users to explore and switch between top-level views in a single tap.They should be used when an application has three to five top-level destinations .


While Creating New Project In Android Studio  File > New Project . Select a Project Template "Bottom Navigation Activity


1. MENU


The BottomNavigationView bar contents can be populated by specifying a menu resource file. Each menu item title,icon and enabled state will be used for displaying bottom navigation bar items. Menu items can also be used for pragmatically selecting which destination is currently active.  
 
file : bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/navigation_home"
        android:icon="@drawable/ic_home_black_24dp"
        android:title="@string/title_home" />

    <item
        android:id="@+id/navigation_dashboard"
        android:icon="@drawable/ic_dashboard_black_24dp"
        android:title="@string/title_dashboard" />

    <item
        android:id="@+id/navigation_notifications"
        android:icon="@drawable/ic_notifications_black_24dp"
        android:title="@string/title_notifications" />

</menu>


2. Drawable


Place Drawable inside res/drawable 

3. XML Layout


Main layout contains BottomNavigationView and fragment .

1. The Above defined menu is set to BottomNavigationView .
2. Fragment is set to NavHostFragment  and mapped to below defined navigation graph  .

file : activity_main.xml
<?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:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingTop="?attr/actionBarSize">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" />

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

</androidx.constraintlayout.widget.ConstraintLayout>

XML Layout for each fragment .


       

The below diagram is very explaintory how BottomNavigationView is mapped to menu with navigation graph .

4. NavigationGraph

  •  NavHostFragment provides an area within your layout for self-contained navigation to occur .
  •  NavHostFragment is intended to be used as the content area within a layout resource .
  •  NavHostFragment has NavController  that defines valid navigation within Navigation host .
file : res/navigation/mobile_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation
    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:id="@+id/mobile_navigation"
    app:startDestination="@+id/navigation_home">

    <fragment
        android:id="@+id/navigation_home"
        android:name="com.tutorialsbuzz.bottomnavexample.ui.home.HomeFragment"
        android:label="@string/title_home"
        tools:layout="@layout/fragment_home" />

    <fragment
        android:id="@+id/navigation_dashboard"
        android:name="com.tutorialsbuzz.bottomnavexample.ui.dashboard.DashboardFragment"
        android:label="@string/title_dashboard"
        tools:layout="@layout/fragment_dashboard" />

    <fragment
        android:id="@+id/navigation_notifications"
        android:name="com.tutorialsbuzz.bottomnavexample.ui.notifications.NotificationsFragment"
        android:label="@string/title_notifications"
        tools:layout="@layout/fragment_notifications" />
        
</navigation>

5. Activity


1. Get the reference of BottomNavigationView and call setupWithNavController by passing NavHostFragment instance .
val navController = findNavController(R.id.nav_host_fragment)
navView.setupWithNavController(navController)

2. Update ActionBar title when there is navigation , just call setupActionBarWithNavController by passing navController and appBarConfiguration instance. 
val appBarConfiguration = AppBarConfiguration(setOf(
	R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications))
setupActionBarWithNavController(navController, appBarConfiguration)

MainActivity Full code

file : MainActivity.kt
package com.tutorialsbuzz.bottomnavexample

import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val navView: BottomNavigationView = findViewById(R.id.nav_view)

        val navController = findNavController(R.id.nav_host_fragment)
        navView.setupWithNavController(navController)

        // Passing each menu ID as a set of Ids because each
        // menu should be considered as top level destinations.
        val appBarConfiguration = AppBarConfiguration(setOf(
            R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications))
        setupActionBarWithNavController(navController, appBarConfiguration)
    }
}


Read More »

Android RecyclerView Collection




Sample RecyclerView Example
RecyclerView Item Click Ripple Effect
RecyclerView Item
Divider
RecyclerView With CardView
RecyclerView GridLayout
RecyclerView StaggeredgridLayout
RecyclerView Swipe to delete
RecyclerView Swipe to delete With Undo
RecyclerView Interactive Swipe Like Gmail
Android RecyclerView Buttons Under Swipe
RecyclerView MultiViewType
RecyclerView Item Drag and Drop
RecyclerView GridLayout Drag and Drop
Filter RecyclerView Using SearchView In Java
RecyclerView Filter Animation
Android RecyclerView Filter Using Filterable

More To Come...
Read More »

Android RecyclerView Filter Using Filterable

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 Using filterable interface .




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 'androidx.recyclerview:recyclerview:1.1.0'
}


2. Menu


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

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">

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

</menu>

3. XML Layout


Create XML Layout for MainActivity and add recyclerView widget to it .

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

<androidx.recyclerview.widget.RecyclerView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="10dp" />

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 will have one TextView  .

file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/txt"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="8dp"
    android:paddingTop="16dp"
    android:paddingRight="8dp"
    android:paddingBottom="16dp"
    android:textSize="20sp" />

4. Data


To Load Data Into RecyclerView , we will use Locale class to get full list of country and we will also create emoji using country iso code .

file : Model.kt
package com.tutorialsbuzz.recyclerviewfilter

data class Model(val name: String) {}

5. Adapter ViewHolder Filterable


5.1  Adapter

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

RecyclerView.Adapter has three abstract methods that we must override .
  • onCreateViewHolder() :Inside this method we specify the layout that each item of the RecyclerView should use .onCreateViewHolder has return type of RecyclerView.ViewHolder which represent each row of recyclerView. Using Inflator get the view of above defined row_item and pass it to viewholder constructor and then return.
  • onBindViewHolder() : Inside this method data will be displayed at the specified position .
  • getItemCount() : Returns the total number of items in the data set held by the adapter.

5.2 ViewHolder

Create Inner class ViewHolder inside CustomAdapter class extend it to RecyclerView.ViewHolder , add bind function which takes above defined model data class object .

5.3 Filterable 

Implement Filterable interface to CustomAdapter and override and getFilter() , Inside this method create an instance of filter and override the performFiltering and publishResults function 
  • performFiltering : This function takes constraint as parameter , list is filtered based on constraint the list is filtered , which is then set to FilterResults and then return FilterResults instance .
  • publishResults : This function  it takes FilterResults as parameter which is returned from performFiltering function , Inside this we will set filtered list to adapter and call notifyDataSetChanged .

file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerviewfilter

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.row_item.view.*
import kotlin.collections.ArrayList

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

    var countryFilterList = ArrayList<Model>()
    // exampleListFull . exampleList

    init {
        countryFilterList = countryList as ArrayList<Model>
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as ViewHolder).bind(countryFilterList.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 countryFilterList.size
    }

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

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

    override fun getFilter(): Filter {
        return object : Filter() {
            override fun performFiltering(constraint: CharSequence?): FilterResults {
                val charSearch = constraint.toString()
                if (charSearch.isEmpty()) {
                    countryFilterList = countryList as ArrayList<Model>
                } else {
                    val resultList = ArrayList<Model>()
                    for (row in countryList) {
                        if (row.name.toLowerCase().contains(constraint.toString().toLowerCase())) {
                            resultList.add(row)
                        }
                    }
                    countryFilterList = resultList
                }
                val filterResults = FilterResults()
                filterResults.values = countryFilterList
                return filterResults
            }

            override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                countryFilterList = results?.values as ArrayList<Model>
                notifyDataSetChanged()
            }
        }
    }

}

6. 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). 
  • Create full list of country using Locale class ,this list will be set to recyclerView.
  • Set Vertical Linear LayoutManager to RecyclerView.
  • Create Instance of Adapter and set to RecyclerView .
SearchView Widget :
  • Get the reference of menu item and call getView and then type cast it androidx.appcompat.widget.SearchView.
  • On SearchView instance call setOnQueryTextListener and override onQueryTextSubmit and onQueryTextChange.
  • Inside the onQueryTextChange function get the instance of filter using adapter and call filter function by passing query string .


file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewfilter

import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.widget.SearchView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*

class MainActivity : AppCompatActivity() {

    var adapter: CustomAdapter? = null
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        adapter = CustomAdapter(getCountries(), this)

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

    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

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

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

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

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

        return super.onCreateOptionsMenu(menu)
    }


    private fun getCountries(): MutableList<Model> {
        val mdList = mutableListOf<Model>()
        for (countryISO in Locale.getISOCountries()) {
            val locale = Locale("", countryISO)
            if (!locale.displayCountry.isEmpty()) {
                mdList.add(Model(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
        val flag =
            (String(Character.toChars(firstChar)) + String(Character.toChars(secondChar)))

        return flag
    }

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

}




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
  13. Android RecyclerView GridLayout Drag and Drop
  14. Android Filter RecyclerView Using SearchView In Java
  15. Android RecyclerView Filter Animation

Read More »

Android Adding Custom Layout To ActionBar

The app bar, also known as the action bar, is one of the most important design elements in your app's activities, because it provides a visual structure and interactive elements that are familiar to users. Using the app bar makes your app consistent with other Android apps , allowing users to quickly understand how to operate your app and have a great experience

In the previous post we have seen adding custom view or widget like Spinner and SearchView to actionBar Thru menu resource .

In this tutorial we will see adding Custom XML Layout to ActionBar.



 


1. XML Layout


Create XML Layout for MainActivity 

file : activity_main.xml
<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</androidx.constraintlayout.widget.ConstraintLayout>

Custom Layout For ActionBar


file : actionbar_layout_content.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="match_parent"
    android:gravity="center"
    android:orientation="horizontal">

    <!-- Search Layout-->
    <include
        layout="@layout/search_layout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <!-- Image View Layout-->
    <ImageView
        android:id="@+id/moreOption"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginStart="5dp"
        android:layout_marginLeft="5dp"
        android:contentDescription="@string/app_name"
        android:src="@drawable/ic_more" />

</LinearLayout>


2. Main Activity


  • Inside onCreate function set the above defined XML Layout 
  • GetActionBar instance and set custom view by passing above defined xml layout file .

file :  MainActivity.kt 
package com.tutorialsbuzz.customtoolbarlayout

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu
import kotlinx.android.synthetic.main.search_layout.*
import kotlinx.android.synthetic.main.actionbar_layout_content.*

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

        val actionBar = supportActionBar
        // set display options of the ActionBar
        actionBar?.apply {
            setDisplayShowTitleEnabled(false)
            setDisplayShowHomeEnabled(false)
            setDisplayShowCustomEnabled(true)
            setCustomView(R.layout.actionbar_layout_content)
        }
        
    }

}







Read More »

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 »