I have a fragment for logging into my app, and that fragment is displayed via an activity. If the user logs in successfully, then I need to add a menu to the parent activity. How to do that??
Menus are inflated during Activity creation. So, in your case, you can only play with it visibility.
This is not a working code but just that you can get the idea:
class MainActivity : AppCompatActivity(), MFragment.Listener {
...
var menu: Menu? = null
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
this.menu = menu
menuInflater.inflate(R.menu.create_order_menu, menu)
menu?.findItem(R.id.m_item)?.apply {
isVisible = false
}
return true
}
override fun onLoginSucceed() {
menu?.findItem(R.id.m_item)?.apply {
isVisible = true
}
}
}
MFragment
class MFragment : Fragment() {
private var listener: Listener? = null
interface Listener {
fun onLoginSucceed()
}
override fun onAttach(context: Context) {
super.onAttach(context)
listener = context as Listener
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
listener?.onLoginSucceed()
}
companion object {
#JvmStatic fun newInstance() = MFragment()
}
}
Related
I have an activity that has a SearchView that I use to enter a query, my app then uses to query to access an API. My activity further contains a fragment, and within this fragment I have my observer.
Further I have my ViewModel, which makes the API call when given a query. However, my observer is never notified about the update, and thus my view never updates. Unless I call it directly from my ViewModel upon initiation. I'll show it specifically here:
ViewModel
class SearchViewModel : ViewModel() {
val booksResponse = MutableLiveData<MutableList<BookResponse>>()
val loading = MutableLiveData<Boolean>()
val error = MutableLiveData<String>()
init {
getBooks("How to talk to a widower")
}
fun getBooks(bookTitle: String) {
GoogleBooksService.api.getBooks(bookTitle).enqueue(object: Callback<ResponseWrapper<BookResponse>> {
override fun onFailure(call: Call<ResponseWrapper<BookResponse>>, t: Throwable) {
onError(t.localizedMessage)
}
override fun onResponse(
call: Call<ResponseWrapper<BookResponse>>,
response: Response<ResponseWrapper<BookResponse>>
) {
if (response.isSuccessful){
val books = response.body()
Log.w("2.0 getFeed > ", Gson().toJson(response.body()));
books?.let {
// booksList.add(books.items)
booksResponse.value = books.items
loading.value = false
error.value = null
Log.i("Content of livedata", booksResponse.getValue().toString())
}
}
}
})
}
private fun onError(message: String) {
error.value = message
loading.value = false
}
}
Query Submit/ Activity
class NavigationActivity : AppCompatActivity(), SearchView.OnQueryTextListener, BooksListFragment.TouchActionDelegate {
lateinit var searchView: SearchView
lateinit var viewModel: SearchViewModel
private val mOnNavigationItemSelectedListener =
BottomNavigationView.OnNavigationItemSelectedListener { menuItem ->
when (menuItem.itemId) {R.id.navigation_search -> {
navigationView.getMenu().setGroupCheckable(0, true, true);
replaceFragment(SearchListFragment.newInstance())
return#OnNavigationItemSelectedListener true
}
R.id.navigation_books -> {
navigationView.getMenu().setGroupCheckable(0, true, true);
replaceFragment(BooksListFragment.newInstance())
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
replaceFragment(SearchListFragment.newInstance())
navigationView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
//Set action bar color
val actionBar: ActionBar?
actionBar = supportActionBar
val colorDrawable = ColorDrawable(Color.parseColor("#FFDAEBE9"))
// actionBar!!.setBackgroundDrawable(colorDrawable)
// actionBar.setTitle(("Bobs Books"))
setSupportActionBar(findViewById(R.id.my_toolbar))
viewModel = ViewModelProvider(this).get(SearchViewModel::class.java)
}
override fun onBackPressed() {
super.onBackPressed()
navigationView.getMenu().setGroupCheckable(0, true, true);
}
private fun replaceFragment(fragment: Fragment){
supportFragmentManager
.beginTransaction()
.replace(R.id.fragmentHolder, fragment)
.commit()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.book_search_menu, menu)
val searchItem = menu.findItem(R.id.action_search)
searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
searchView.queryHint = "Search for book"
/*searchView.onActionViewExpanded()
searchView.clearFocus()*/
// searchView.setIconifiedByDefault(false)
return true
}
override fun onQueryTextSubmit(query: String): Boolean {
//replaces fragment if in BooksListFragment when searching
replaceFragment(SearchListFragment.newInstance())
val toast = Toast.makeText(
applicationContext,
query,
Toast.LENGTH_SHORT
)
toast.show()
searchView.setQuery("",false)
searchView.queryHint = "Search for book"
// viewModel.onAddBook(Book(title = query!!, rating = 5, pages = 329))
Log.i("Query fra text field", query)
// viewModel.getBooks(query)
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
return false
}
override fun launchBookFragment(bookId: Book) {
supportFragmentManager
.beginTransaction()
.replace(R.id.fragmentHolder, com.example.bobsbooks.create.BookFragment.newInstance(bookId.uid))
.addToBackStack(null)
.commit()
navigationView.getMenu().setGroupCheckable(0, false, true);
}
}
Fragment
class SearchListFragment : Fragment() {
lateinit var viewModel: SearchViewModel
lateinit var contentListView: SearchListView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_search_list, container, false).apply {
contentListView = this as SearchListView
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindViewModel()
setContentView()
}
private fun setContentView(){
contentListView.initView()
}
private fun bindViewModel(){
Log.i("ViewmodelCalled", "BindViewModel has been called")
viewModel = ViewModelProvider(this).get(SearchViewModel::class.java)
viewModel.booksResponse.observe(viewLifecycleOwner, Observer {list ->
list?.let {
Log.i("Observer gets called", "Updatelistgetscalled")
contentListView.updateList(list)
}
} )
viewModel.error.observe(viewLifecycleOwner, Observer { errorMsg ->
})
viewModel.loading.observe(viewLifecycleOwner, Observer { isLoading ->
})
}
companion object {
fun newInstance(): SearchListFragment {
return SearchListFragment()
}
}
When I put the getBooks call into my Viewmodel Init, it will do everything correctly. It gets the bookresponse through the API, adds it to my LiveData and notifies my adapter.
However, if I instead delete that and call it through my Querysubmit in my Activity, it will, according to my logs, get the data and put it into my booksReponse:LiveData, but thats all it does. The observer is never notifed of this change, and thus the adapter never knows that it has new data to populate its views.
I feel like I've tried everything, I even have basically the same code working in another app, where it runs entirely in an activity instead of making the query in an activity, and rest is called in my fragment. My best guess is this has an impact, but I cant figure out how.
As per your explanation
However, if I instead delete that and call it through my Querysubmit in my Activity, it will, according to my logs, get the data and put it into my booksReponse:LiveData, but thats all it does. The observer is never notifed of this change, and thus the adapter never knows that it has new data to populate its views.
the problem is you are initializing SearchViewModel in both activity & fragment, so fragment doesn't have the same instance of SearchViewModel instead you should use shared viewmodel in fragment like :
viewModel = ViewModelProvider(requireActivity()).get(SearchViewModel::class.java)
I have 2 menu items (filter button and search button) that allow to filter or search within a list. When a list item is selected, it is given to the view throught the viewModel's LiveData called listItemSelected, when this happen, we move from the ListFragment to the DetailFragment and hide the 2 buttons from the menu, as they aren't relevent here.
My problem is on configuration change, for example on screen rotation, the old activity is destroyed, and the new Activity.onCreate() function is called before onCreateOptionsMenu(), so I don't know how can I set back the menu state properly.
Simplified code sample below, right now it just crash on filterMenuItem.isVisible = it == null because filterMenuItem is not initialized at this point.
class MyActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
private lateinit var filterMenuItem: MenuItem
private lateinit var searchMenuItem: MenuItem
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = getViewModel { injector.myViewModel }
viewModel.listItemSelected.observe(this, Observer {
filterMenuItem.isVisible = it == null
searchMenuItem.isVisible = it == null
})
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
menu!!.apply {
filterMenuItem = findItem(R.id.main_menu_filter)
searchMenuItem = findItem(R.id.main_menu_search)
}
return true
}
How about observing data after you initialized the MenuItem's:
class MyActivity : AppCompatActivity() {
private lateinit var viewModel: MyViewModel
private lateinit var filterMenuItem: MenuItem
private lateinit var searchMenuItem: MenuItem
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = getViewModel { injector.myViewModel }
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
menu!!.apply {
filterMenuItem = findItem(R.id.main_menu_filter)
searchMenuItem = findItem(R.id.main_menu_search)
}
// observe after the menu items are initialized
viewModel.listItemSelected.observe(this, Observer {
filterMenuItem.isVisible = it == null
searchMenuItem.isVisible = it == null
})
return true
}
}
How can the position of a RecyclerView be retained when Activity has restarted? I've considered using something like savedInstanceState but there doesn't seem to be a simple away of doing this without using dozens of lines of code.
Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.md)
if (savedInstanceState == null) supportFragmentManager.beginTransaction()
.replace(R.id.master_container, MyFragment())
.commit()
}
}
Fragment
class MyFragment : androidx.fragment.app.Fragment() {
private lateinit var mRecyclerView: RecyclerView
private var myAdapter: AdapterMain? = null
private val myList = ArrayList<RVItem>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.activity_main, container, false)
myRecyclerView = view.myRecyclerView
myList.add(RVItem("Item A"))
myList.add(RVItem("Item B"))
myList.add(RVItem("Item C"))
...
myList.add(RVItem("Item Y"))
myList.add(RVItem("Item Z"))
myAdapter = AdapterMain(activity!!, myList)
myRecyclerView.adapter = mAdapter
return view
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
val mInflater = Objects.requireNonNull<androidx.fragment.app.FragmentActivity>(activity).menuInflater
mInflater.inflate(R.menu.main, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_restart -> {
restartActivity()
true
}
else -> super.onOptionsItemSelected(item)
}
}
private fun restartActivity() {
startActivity(
Intent(view!!.context, MainActivity::class.java)
)
activity!!.overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
activity!!.finish()
}
}
RecyclerView restores scroll position natively by implementing view save state and requires no extra code on your part.
What you're doing incorrectly is "restarting" your activity. By calling start and finish you're destroying current activity and creating new instance. It will never receive savedInstanceState because it's not the same activity.
Instead you should use activity.recreate() to properly recreate the activity instance. It will receive savedInstanceState and implicitly restore view state including RecyclerView scroll.
i have an issue with an app that use ViewPager for display fragment. All works fine until the app goes in background and be killed from OS. It seems that after restore i have 2 IncidentScreenFragment that handle events, one with a null presenter (MVP) that crash my app.
My HomeActivity looks like:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
presenter.onViewCreated()
initViews(savedInstanceState)
}
private fun initViews(savedInstanceState: Bundle?){
mapView.onCreate(savedInstanceState)
mapView.getMapAsync(this)
initFragment()
initMenu()
}
private fun initFragment(){
homeFragment = HomeScreenFragment.newInstance()
incidentFragment = IncidentScreenFragment.newInstance()
chatFragment = ChatFragment.newInstance()
weatherFragment = WeatherFragment.newInstance()
viewPager.adapter = ViewPagerAdapter(supportFragmentManager, this)
viewPager.offscreenPageLimit = 4
viewPager?.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {bottom_navigation.currentItem = position}
})
}
override fun getFragmentByPos(pos: Int): Fragment {
return when(pos){
0 -> homeFragment
1 -> incidentFragment
2 -> chatFragment
3 -> weatherFragment
else -> {
homeFragment
}
}
}
And my Adapter:
class ViewPagerAdapter internal constructor(fm: FragmentManager, activity:infinite_software.intelligence_center.intelligencecenter.ui.home.FragmentManager) : FragmentPagerAdapter(fm) {
private val COUNT = 4
private val activity = activity
override fun getItem(position: Int): Fragment{
var fragment: Fragment? = null
when (position) {
0 -> fragment = activity.getFragmentByPos(0)
1 -> fragment = activity.getFragmentByPos(1)
2 -> fragment = activity.getFragmentByPos(2)
3 -> fragment = activity.getFragmentByPos(3)
}
return fragment!!
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
super.destroyItem(container, position, `object`)
}
override fun getCount(): Int {
return COUNT
}
override fun getPageTitle(position: Int): CharSequence? {
return "Section " + (position + 1)
}
}
Each Fragment have a static method that return new Fragment:
companion object {
fun newInstance(): HomeScreenFragment {
return HomeScreenFragment()
}
}
When the app has been killed in background i figure out that there is 2 objects (Fragment) that listen to event, one with Presenter correctly instantiate and one without.
Below my abstract BaseFragment class:
abstract class BaseFragment<P : BasePresenter<BaseView>> : BaseView,Fragment() {
protected lateinit var presenter: P
override fun getContext(): Context {
return activity as Context
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter = instantiatePresenter()
}
override fun showError(error: String) {
(activity as BaseActivity<BasePresenter<BaseView>>).showError(error)
}
override fun showError(errorResId: Int) {
(activity as BaseActivity<BasePresenter<BaseView>>).showError(errorResId)
}
abstract fun onBackPressed(): Boolean
/**
* Instantiates the presenter the Fragment is based on.
*/
protected abstract fun instantiatePresenter(): P
abstract val TAG: String
Incident Fragment code:
class IncidentScreenFragment: BaseFragment<IncidentScreenPresenter>(), BaseView, IncidentView, AlertFilterListener, AlertItemClickListener, IncidentDetailListener {
var rvAdapter : IncidentAdapter? = null
var state : Int = LIST_STATE
override fun instantiatePresenter(): IncidentScreenPresenter {
return IncidentScreenPresenter(this)
}
override val TAG: String
get() = "INCIDENT"
override fun getContext(): Context {
return activity as Context
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
return inflater.inflate(R.layout.fragment_incident, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initViews()
presenter.onViewCreated()
initObserve()
}
private fun initViews(){
//Reclycler view
alertRV.layoutManager = LinearLayoutManager(context)
rvAdapter = IncidentAdapter(ArrayList(), context, this)
alertRV.adapter = rvAdapter
//Apply Listeners
headerBox.setFilterListener(this)
incidentDetailView.setListener(this)
}
override fun initObserve() {
//Init observe presenter model
val alertObserver = Observer<ArrayList<AlertModel>> { alerts ->
Timber.d("Data received from Presenter [$alerts]")
showAlertList(alerts)
}
presenter.filteredAlertList.observe(context as BaseActivity<BasePresenter<BaseView>>,alertObserver)
}
override fun updateThisFilters(boxState: Boolean, level: Int) {
presenter.updateFilterList(boxState,level)
}
fun showOnlyThisLevel(level:Int){
presenter.showOnlyThisLevel(level)
headerBox.disableBoxExcept(level)
}
fun showAlertList(list: ArrayList<AlertModel>){
rvAdapter?.updateData(list)
}
override fun onItemClick(model: AlertModel) {
presenter.loadAlertDetail(model)
}
override fun showAlertDetail(model: AlertModel) {
incidentDetailView.setUpFromModel(model)
WhiteWizard.slideLeftEffect(incidentDetailView,incidentListRootElement)
state = DETAIL_STATE
}
override fun onbackFromDetailPressed() {
WhiteWizard.slideRightEffect(incidentListRootElement,incidentDetailView)
state = LIST_STATE
}
override fun showLoader() {
loaderIncident.visibility = View.VISIBLE
}
override fun hideLoader() {
loaderIncident.visibility = View.INVISIBLE
}
override fun onBackPressed(): Boolean {
when(state){
LIST_STATE -> return false
DETAIL_STATE -> {
onbackFromDetailPressed()
return true
}
else -> return false
}
}
fun newInstance(): IncidentScreenFragment {
return IncidentScreenFragment()
}
}
When i click on the button in homePage to display fragment content i got:
Process: XXXXXX, PID: 3192
kotlin.UninitializedPropertyAccessException: lateinit property presenter has not been initialized
at infinite_software.intelligence_center.intelligencecenter.base.BaseFragment.getPresenter(BaseFragment.kt:11)
at XXXXXX.ui.home.incidentScreen.IncidentScreenFragment.showOnlyThisLevel(IncidentScreenFragment.kt:78)
at XXXXXX.ui.home.HomeActivity.filterDataWithSeverity(HomeActivity.kt:110)
at XXXXXX.ui.home.homeScreen.HomeScreenFragment.filterBy(HomeScreenFragment.kt:76)
at XXXXXX.ui.home.homeScreen.HomeScreenFragment$initViews$5.onClick(HomeScreenFragment.kt:56)
If i try to print the id of Fragment, i obtain 2 different ids from method call showOnlyThisLevel() and onBackPressed(). What i miss ?
After doing some research, it seems that the problem stems from the misnaming of FragmentPagerAdapter's method - being named getItem(), but not clearly specifying that the abstract method getItem(int position) is supposed to return a new instance of a fragment rather than just "get an instance of one".
Of course, there is not much we can do about an incorrect name after it's been out in the wild for 7 years, but at least we can fix the bug that stems from this issue in your code ;)
Without further ado, the cause of your NPE is that onCreateView (where your Presenter is instantiated) is never called.
This happens because you are creating the fragment here:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
homeFragment = HomeScreenFragment.newInstance()
incidentFragment = IncidentScreenFragment.newInstance()
}
You return this fragment from inside getItem(int position) in your FragmentPagerAdapter:
override fun getItem(position: Int): Fragment = when(position) {
...
1 -> activity.incidentFragment
...
}
So what we know about activity.incidentFragment is that in it, onCreateView() is never called.
This is caused by the fact that it's never actually added to a FragmentManager and never displayed on the screen.
That's because super.onCreate(savedInstanceState) in Activity recreates all Fragments, using their no-args constructor, via reflection, while keeping their tag (see findFragmentByTag).
So as you can see in this answer, or as I can quote here:
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
The getItem(position) method is only called if the Fragment is not found by the fragment tag that the FragmentPagerAdapter sets for the fragment, which IS automatically recreated after low memory condition kills your app.
Therefore, YOUR new fragment (that you create by hand in the Activity) is NEVER used, and therefore it has no view, never initialized, never added to FragmentManager, it's not the same instance as what's actually inside your ViewPager, and it crashes when you call it. Boom!
Solution is to instantiate the Fragment inside FragmentPagerAdapter's getItem(position) method. To get an instance of the fragment, use this answer.
I have an Activity with two child fragments Timeline and Milestones. Both these fragments contain listviews populated by a custom Cursor adapter
Here is a graphical Representation:
Now when I am on TIMELINE and I open up the searchview, I type something all is good I get the desired result. But when I navigate from Timeline to Milestones with some text in the searchview the searchview does not get cleared, so I get filtered results on the Milestones page too and acccording to the paramaters I provided in Timeline.
I am using AppCompact lib to develop my ActionBar. The tabs in there are not ActionBar Tabs but simple SlidingTabLayout.
So far I have tried using
getActivity().supportInvalidateOptionsMenu(); in onResume() of both the fragments, does not work.
I have tried searchView.setQuery("",false) - does not work and randomly gives me a NPE.
SO what do I miss here?
You can take a look on my example, where I showed how to control searchView between fragments.
Firstly. You need to create BaseFragment, which works with context of activity with appBarLayout.
open class BaseFragment: Fragment() {
lateinit var rootActivity: MainActivity
lateinit var appBarLayout: AppBarLayout
lateinit var searchView: androidx.appcompat.widget.SearchView
override fun onAttach(context: Context) {
super.onAttach(context)
this.rootActivity = context as MainActivity
appBarLayout = rootActivity.findViewById(R.id.app_bar_layout)
searchView = rootActivity.findViewById(R.id.search_input)
}
override fun onResume() {
super.onResume()
resetAppBarLayout()
}
private fun resetAppBarLayout() {
appBarLayout.elevation = 14f
}
fun setupSearch(query: String) {
searchView.visibility = View.VISIBLE
searchView.clearFocus()
when(query.isNotEmpty()) {
true -> {
searchView.setQuery(query, true)
searchView.isIconified = false
}
false -> {
searchView.isIconified = true
searchView.isIconified = true
}
}
}
fun hideSearchKeyboard() {
context?.let {
KeyboardHelper.hideSearchKeyboard(it, searchView.findViewById(R.id.search_src_text))
}
}
fun hideSearch() {
searchView.visibility = View.GONE
searchView.clearFocus()
}
}
Secondly. Inherit your fragments from BaseFragment, override onResume() method and control searchView in your fragments by calling methods from BaseFragment. Something like this.
class FragmentA : BaseFragment() {
private var searchQuery = ""
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment, container, false)
val textView: TextView = root.findViewById(R.id.textView)
textView.text = "Fragment A"
return root
}
override fun onResume() {
super.onResume()
setupSearch()
}
private fun setupSearch() {
searchView.setOnQueryTextListener(object : androidx.appcompat.widget.SearchView.OnQueryTextListener {
override fun onQueryTextChange(newText: String?): Boolean {
when(newText.isNullOrEmpty()) {
true -> searchQuery = ""
false -> searchQuery = newText
}
return true
}
override fun onQueryTextSubmit(query: String?): Boolean {
hideSearchKeyboard()
return true
}
})
super.setupSearch(searchQuery)
}
}
Full example you can find here https://github.com/yellow-cap/android-manage-searchview