Added a SearchView to MerchantsFragment's Toolbar, with an autocomplete feature that once the user starts writing shows suggestions to the user and listens when he selects one of those suggestions. The suggestions show dummy data for now, but it will query the database to obtain such suggestions.

This commit is contained in:
Severiano Jaramillo 2019-01-28 15:02:49 -06:00
parent 8aab7c03ac
commit db58d601f4
2 changed files with 75 additions and 2 deletions

View file

@ -2,22 +2,28 @@ package cy.agorise.bitsybitshareswallet.fragments
import android.Manifest import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.SearchManager
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.database.Cursor
import android.database.MatrixCursor
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.fragment.app.Fragment
import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment import com.google.android.gms.maps.SupportMapFragment
import cy.agorise.bitsybitshareswallet.R import cy.agorise.bitsybitshareswallet.R
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.provider.BaseColumns
import android.view.* import android.view.*
import android.widget.PopupWindow import android.widget.PopupWindow
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.cursoradapter.widget.SimpleCursorAdapter
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders import androidx.lifecycle.ViewModelProviders
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
@ -28,6 +34,7 @@ import com.google.maps.android.MarkerManager
import com.google.maps.android.clustering.Cluster import com.google.maps.android.clustering.Cluster
import com.google.maps.android.clustering.ClusterItem import com.google.maps.android.clustering.ClusterItem
import com.google.maps.android.clustering.ClusterManager import com.google.maps.android.clustering.ClusterManager
import com.jakewharton.rxbinding3.appcompat.queryTextChangeEvents
import cy.agorise.bitsybitshareswallet.database.entities.Merchant import cy.agorise.bitsybitshareswallet.database.entities.Merchant
import cy.agorise.bitsybitshareswallet.database.entities.Teller import cy.agorise.bitsybitshareswallet.database.entities.Teller
import cy.agorise.bitsybitshareswallet.utils.Constants import cy.agorise.bitsybitshareswallet.utils.Constants
@ -35,10 +42,13 @@ import cy.agorise.bitsybitshareswallet.utils.MerchantClusterRenderer
import cy.agorise.bitsybitshareswallet.utils.TellerClusterRenderer import cy.agorise.bitsybitshareswallet.utils.TellerClusterRenderer
import cy.agorise.bitsybitshareswallet.utils.toast import cy.agorise.bitsybitshareswallet.utils.toast
import cy.agorise.bitsybitshareswallet.viewmodels.MerchantViewModel import cy.agorise.bitsybitshareswallet.viewmodels.MerchantViewModel
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import java.lang.Exception import java.lang.Exception
import java.util.concurrent.TimeUnit
class MerchantsFragment : Fragment(), OnMapReadyCallback { class MerchantsFragment : Fragment(), OnMapReadyCallback, SearchView.OnSuggestionListener {
companion object { companion object {
private const val TAG = "MerchantsFragment" private const val TAG = "MerchantsFragment"
@ -53,6 +63,11 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback {
private var mMarkerManager: MarkerManager? = null private var mMarkerManager: MarkerManager? = null
/** Keeps track of all RxJava disposables, to make sure they are all disposed when the fragment is destroyed */
private var mDisposables = CompositeDisposable()
private var mSearchView: SearchView? = null
// Cluster managers to create custom merchants and tellers clusters with a custom behavior too // Cluster managers to create custom merchants and tellers clusters with a custom behavior too
private var mMerchantClusterManager: ClusterManager<Merchant>? = null private var mMerchantClusterManager: ClusterManager<Merchant>? = null
private var mTellerClusterManager: ClusterManager<Teller>? = null private var mTellerClusterManager: ClusterManager<Teller>? = null
@ -128,6 +143,51 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback {
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_merchants, menu) inflater.inflate(R.menu.menu_merchants, menu)
// Adds listener for the SearchView
val searchItem = menu.findItem(R.id.menu_search)
mSearchView = searchItem.actionView as SearchView
mSearchView?.suggestionsAdapter = SimpleCursorAdapter(context, android.R.layout.simple_list_item_1, null,
arrayOf(SearchManager.SUGGEST_COLUMN_TEXT_1), intArrayOf(android.R.id.text1))
// Add listener to changes in the SearchView's text to update the suggestions
mSearchView?.queryTextChangeEvents()
?.skipInitialValue()
?.debounce(500, TimeUnit.MILLISECONDS)
?.map { it.queryText.toString().toLowerCase() }
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe {
updateSearchViewSuggestions(it)
}?.let {
mDisposables.add(it)
}
mSearchView?.setOnSuggestionListener(this)
}
private fun updateSearchViewSuggestions(query: String) {
// TODO make call to the db to obtain the list of merchants/tellers
val cursor = MatrixCursor(arrayOf(BaseColumns._ID, SearchManager.SUGGEST_COLUMN_TEXT_1))
for (i in 1..3) {
cursor.addRow(arrayOf(i.toString(), "Merchant $i"))
}
mSearchView?.suggestionsAdapter?.changeCursor(cursor)
}
override fun onSuggestionSelect(position: Int): Boolean {
return onSuggestionClick(position)
}
override fun onSuggestionClick(position: Int): Boolean {
val cursor = mSearchView?.suggestionsAdapter?.getItem(position) as Cursor?
val name = cursor?.getString(cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1)) ?: ""
cursor?.close()
context?.toast(name)
return true
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
@ -418,4 +478,10 @@ class MerchantsFragment : Fragment(), OnMapReadyCallback {
dismissPopupWindow() dismissPopupWindow()
} }
override fun onDestroy() {
super.onDestroy()
if (!mDisposables.isDisposed) mDisposables.dispose()
}
} }

View file

@ -2,6 +2,13 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_search"
android:icon="@drawable/ic_search"
android:title="@string/title_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always"/>
<item <item
android:id="@+id/menu_filter" android:id="@+id/menu_filter"
android:icon="@drawable/ic_more_vert" android:icon="@drawable/ic_more_vert"