My application has one Activity and several fragments. Navigation Component was used for navigation and it is not very complicated. Also BottomNavigationComponent was used for bottom navigation.
There are three top level fragments which can be accessed via bottom navigation and there is an options menu at one of the top level fragments. Also this is not a complicated menu,too; there is just one item.
So, this is not a big deal and created menu like below.
notifications_menu
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/dismiss_all"
android:title="#string/dismiss_all"
android:orderInCategory="10"
app:showAsAction="ifRoom" />
</menu>
NotificationsFragment.kt
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewModel =
ViewModelProvider.NewInstanceFactory().create(NotificationsViewModel::class.java)
setHasOptionsMenu(true)
(activity as MainActivity).supportActionBar?.setDisplayHomeAsUpEnabled(false)
(activity as MainActivity).supportActionBar?.setDisplayShowHomeEnabled(false)
//other stuff
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.notifications_menu, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == R.id.dismiss_all) {
// there is no code here yet
return true
}
return super.onOptionsItemSelected(item);
}
And here is the result...
The problem is, as you can see above, when I tap Dismiss All button it navigates to the initial fragment. But I couldn't understand why?
After digging two hours I found that MainActivity's onOptionsItemSelected is causing this situation. Because as you can see it is handling all menu items and call onBackPressed event.
override fun onOptionsItemSelected(menuItem: MenuItem?): Boolean {
if (menuItem != null) {
onBackPressedDispatcher.onBackPressed()
}
return super.onOptionsItemSelected(menuItem)
}
Handling correctly this method solved my problem.
Here is the correct version of the code above.
override fun onOptionsItemSelected(menuItem: MenuItem?): Boolean {
val id = menuItem?.itemId
if (id == android.R.id.home) {
onBackPressedDispatcher.onBackPressed()
return true
}
return super.onOptionsItemSelected(menuItem)
}
Related
I'm currently trying to enable one menu item I have on my Options Menu and disabling another and vice versa when it's pressed. I don't quite know how this is done and every other attempt I've tried hasn't been successful.
Here's how my app is currently set up. I'm using a single activity and multiple fragments. My MainFragment has the following within it to set up the Options menu:
class MainFragment: Fragment() {
...
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMainBinding.inflate(inflater, container, false)
...
setHasOptionsMenu(true)
(activity as AppCompatActivity).setSupportActionBar(binding.mfToolbar)
...
return binding.root
}
...
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
super.onOptionsItemSelected(item)
when(item.itemId) {
R.id.recycler_view_toggle_list -> {
// TODO: Implement what happens here with the RecyclerView
// Also disable this menu item and enable grid view item
}
R.id,recyclver_view_toggle_grid -> {
// TODO: Implement what happens here with the RecyclerView
// Also disable this menu item and enable list view item
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
menu_item.xml:
<menu 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"
tools:context="com.jre.projectcounter.ui.main.MainActivity">
<item
android:id="#+id/recycler_view_toggle"
android:icon="#drawable/ic_view_list"
android:title="#string/list_view"
android:visible="true"
app:showAsAction="ifRoom"/>
<item
android:id="#+id/recycler_view_toggle_grid"
android:icon="#drawable/ic_view_grid"
android:title="#string/grid_view"
android:visible="false"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_settings"
android:orderInCategory="100"
android:title="Settings"
app:showAsAction="never" />
</menu>
When I try to do the following within the when statement under R.id.recycler_view_toggle_list:
binding.mfToolbar.menu[R.id.recycler_view_toggle_list].isVisible = false
binding.mfToolbar.menu[R.id.recycler_view_toggle_grid].isVisible = true
I get the following exception:
java.lang.IndexOutOfBoundsException
What am I doing wrong or missing to get the options to switch between each other?
You need to use findItem if you're using the id of item. Because when you do menu[0] it expects index & not id.
So in your case it should be something like this:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
super.onOptionsItemSelected(item)
when(item.itemId) {
R.id.recycler_view_toggle -> {
binding.mfToolbar.menu.findItem(R.id.recycler_view_toggle).isVisible = false
binding.mfToolbar.menu.findItem(R.id.recycler_view_toggle_grid).isVisible = true
}
}
return true
}
I describe my problem (i'm new in kotlin)...
I want to put a button search on the toolbar in kotlin because it's nice compare to a search view.
But when i try to do this the button search it's available for all view (Home, upload, settings by navigation bar)
It's possible to create a condition "just for home view show search button in toolbar"?
In your home view fragment, add your search item to the menu, so when that fragment is displayed its search option is present in the menn
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
// put your search menu item in this menu file
inflater.inflate(R.menu.home_view, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_search -> doSearchThing()
...
else -> return super.onOptionsItemSelected(item)
}
return true
}
and you need to tell the system that this fragment wants to mess with the menu:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
...
}
If your "home view" is an Activity instead of a Fragment, so you only want to show the search button if certain fragments aren't being displayed, you'll have to put some logic in onCreateOptionsMenu to decide whether to removeItem the search button from the menu, and call invalidateOptionsMenu() whenever the fragments change and you need to work out whether to show the button again
I'm assuming that you have a toolbar at the activity level, and then several fragments which represent the different screens handled by your navigation bar.
If I understood your problem correctly, you want, just when the home section is selected, that the toolbar shows the search button on the toolbar, and maybe other buttons or none for the other sections.
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navView.setOnNavigationItemSelectedListener {
invalidateOptionsMenu()
true
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
return when(navView.selectedItemId) {
R.id.navigation_home -> {
menuInflater.inflate(R.menu.menu_home, menu)
true
}
R.id.navigation_dashboard -> {
menuInflater.inflate(R.menu.menu_dashboard, menu)
true
}
R.id.navigation_notifications -> {
menuInflater.inflate(R.menu.menu_notifications, menu)
true
}
else -> {
super.onCreateOptionsMenu(menu)
}
}
}
}
menu_search.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/item_search"
app:showAsAction="always"
android:title="Do search" />
</menu>
menu_dashboard.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"/>
menu_notificationx.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"/>
NOTE: I'm not using the navigation library here, not sure if it's your case
Another solution is set the toolbar menu, just for the home section
I know this already asked a few times, but I still don't get anything after all (I'm quite new in android development).
So i set up my back button in the MainActivity.kt like this:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
}
// Set up the back button on action bar
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
}
What I want is that this back button is disabled in some fragments, so I tried to override the onBackPressed() function (It is what most people on the internet told) in one of the fragments:
class DashboardFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Declare that this fragment has menu
setHasOptionsMenu(true)
// Set action bar title to "Main Dashboard"
(activity as AppCompatActivity).supportActionBar?.title = "Main Dashboard"
// Binding object for this fragment and the layout
val binding: FragmentDashboardBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_dashboard, container, false)
//Some codes here//
return binding.root
}
// This is where the error occured
override fun onBackPressed() {
super.onBackPressed()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater?.inflate(R.menu.nav_overflow_menu, menu)
}
}
But it returns an error saying:
"OnBackPressed" overrides Nothing
Am I missing something? I'm already searching for the solutions but still confused over this.
Who knew... onSupportNavigateUp() works only on 4.0 and above. For below onNavigateUp() is called.
so
override fun onNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
What you could do is set your nonbackbutton fragments to backstack when inserting them, which is done by
val transaction = supportFragmentManager.beginTransaction()
transaction.addToBackStack(null)
transaction.replace(R.id.frame, fragment) //or add, whatever you use
transaction.commit()
(if you want back button functionality, just skip the addtoBackStack line)
Then, also in your activity, you override the onBackPressed() and check whether the backstack is empty or full. If it is empty, which is checked by
if(supportFragmentManager.backStackEntryCount() == 0)
then you are, for example, on a fragment that supports back button, and just do super.onBackPressed().
On the other hand, if it has something, you can do the navController.navigateUp() part, and when done, pop it using supportFragmentManager.popBackStackImmediate().
I think you could make something like this work, try it, and let us know :)
I have a fragment that is called from the activity_main screen when a main menu drop down option is clicked. I am trying to have the fragment close and show the activity_main screen again when the cancel button is clicked. I have user input in the activity_main screen that I would like to still be there if the fragment is closed while clicking cancel but at this point i will settle for the fragment just closing and the activity_main showing back at it's original state.
I am using androidx and all of the answers I have found so far have been compatible with android.support.v7
This is the code that i have in my fragment, it does not do anything at all when I click the 'Cancel' button, the fragment just continues to sit there over the activity_main.
class SaveFragment: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.savefragment, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
b_cancel.setOnClickListener {
fragmentManager?.popBackStack()
}
}
}
Here is the code that calls the savefragment from the MainActivity:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.i_save -> {
val SaveFragment = SaveFragment()
supportFragmentManager.beginTransaction().replace(R.id.activity_main_main, SaveFragment).commit()
true
}
R.id.i_recall -> {
val RecallFragment = RecallFragment()
supportFragmentManager.beginTransaction().replace(R.id.activity_main_main, RecallFragment).commit()
true
}
else -> super.onOptionsItemSelected(item)
}
}
Any help at all in Kotlin would be appreciated.
I am getting an issue of hiding and adding the menu item in fragment and navigation drawer as I in my application I have logout and adding contact menu and I want to set logout option in all but don't need an adding contact button in all screen. As my application contains four view pager tab fragment and navigation drawer. I have added my menu through menu XML file.
And I want my logout option in all fragment in navigation drawer but adding contact in only on my first tab fragment.
Here is code of Main Activity:
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val menuInflater = menuInflater.inflate(R.menu.main, menu)
val menuLogout = menu.findItem(R.id.logout)
val menuadd = menu.findItem(R.id.iadd_contact)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: android.view.MenuItem): Boolean {
when (item.itemId) {
R.id.logout -> {
postLogout(path, params) { response ->
}
}
return true
R.id.iadd_contact -> {
val intent = Intent(Intent.ACTION_INSERT)
intent.setType(ContactsContract.Contacts.CONTENT_TYPE)
startActivity(intent)
return true
}
}
return super.onOptionsItemSelected(item)
}
What is going with that when I move from fragment A to Fragment B i want only logout button enable and add contact disable and then when I again go revert back from Fragment B to Fragment A my add contact menu also get disable but that I want to be in Fragment A only and same as it happens when I open my navigation drawer
And in fragment I have set my code like this:
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
super.onCreateOptionsMenu(menu, inflater)
menu!!.removeItem(R.id.iadd_contact)
}
and it oncreateview:
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// TODO Auto-generated method stub
setHasOptionsMenu(false)
rootv = inflater!!.inflate(R.layout.edit_profile, container, false)
activity.invalidateOptionsMenu()
return rootv
}
Simply I just want my R.id.iadd_contact in my first tab fragment to enable and in all other fragment disable so due to fragment back stack or moving again from another fragment to my first tab fragment my add R.id.iadd_contact get also removed.
first override onCreate Method
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
And then inflate menu in your fragment
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
super.onCreateOptionsMenu(menu, inflater)
menu!!.clear()
inflater!!.inflate(R.menu.main, menu)
}