I'm implementing an app in Xamarin Android that contains a page that once you click on an actionbar button, you get a new dialog that contains a toolbar.
The simplified code is something like:
public class MyDialogFragment : MvxDialogFragment<MyDialogViewModel>
{
public MvxDialogFragment()
{
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.EnsureBindingContextIsSet(inflater);
var view = this.BindingInflate(Resource.Layout.dialog_view, null);
SetupToolbar(view);
return view;
}
private void SetupToolbar(View view)
{
Dialog.RequestWindowFeature((int)WindowFeatures.NoTitle);
Android.Support.V7.Widget.Toolbar toolbar = view.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.dialog_toolbar);
((AppCompatActivity)Activity).SetSupportActionBar(toolbar);
Android.Support.V7.App.ActionBar actionBar = ((AppCompatActivity)Activity).SupportActionBar;
actionBar.Title = null;
HasOptionsMenu = true;
}
public override void OnCreateOptionsMenu(IMenu menu, MenuInflater inflater)
{
menu.Clear();
inflater.Inflate(Resource.Menu.menu_dialog, menu);
base.OnCreateOptionsMenu(menu, inflater);
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
switch (item.ItemId)
{
(...)
}
}
public override void OnPrepareOptionsMenu(IMenu menu)
{
(...)
base.OnPrepareOptionsMenu(menu);
}
The main workflow it works just fine, but there is a side effect when I rotate the screen while displaying the dialog.
If I do so, the actionbar buttons of the parent fragment(the one hosting the dialog), disappear till I recreate the view (ie: rotation).
Any ideas about how to solve this? I have tried several things like invalidate the parent menu after the dialog is closed, but it didn't work.
After some time investigating the problem, I don't think there is a way to make work 2 actionbars at the same time in a "native way".
My solution, in the end, was to manually handle the click events over the second actionbar.
public class MyDialogFragment : MvxDialogFragment<MyDialogViewModel>
{
public MvxDialogFragment()
{
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.EnsureBindingContextIsSet(inflater);
var view = this.BindingInflate(Resource.Layout.dialog_view, null);
SetupToolbar(view);
return view;
}
private void SetupToolbar(View view)
{
Dialog.RequestWindowFeature((int)WindowFeatures.NoTitle);
toolbar = view.FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
toolbar.Title = null;
toolbar.InflateMenu(Resource.Menu.menu);
toolbar.MenuItemClick += HandlerMenuItemClick;
HasOptionsMenu = true;
}
public override bool HandlerMenuItemClick(object sender, Android.Support.V7.Widget.Toolbar.MenuItemClickEventArgs e)
{
switch (e.item.ItemId)
{
(...)
}
}
}
Hope it can helps anybody
Related
After OnCreateOptionsMenu() marked as deprecated, I've managed to use new API from release notes https://developer.android.com/jetpack/androidx/releases/activity#1.4.0-alpha01
In my app user can switch Fragments via bottomNavigation.
As I understand docs, in each Fragment I've implemented MenuProvider(with or without Lifecycle, doesn't matter for result). But now in each Fragment user have all items from all menuInflaters.
There is the code of implementation
FRAGMENT A
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
SetMainParams();
fragment = inflater.inflate( R.layout.fragment_A, container, false );
addMenu();
return fragment;
}
private void addMenu()
{
MenuProvider menuProvider = new MenuProvider()
{
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater)
{
menuInflater.inflate(R.menu.menu_fragment_A, menu);
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem)
{
if( menuItem.getItemId() == R.id.filters_prev )
filtersPrevious();
else if( menuItem.getItemId() == R.id.filters )
showFilters();
else
filtersNext();
return false;
}
};
requireActivity().addMenuProvider(menuProvider, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
}
FRAGMENT B
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
SetMainParams();
binding = FragmentBBinding.inflate(inflater, container, false);
fragment = binding.getRoot();
init();
addMenu();
return fragment;
}
private void addMenu()
{
MenuProvider menuProvider = new MenuProvider()
{
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater)
{
menuInflater.inflate(R.menu.menu_fragment_B, menu);
filtersMenu = menu.getItem(0);
}
#Override
public boolean onMenuItemSelected(#NonNull MenuItem menuItem)
{
if( menuItem.getItemId() == R.id.filters )
loadFilters();
return false;
}
};
requireActivity().addMenuProvider(menuProvider, getViewLifecycleOwner(), Lifecycle.State.RESUMED);
}
Switching from bottomNavigation
binding.bottomNav.setOnItemSelectedListener(item ->
{
int itemId = item.getItemId();
if( itemId == R.id.A )
{
fm.beginTransaction().hide(active_fragment).show(A_fragment).commit();
active_fragment = A_fragment;
setWithElevation(false);
}
else if( itemId == R.id.B )
{
fm.beginTransaction().hide(active_fragment).show(B_fragment).commit();
active_fragment = B_fragment;
setWithElevation(true);
}
active_fragment.startFragment();
active_fragment.setTitle();
return true;
});
fm.beginTransaction().add( R.id.fl_content, A_fragment, "A_fragment" ).hide(A_fragment).commit();
fm.beginTransaction().add( R.id.fl_content, B_fragment, "B_fragment" ).hide(B_fragment).commit();
Is there any ideas, why new API works like this, or maybe i've made a mistake. Thanks a lot for help :)
I also experienced this problem today. So not sure if you have intellisense, but look at some of the Menu interface methods. I used the hasVisibleItems() method in Kotlin to only inflate the menu if it does not exist. Java also has the method; your snippet augmented with conditional:
MenuProvider menuProvider = new MenuProvider()
{
#Override
public void onCreateMenu(#NonNull Menu menu, #NonNull MenuInflater menuInflater)
{
if (!menu.hasVisibleItem()) { // only inflate if no items
menuInflater.inflate(R.menu.menu_fragment_A, menu);
}
}
//...
Menu also has a bunch of other methods that may assist for specific items.
Also another good idea would be to use the MenuHost interface to keep track of your MenuProvider. I believe in Java; inside your Fragments; it would be along the lines of:
activityFragment = this.requireActivity();
Menuhost menuHost = (MenuHost) activityFragment;
menuHost.addMenuProvider(
menuProvider,
this.getViewLifecycleOwner(),
State.RESUMED
);
Menu providers can then be invalidated ect.
I've a similar problem. In my application, I'm using fragments and navigation. The problem I found is that during navigation, the fragment never call the onPause event. So all my fragment implements this code in onViewCreated method:
class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
val menuHost: MenuHost = requireActivity()
menuHost.addMenuProvider(HomeMenuProvider(R.id.nav_main, findNavController(), this, authViewModel, homeViewModel), viewLifecycleOwner, Lifecycle.State.RESUMED)
}
The HomeMenuProvider is so defined:
class HomeMenuProvider(private val currentNavId: Int) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
if ((navController.currentDestination?.id ?: -1L) == currentNavId) {
menu.clear()
menuInflater.inflate(R.menu.menu_main, menu)
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.menu_action_login -> {
...
true
}
else -> {
false
}
}
}
}
As you can see in my code, I add menu items only if the current navigation status is the one associated with the fragment. Otherwise, simply I do nothing.
To avoid menu persistence, before add items I clear the menu.
I hope this helps.
My Fragment class:
Toolbar toolbar;
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
toolbar = getView().findViewById(R.id.toolbar3);
toolbar.inflateMenu(R.menu.menufragmentmain);
setHasOptionsMenu(true); //i also tried putting this function in oncreate function
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
Toast.makeText(getActivity(), "i never enter this function also" , Toast.LENGTH_LONG).show();
super.onCreateOptionsMenu(menu,inflater);
inflater.inflate(R.menu.menufragmentmain, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(getActivity(), "i never enter this function" , Toast.LENGTH_LONG).show();
//some switch cases.....
return super.onOptionsItemSelected(item);
}
I've got a stupid error which I cant find unfourtantly. I've got a simple toolbar for my fragment. In onViewCreated I inflate my actionbar menu with my toolbar.
The issue is that the functions onCreateOptionsMenu and 'onOptionsItemSelected' are never called. I've got no clue why.
Things i checked in other similar questions with the same issue:
I checked if my main activity has a onCreateOptionsMenu or'onOptionsItemSelected'. It doesn*t
Checked if my style class has NOT: android:theme="#android:style/Theme.Black.NoTitleBar
None of the points unfourtantly work. What did I miss. Do I need to check something else?
As per the Fragment-owned app bar guide which explains how to use a Fragment-owned Toolbar, you do not use any of the setHasOptionsMenu(), onCreateOptionsMenu(), or onOptionsItemSelected() APIs - those are only used for activity owned app bars.
Instead, you would follow the guide for handling menu click events by using the Toolbar APIs:
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
toolbar = getView().findViewById(R.id.toolbar3);
toolbar.inflateMenu(R.menu.menufragmentmain);
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(getActivity(), "onMenuItemClick called" , Toast.LENGTH_LONG).show();
//some switch cases.....
return true;
}
});
}
Set up the toolbar in the host Activity. Then override the menu handling in each Fragment wherever necessary.
Here's some generic scaffolding (with comments):
public class MyFragment extends Fragment {
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Report that this Fragment has an options menu
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(#NonNull Menu menu, #NonNull MenuInflater inflater) {
// Inflate then menu, and THEN call super()
// Note the order of the invocations
inflater.inflate(R.menu.my_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
// Handle menu item selections
final int itemId = item.getItemId();
switch(itemId) {
case R.id.my_menu_option_x: ...; return true;
case R.id.my_menu_option_y: ...; return true;
case R.id.my_menu_option_z: ...; return true;
default: return super.onOptionsItemSelected(item);
}
}
}
I have a activity and fragment ,i want to set a menu to mytoolbar in pre run time the icon has set but in run application it does not show anything.
in my activity:
public class MainActivity extends AppCompatActivity {
private TabLayout mTabLayout;
private TextView mTextViewTabOne;
private TextView mTextViewTabTwo;
private TextView mTextViewTabThree;
private android.support.v7.widget.Toolbar mToolbar;
private TextView mTextViewToolbarTitle;
private AlertDialog mAlertDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setLocale("fa");
getSupportFragmentManager().beginTransaction().replace(R.id.framelayout_mainactivity_fragmentcontainer, new AuthenticationPasswordFragment()).commit();
setToolBar(getString(R.string.addbank_toolbartitle));
}
public void setToolBar(String title) {
mToolbar = findViewById(R.id.toolbar_everywhere_toolbar);
mTextViewToolbarTitle = findViewById(R.id.toolbar_title);
mTextViewToolbarTitle.setText(title);
}
and in my fragment :
public class AuthenticationPasswordFragment extends BaseFragment implements BaseAuthenticationContract.View {
private TextInputEditText mEditTextPassword;
private TextInputLayout mTextInputEditTextPassword;
private View mRoot;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
mRoot=inflater.inflate(R.layout.fragment_authenticationpassword,null);
return mRoot;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.authenticationpassword_menutoolbar,menu);
}
in my menu is:
<item
android:id="#+id/item_authenticationpassword_confirm"
android:title="confirm"
android:icon="#drawable/everywhere_confirm"
app:showAsAction="always"
/>
Replace this setToolbar in activity :
public void setToolBar(String title,int resourceMenu) {
mToolbar = findViewById(R.id.toolbar_everywhere_toolbar);
mTextViewToolbarTitle = findViewById(R.id.toolbar_title);
mTextViewToolbarTitle.setText(title);
mToolbar.inflateMenu(resourceMenu);
}
With this :
public void setToolBar(String title) {
mToolbar = findViewById(R.id.toolbar_everywhere_toolbar);
mTextViewToolbarTitle = findViewById(R.id.toolbar_title);
mTextViewToolbarTitle.setText(title);
}
And add this line to your onCreateView fragment or any fragment that you want add menu toolbar to it :
((MainActivity)getActivity()).setToolBar(getString(R.string.authenticationpassword_titletoolbar),R.menu.authenticationpassword_menutoolbar);
In the activity's onCreate method, set the toolbar with
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Then add this method.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.yourmenufile, menu);
return super.onCreateOptionsMenu(menu);
}
Then add menu click listener,
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 0:
// do whatever
return true;
default:
return super.onOptionsItemSelected(item);
}
}
If you want to change the menu items and listeners while in different fragments, use interfaces to let the activity know which fragment is active and change accordingly.
If you want the method onCreateOptionsMenu inside a Fragment, add the codeline toolbar.setHasOptionsMenu(true) in onCreate of the activity where the Toolbar is set up.
The second option is to move the onCreateOptionsMenu into the Activity.
See this related StackOverflow question.
Report back if this works, I hope it helps you.
I'm learning how to create an app and have figured out how to build a login activity and a menu for logging out that returns you to the login layout. However, if I navigate through my different layouts/activities, the menu item for logging out duplicates. I believe it's due to having the menu created on each layout, but I'm not sure how to change it so that it doesn't duplicate.
Here's my fragment.
public class UserFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
savedInstanceState) {
return inflater.inflate(R.layout.activity_user_fragment, container, false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_logout, menu);
}
}
My LoginActivity.class
public class LoginActivity extends AppCompatActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
Button b1login = (Button) findViewById(R.id.btlogin);
Button b2login_cancel = (Button) findViewById(R.id.btcancel_login);
assert b1login != null;
b1login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText ed1 = (EditText) findViewById(R.id.etuser_name);
EditText ed2 = (EditText) findViewById(R.id.etpassword);
if (ed1.getText().toString().equals(getText(R.string.user_id)) &&
ed2.getText().toString().equals(getText(R.string.user_password))) {
Toast.makeText(getApplicationContext(), R.string.successful_login,
Toast.LENGTH_SHORT).show();
setContentView(R.layout.activity_clients);
} else {
Toast.makeText(getApplicationContext(), R.string.unsuccessful_login,
Toast.LENGTH_SHORT).show();
}
}
});
assert b2login_cancel != null;
b2login_cancel.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.exit(0);
}
});
}
//Menu option logout return to login screen.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_item_logout) {
Intent i = new Intent(this, LoginActivity.class);
this.startActivity(i);
return true;
}
return super.onOptionsItemSelected(item);
}
public void addClient(View view) {
setContentView(R.layout.activity_new_client);
}
public void submitClient(View view) {
setContentView(R.layout.activity_sessions);
}
public void cancelClient(View view) {
setContentView(R.layout.activity_clients);
}
public void newSession(View view) {
setContentView(R.layout.activity_new_session);
}
public void cancelSessionCompletion(View view) {
setContentView(R.layout.activity_sessions);
}
public void cancelSession(View view) {
setContentView(R.layout.activity_sessions);
}
}
Fragment layout.
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/menu_item_logout"
android:icon="#drawable/ic_logout"
android:title="#string/logout"
app:showAsAction="ifRoom|withText" />
</menu
EDIT:
I deleted the fragment where I initially created the menu as well as the menu code from the UserFragment and hard coded the menu itself into the LoginActivity class, which fixed the duplication issue.
Code adding menu.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
However, the menu now shows on the login screen. How do I prevent it from showing there?
For having different menu items in different activities you need to create different menu xml layouts.
So one file named menu.xml is already there in res folder. You need to create new xml file in same folder.
Then in java code of that activity :- in override Oncreateoptionmenu inflate the menu you want.
Here is what you need to do:
In your LoginActivity class, you want to NOT inflate the menu because you don't want users to do see the logout option.
In your Activity that loads after Login, you actually want to inflate the Logout menu - well, because you want them to see and use that option.
In your code, I see that your fragment uses the method :
setHasOptionsMenu(true);
What you can do here is use an if statement to see if a user has logged in before and if not, set that to true otherwise, do nothing (which is like setting to false).
You can reuse the menu in other activities if you want to enable users to Logout from those activities or just leave them it out!
I hope this helps!
EDIT
According to your sample code above, your first part shows what you would do inside a Fragment class - you setHasOptionsMenu(true). Now, it seems that you have two logout menus, in the activity and in the fragment - which I do not see where you have used your Fragment in your LoginActivity - unless it is somewhere else in a different activity!
Similar to my answer here, https://stackoverflow.com/a/63008005/5916188, a work-around would be to remove the menu each time, if it exists, before adding it.
Note that the existing menu is removed item by item (R.id...) while the inflated one is added by menu (R.menu...) as usual.
This way, you can keep the menu in the fragment. Like this:
#Override public void onCreateOptionsMenu (#NonNull Menu menu, #NonNull MenuInflater
inflater)
{
super.onCreateOptionsMenu (menu, inflater);
removeMenuItemIfPresent (menu, R.id.menu_search_options);
removeMenuItemIfPresent (menu, R.id.menu_sample_filter1);
removeMenuItemIfPresent (menu, R.id.menu_sample_filter2);
inflater.inflate (R.menu.menu_search_menu, menu);
}
private void removeMenuItemIfPresent (#NonNull Menu menu, int resourceIDToRemove)
{
MenuItem menuItemToRemove = menu.findItem (resourceIDToRemove);
if (menuItemToRemove != null) {
menu.removeItem (menuItemToRemove.getItemId ());
}
}
I am trying to create an Action Bar with one option in a fragment but it doesn't work.
And I have followed all the steps which are indicated in the internet tutorials:
Create the function "onCreate", it doesn't appear by default, with the "sethasoptionmenu(true)".
Write the function "onCreateOptionsMenu" with the "inflater.inflate(R.menu.menu_fotos, menu);"
Create the function "onOptionsItemSelected"
With this, what I get is a normal options menu. But what I need is an Action Bar menu!
Can you help me?
I copy here the code of the fragment:
package com.carlesqf.laguerra;
import *.*;
public class FragmentContingutCapitols extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v= inflater.inflate(R.layout.activity_contingutcapitols, null);
// Getting the bundle object passed from "PantallaContingutCapitols"
Bundle b = getArguments();
String nomcap=LlistaCapitols.name[b.getInt("position")];
if (nomcap.contains("1700-1701 Les causes del conflicte:"))
v = inflater.inflate(R.layout.capitol1700, null);
else if ((nomcap.contains("1702 – Primers combats. Itàlia i front del Rin:")))
v = inflater.inflate(R.layout.capitol1702, null);
...
return v;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Do something that differs the Activity's menu here
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_fotos, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_settings:
// do s.th.
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Actually the actionbar menu is a normal options menu. Is NavigationDrawer what you're looking for?