I have two support fragments sitting inside an AppCompatActivity. By design they each have unique Toolbar and Option Menus. The AppCompatActivity does not have a toolbar in it's layout as these are included in each fragment.
In each fragment, I setHasOptionsMenu(true); in onCreate().
In onCreateView() I am calling ((AppCompatActivity)getActivity()).setSupportActionBar(toolbar); where toolbar is the object bound to the xml Toolbar element.
In onCreateOptionsMenu(), I first make the call to super, then I call menu.clear() and finally inflate the menu with inflater.inflate(R.menu.searchbar_menu, menu);
Where this fails is in calling ((AppCompatActivity)getActivity()).setSupportActionBar(toolbar); in both fragments. In doing so, the options will only appear on the second fragment and not on the first. If I call it in only one fragment, the options appear as expected in only that one fragment. Of course, if I call it in neither, they do not appear at all.
Here is my code. Both Fragments are essentially identical with the exception of the menus inflated:
public class FeedFragment extends Fragment implements FeedView{
#BindView(R.id.toolbar)
Toolbar toolbar;
FeedPresenter presenter;
static final String TAG = "FEED_FRAGMENT";
/*
* Some boilerplate fragment setup code
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_feed, container, false);
ButterKnife.bind(this, view);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
setupToolar();
return view;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
menu.clear();
inflater.inflate(R.menu.searchbar_menu, menu);
}
private void setupToolbar(){
toolbar.setPadding(0,ScreenUtil.getStatusBarHeight(this.getActivity()),0,0);
toolbar.setTitle("");
}
/*
* Other logic code unique to each fragment
*/
Does anyone know has I can set both fragments to have their toolbar options?
Thanks in advance.
When you call getActivity(), it returns the same instance of your Activity. The second fragment was initialized later than the first fragment, that why the Activity can only support action bar on the second fragment.
You could try this
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
AppCompatActivity baseActivity = (AppCompatActivity) getContext();
baseActivity.setSupportActionBar(yourToolbar);
}
}
What worked for me was setting suppoerActionBar according to which fragment is currently visible. You can use the following code:
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if(!hidden){
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
}
}
Related
I have a rare problem. I have an activity that has tabs populated dynamically (up to 20 tabs). Each fragment has an implementation of OnCreateOptionsMenu and a menu which contains a bookmark icon that changes color when pressed. There is a class variable menu of type Menu which is set when fragment's OnCreateOptionsMenu is called. This is to enable me set the bookmark menu item outside the onCreateOptionsMenu(This is also done dynamically).
Problem:
When the parent activity have only a single tab(One fragment instance), this implementation works fine. However with more than one tab, the app crashes with the error:
Attempt to invoke interface method android.view.MenuItem android.view.Menu.findItem(int)
What I think: I think the problem is that the OnCreateOptionsMenu is called once i.e Class variable: menu is set only once for the first tab (fragment) hence when other tabs are added, menu is null
Some codes:
Fragment
Menu menu;
...
...
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setMenuBookmark(ques_obj);
}
private void setMenuBookmark(QuestionAnswerList ques_obj){
MenuItem menuBookmark = this.menu.findItem(R.id.action_bookmark);
if (ques_obj.getIs_bookmarked().equals("1")) {
menuBookmark.setIcon(R.drawable.ic_bookmark_white_24dp);
} else {
menuBookmark.setIcon(R.drawable.ic_bookmark_border_white_24dp);
}
}
...
...
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.cbt_fragment_menu, menu);
this.menu = menu;
super.onCreateOptionsMenu(menu, inflater);
}
P.S setHasOptionsMenu() is set to true in Fragment's OnCreate
Try this way
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
getActivity().invalidateOptionsMenu()
}
}, 500 );
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.cbt_fragment_menu, menu);
MenuItem menuBookmark = menu.findItem(R.id.action_bookmark)
if (ques_obj.getIs_bookmarked().equals("1")) {
menuBookmark.setIcon(R.drawable.ic_bookmark_white_24dp);
} else {
menuBookmark.setIcon(R.drawable.ic_bookmark_border_white_24dp);
}
return true;
}
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 have a fragment that contains menu I have tried calling setHasOptionsMenu(true); in both onCreate() as well as onCreateView() and set a toolbar as my Actionbar. I have gone through all the questions about the same here but none of the solutions seem to be working for me! I have tried changing the theme, menu.clear(), and returning true from onCreateOptions() of the Activity
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setHasOptionsMenu(true);
// Inflating view layout
layout = inflater.inflate(R.layout.side_panel, container,false);
toolbar = layout.findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
return layout;
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_side_panel,menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
If you have declared Toolbar in the XML file, Then try this
First get a reference to Toolbar using findViewByid() then inflate your menu
mToolbar.inflateMenu(R.menu.main_menu);
I'm working to implement a SearchView on my Fragment's RecyclerView as shown here. When the user taps the search button I want the Menu Item for the SearchView to Override the toolbar and display the area for them to search. If I don't use my custom style for the toolbar it works fine but when I do it get the images below.
Here is what I am currently getting:
This is the class that the EventListActivity inherits from:
public abstract class SingleFragmentActivity extends AppCompatActivity {
protected abstract Fragment createFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.eventcalendar_activity_fragment);
// Manages our fragments. We can call it to add a fragment to an activity in code
FragmentManager fm = getSupportFragmentManager();
// Check if fragment of R.id already exits
// The FragmentManager saves out the list of fragments on rotation destruction or memory reclaim
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
// If the the fragment does not exist, create it
if(fragment == null) {
fragment = createFragment();
// Create a new fragment transaction, include one add operation in it, and then commit it
fm.beginTransaction().add(R.id.fragment_container, fragment).commit();
}
}
}
This creates the fragment that manages and works with the RecyclerView:
public class EventListActivity extends SingleFragmentActivity {
#Override
protected Fragment createFragment() {
// Setting arguments for the new fragment created from the intent from EventFeedResultWrapper
EventListFragment fragment = new EventListFragment();
fragment.setArguments(getIntent().getExtras());
return fragment;
}
}
Fragment that holds the RecyclerViewthat the SearchView will interface with. This is where the search menu button is inflated. It is where I've been trying to modify the toolbar:
public class EventListFragment extends Fragment {
private RecyclerView mEventRecyclerView;
private EventAdapter mAdapter;
// Telling the FragmentManager that it is
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
((AppCompatActivity)getActivity()).getSupportActionBar().setCustomView(R.layout.action_bar_center);
View sabView = ((AppCompatActivity)getActivity()).getSupportActionBar().getCustomView();
TextView titleTxtView = (TextView) sabView.findViewById(R.id.action_bar_title);
titleTxtView.setText("Events Calendar");
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayShowCustomEnabled(true);
setHasOptionsMenu(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.eventcalendar_fragment_event_list, container, false);
mEventRecyclerView = (RecyclerView) view.findViewById(R.id.event_recycler_view);
// RecyclerView requires a layout manager to work, layout manager is in charge of position items on screen
mEventRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateUI();
return view;
}
// Populate the menu instance
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.eventcalendar_menu, menu);
}
// When we edit a EventActivity this saves it back to the EventListActivity
// onResume over onStart because we cannot assume the activity will be stopped
// when another activity is in front of it. If the other activity is transparent
// then the activity might just get paused. If it is paused then onStart() will not be called
// but on resume will be called.
// NOTE: In general onResume() is the safest place to take action to update a fragment's view
#Override
public void onResume() {
super.onResume();
updateUI();
}
private void updateUI() {
// Read in the events saved in to EventFeedResultWrapper by the Async task in ParseEventFeedTask
EventFeedResultWrapper wrapper = (EventFeedResultWrapper) getArguments().getSerializable(ParseEventFeedTask.EXTRA_RESULTS_LIST);
// Make sure wrapper is not null, it will NEVER be null
if(wrapper == null){
throw new RuntimeException("Error: The wrapper is null!");
}
// Get all the events from the wrapper/serializable
List<Event> events = wrapper.getEventFeedResults();
// Gets the context that we don't use rofl
EventCal eventCal = EventCal.get(getActivity());
// Add all the events we got from the wrapper to our event manager eventCal
eventCal.addEvents(events);
// Check to see if the EventAdapter is already setup
if(mAdapter == null) {
mAdapter = new EventAdapter(events);
mEventRecyclerView.setAdapter(mAdapter);
} else {
mAdapter.notifyDataSetChanged();
}
}
//Adapter here - removed code since it doesn't do anything with the toolbar
//RecycleView onClickLister - removed code since it doesn't do anything with the toolbar
}
This is the custom XML Style
<?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="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/action_bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#ffffff"
android:textSize="24dp"/>
</LinearLayout>
Event Menu 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:title="Search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="always"/>
</menu>
Did you missed android:orientation="vertical". The text is displaying vertically. You want horizontal right?
Oh nvm i didn't understood it at first. The problem must be the inflating of the custom view, for some reason it seems it keeps the original view there and inflated it somewhere else. Give me 1s i'll update the answer when i find the issue
Check here: getSupportActionBar().setCustomView(view) does not fill entire actionbar
Similar problem. First and Second answer
EDIT2: Remove the code from EventListFragment onCreate. You don't need to inflate a searchView, you can use the default one.
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_main, menu);
super.onCreateOptionsMenu(menu, inflater);
setOptionsMenu(menu);
}
public void setOptionsMenu(Menu menu) {
MenuItem search = menu.findItem(R.id.search);
SearchView searchView;
/**
* Setup the SearchView
*/
SearchManager searchManager = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
final boolean[] modifiedOriginal = {false};
searchView = (SearchView) search.getActionView();
if (searchView != null) {
searchView.setSearchableInfo(searchManager.getSearchableInfo(((Activity) context).getComponentName()));
MenuItemCompat.setOnActionExpandListener(search, new MenuItemCompat.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
...
return true;
}
#Override
public boolean onMenuItemActionCollapse(MenuItem item) {
...
return true;
}
});
final EditText et = ButterKnife.findById(searchView, android.support.v7.appcompat.R.id.search_src_text);
et.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String s = editable.toString();
if (!s.equals("") && !s.equals(" ")) {
...
}
}
});
ButterKnife.findById(searchView, android.support.v7.appcompat.R.id.search_close_btn).setOnClickListener(
view -> {
...
et.setText("");
}
);
}
}
You can leave your menu xml as it is.
I have placed setHasOptionsMenu(true) inside onCreateView, but I still can't call onCreateOptionsMenu inside fragments.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
setHasOptionsMenu(true);
return inflater.inflate(R.layout.facesheet, container, false);
}
Below is my onCreateOptionsMenu code.
#Override
public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
getSupportMenuInflater().inflate(R.menu.layout, menu);
return (super.onCreateOptionsMenu(menu));
}
The error message I get:
The method onCreateOptionsMenu(Menu) of type Fragment must override or implement a supertype method.
try this,
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_sample, menu);
super.onCreateOptionsMenu(menu,inflater);
}
Finally, in onCreateView method, add this line to make the options appear in your Toolbar
setHasOptionsMenu(true);
Your already have the autogenerated file res/menu/menu.xml defining action_settings.
In your MainActivity.java have the following methods:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.action_settings:
// do stuff, like showing settings fragment
return true;
}
return super.onOptionsItemSelected(item); // important line
}
In the onCreateView() method of your Fragment call:
setHasOptionsMenu(true);
and also add these 2 methods:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.fragment_menu, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.action_1:
// do stuff
return true;
case R.id.action_2:
// do more stuff
return true;
}
return false;
}
Finally, add the new file res/menu/fragment_menu.xml defining action_1 and action_2.
This way when your app displays the Fragment, its menu will contain 3 entries:
action_1 from res/menu/fragment_menu.xml
action_2 from res/menu/fragment_menu.xml
action_settings from res/menu/menu.xml
I tried the #Alexander Farber and #Sino Raj answers. Both answers are nice, but I couldn't use the onCreateOptionsMenu inside my fragment, until I discover what was missing:
Add setSupportActionBar(toolbar) in my Activity, like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.id.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
I hope this answer can be helpful for someone with the same problem.
Call
setSupportActionBar(toolbar)
inside
onViewCreated(...)
of Fragment
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar);
((MainActivity)getActivity()).setSupportActionBar(toolbar);
setHasOptionsMenu(true);
}
Set setHasMenuOptions(true) works if application has a theme with Actionbar such as Theme.MaterialComponents.DayNight.DarkActionBar or Activity has it's own Toolbar, otherwise onCreateOptionsMenu in fragment does not get called.
If you want to use standalone Toolbar you either need to get activity and set your Toolbar as support action bar with
(requireActivity() as? MainActivity)?.setSupportActionBar(toolbar)
which lets your fragment onCreateOptionsMenu to be called.
Other alternative is, you can inflate your Toolbar's own menu with toolbar.inflateMenu(R.menu.YOUR_MENU) and item listener with
toolbar.setOnMenuItemClickListener {
// do something
true
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_add_customer, container, false);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_sample, menu);
super.onCreateOptionsMenu(menu,inflater);
}
You can do easily as,
inside your fragment on Create method,
setHasOptionsMenu(true)
and now you can override,
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
menu.clear()
}