How do I realize options and context menus in a "clean" and proper way, when an Activity switches between several fragments?
Here example code using a NavigationDrawer:
public class MainActivity extends Activity implements OnFragmentInteractionListener {
...
#Override
public void onNavigationDrawerItemSelected(int position) {
FragmentManager fragmentManager = getFragmentManager();
switch (position) {
case 0:
fragmentManager.beginTransaction().replace(R.id.container, Fragment1.newInstance(null, null)).commit();
break;
case 1:
fragmentManager.beginTransaction().replace(R.id.container, Fragment2.newInstance(null, null)).commit();
break;
}
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// ???
return super.onPrepareOptionsMenu(menu);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
if (!mNavigationDrawerFragment.isDrawerOpen()) {
getMenuInflater().inflate(R.menu.main, menu);
restoreActionBar();
return true;
}
// ???
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
// ???
return super.onOptionsItemSelected(item);
}
#Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
//???
super.onCreateContextMenu(menu, v, menuInfo);
}
#Override
public boolean onContextItemSelected(MenuItem item) {
// ???
return super.onContextItemSelected(item);
}
#Override
public void onFragmentInteraction(String id) {
// ???
}
}
public class Fragment1 extends Fragment {
// ???
// option menu item A
// listView1 context menu item X
}
public class Fragment2 extends Fragment {
// ???
// option menu item B
// listView 2 context menu item Y
}
Maybe I am on the wrong track. I am a bit confused...
I understand you want to have different menus for each fragment? You can specify a menu for each fragment and it will only show when that fragment is attached to the Activity. The code below all belongs in a fragment.
You must tell the fragment it has an options menu:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
Then inflate the the menu for this fragment:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.about, menu);
}
Then handle each item in the menu when it is clicked:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle menu item click here
return super.onOptionsItemSelected(item);
}
You can also specify a "global" menu in the activity and those menu item will be shown as well as the menu items for the current fragment.
Related
I've the base activity where all toolbar initializations and options menu are done, the activities extending the base can not fire onitemclick
In the base i have
public class BaseActivity extends AppCompatActivity {
private MenuItem refresh;
public Toolbar getToolbar() {
return toolbar;
}
public MenuItem getRefresh() {
return refresh;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
refresh = menu.findItem(R.id.action_refresh);
refresh.setActionView(R.layout.menu_item_view);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
}
return false;
}
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
switch (mState) {
case Const.STATE_IDLE:
refresh.setVisible(true);
break;
case STATE_WORKING:
refresh.setVisible(false);
break;
default:
refresh.setVisible(true);
break;
}
return super.onPrepareOptionsMenu(menu);
}
}
In one of the activites I handle it like
public class CommentsActivity extends BaseToolbarActivity
{
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
if(item.getItemId() == R.id.action_refresh){
setState(Const.STATE_WORKING);
showMsg(contentRoot,"oops");
return true;
}
return super.onOptionsItemSelected(item);
}
}
but the click items won't fire
After a while I came to realize that since I was setting a custom layout for my items (useful for animations) an option menu with a custom view that is either set in the xml or dynamically using
item.setActionView(R.layout.menu_lay);
Just like my problem above the menu item can never be invoked by the normal onOptionsItemSelected listener so the way to make it work is to implement onClickListener on the item's custom View so in my case the way to make it invoke is by
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getRefresh.getActionView().setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
//refresh some data
}
});
return true;
}
I have a TabGroup in my app. How can I have different actions items for each Tab ? I just found an example from here, but it is not enough for me.
Can someone provide some hints or at least a link ?
I'm using SlidingTabLayout, and this is the way i changed the Toolbar icons for each Tab.
I've 3 tabs, each tab got its own fragment, and in each fragment I've created the following:
Tab 1 fragment:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.tab_1_menu, menu);
}
Tab 2 fragment:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.tab_2_menu, menu);
}
And so on, hope this help .
Full Example with getItemId / Click
#Override
public boolean onCreateOptionsMenu(Menu menu) {
//Set The Menu View
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//Menu Items, every fragment menu item
//Must have different ID
if (id == R.id.settings) {
//Do Something here
);
return true;
}
if (id == R.id.About) {
//Do Something here
);
return true;
}
if ( id == R.id.exit)
{
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
I have an ActionBarActivity and one Fragment. The Activity has no menu inflated, while the Fragment has a menu with two buttons. Fragment menu is visible, but the buttons don't react at all when tapped. At debugging I can see that both onCreateOptionsMenu() for Fragment and Activity get called, but when tapping buttons no onOptionsItemSelected() gets called, neither from Activity nor from Fragment.
Activity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
return false;
}
Fragment
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mainActivity = (NavigationActivity)getActivity();
setHasOptionsMenu(true);
}
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState){
return (ScrollView) inflater.inflate(R.layout.tutoring_detail, container, false);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.query_details_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.accept_query:
respondToQuery(true);
return true;
case R.id.decline_query:
respondToQuery(false);
return true;
default:
break;
}
return false;
}
Menu to be displayed in Fragment
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<item
android:id="#+id/accept_query"
android:orderInCategory="100"
app:showAsAction="always"
android:checkable="true"
style="?android:attr/borderlessButtonStyle"
app:actionViewClass="android.widget.ImageButton"/>
<item
android:id="#+id/decline_query"
android:orderInCategory="101"
app:showAsAction="always"
android:checkable="true"
style="?android:attr/borderlessButtonStyle"
app:actionViewClass="android.widget.ImageButton"/>
</menu>
In the Activity class,
#Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
return false;
}
In the Fragment,
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(getActivity(), "called " + item.getItemId(), Toast.LENGTH_SHORT).show();
return super.onOptionsItemSelected(item);
}
You must use super.onOptionsItemSelected(item) in the parent activity's onOptionsItemSelected(...) method.
From Activity | Android Developers:
Derived classes should call through to the base class for it to perform the default menu handling.
Try moving setHasOptionsMenu(true) inside of the onCreateView() method in your Fragment instead of onCreate().
I had a similar issue after making special layout for my action button (I made an Image Button and needed to pad it and change some other things so I had to use layout for it).
Then onOptionsItemSelected lost connection with this imageButton so I just use clickListener for it inside onCreateOptionsMenu. It might not be the best practice, maybe there is a better solution, but this is what solve my problem.
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.picker_list_menu, menu);
MenuItem itemDone = menu.findItem(R.id.menu_done);
MenuItemCompat.setActionView(itemDone, R.layout.menu_layout_done);
menuDoneIB = (ImageButton) MenuItemCompat.getActionView(itemDone);
itemDone.getActionView().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//your code..
}
});
}
Try using oncreateview
public static class ExampleFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
If you have dynamically changed menu item, for instance, a badge menu (https://stackoverflow.com/a/16648170/2914140 or https://stackoverflow.com/a/26017587/2914140), you should initialize the item in onCreateOptionsMenu and re-set setOnClickListeners after every change of the item.
In my case:
private MenuItem menuItem;
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_catalog, menu);
menuItem = menu.findItem(R.id.action_badge);
writeBadge(0);
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_badge) {
// Your code.
return true;
}
return super.onOptionsItemSelected(item);
}
private void writeBadge(int count) {
MenuItemCompat.setActionView(menuItem, R.layout.item_badge);
RelativeLayout layout = (RelativeLayout) MenuItemCompat.getActionView(menuItem);
// A TextView with number.
TextView tv = (TextView) layout.findViewById(R.id.badge);
if (count == 0) {
tv.setVisibility(View.INVISIBLE);
} else {
tv.setVisibility(View.VISIBLE);
tv.setText(String.valueOf(count));
}
// An icon, it also must be clicked.
ImageView imageView = (ImageView) layout.findViewById(R.id.image);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
onOptionsItemSelected(menuItem);
}
};
menuItem.getActionView().setOnClickListener(onClickListener);
imageView.setOnClickListener(onClickListener);
}
I am passing the click event to fragment from the activity. This worked for me.
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
return getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getFragments().size() - 1)
.onOptionsItemSelected(item);
}
I have a MainActivity on that I have a navigation drawer,Lists of navigation drawers are fragments.
I want to have refresh button on one of my fragment,to add refresh button on action bar i added following code in my MainActivity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.activity_main_actions, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// toggle nav drawer on selecting action bar app icon/title
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
if (item.getItemId() == R.id.action_settings) {
return true;
} else if(item.getItemId() == R.id.action_refresh) {
if(haveNetworkConnection(getApplicationContext()))
{
LoadAllProducts task = new LoadAllProducts(ListDepartmentSemesterActivity.this);
task.execute();
}else{
Toast.makeText(getApplicationContext(), "No Internet Connection", Toast.LENGTH_LONG).show();
}
return true;
}else {
return super.onOptionsItemSelected(item);
}
}
But the thing is on every fragment refresh button is visible and from any fragments i can refresh products,how to hide it on other fragments???
Navigation drawer MainActivity and Fragments share same action bar???
Define onOptionsItemSelected(MenuItem item) in fragment and add your refresh button to it. And insert this in your fragment:
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
setRetainInstance(true);
}
if you want handle menu button in each fragment try this code
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.simple_calendar_view,
container, false);
// you code here
setHasOptionsMenu(true);
return rootView;
}
inside same fragment you want handle its menu item clicks
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh:
// you Custom code here
// Example i make method called Refresh_event
Refresh_event();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
if you need hide some menu item when you open slide menu navigation
in you activity that host fragments
public void onDrawerClosed(View view) {
// calling onPrepareOptionsMenu() to show action bar icons
supportInvalidateOptionsMenu();
}
public void onDrawerOpened(View drawerView) {
// calling onPrepareOptionsMenu() to hide action bar icons
supportInvalidateOptionsMenu();
}
/* *
* Called when invalidateOptionsMenu() is triggered
*/
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
// if nav drawer is opened, hide the action item(About App && Refresh)
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.about_app).setVisible(!drawerOpen);
menu.findItem(R.id.refresh).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
How should I access the actionbar's menu items in fragment ?
I have tried this but nothing happened
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.menu_refresh:
Toast.makeText(getActivity().getApplicationContext(), "clicked", Toast.LENGTH_SHORT).show();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Auto-generated method stub
super.onCreateOptionsMenu(menu, inflater);
}
Follow this steps:
Add setHasOptionsMenu(true) method in your Fragment.
Override onCreateOptionsMenu(Menu menu, MenuInflater inflater) and
onOptionsItemSelected(MenuItem item) methods in your Fragment.
Inside your onOptionsItemSelected(MenuItem item) Activity's method,
make sure you return false when the menu item action would be
implemented in onOptionsItemSelected(MenuItem item) Fragment's
method.
Example:
Activity
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Do Activity menu item stuff here
return true;
case R.id.fragment_menu_item:
// Not implemented here
return false;
default:
break;
}
return false;
}
Fragment
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
....
}
#Override
public void onCreateOptionsMenu(Menu menu,MenuInflater inflater) {
// Do something that differs the Activity's menu here
super.onCreateOptionsMenu(menu, inflater);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.activity_menu_item:
// Not implemented here
return false;
case R.id.fragment_menu_item:
// Do Fragment menu item stuff here
return true;
default:
break;
}
return false;
}
In your fragments onCreate method add setHasOptionsMenu(true);.
If your fragment is in a ViewPager then the fragment with the ViewPager also needs the above line.
You cant access directly ActionBar menu items in a Fragment.
What you can do is put setHasOptionsMenu(true); in onCreateView function in fragment class and this calls the function onCreateOptionsMenu(Menu menu) in the corresponding activity.
There, you can access all the menu items you have in the action bar.
You can use:
MenuItem item = menu.getItem(index);
You have one example of using this:
in fragment onCreateView class:
setHasOptionsMenu(true);
in corresponding activity class:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
MenuItem item = menu.getItem(0);
if(condition)
item.setVisible(true);
else
item.setVisible(false);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle action bar actions click
switch (item.getItemId()) {
case R.id.action_logout:
makeLogout();
return true;
default :
return super.onOptionsItemSelected(item);
}
}
One thing I would add to this (my reputation does not allow me to comment) and reason it was not working for me.
Make sure your fragment's hosting activity extends AppCompatActivity not FragmentActivity!
public class MainActivity extends AppCompatActivity {
}
From the Google Reference Documentation for FragmentActivity:
Note: If you want to implement an activity that includes an action bar, you should instead use the ActionBarActivity class, which is a subclass of this one, so allows you to use Fragment APIs on API level 7 and higher.
ActionBarActivity now being deprecated, use AppCompatActivity instead. When using AppCompatActivity, also make sure you set "the activity theme to Theme.AppCompat or a similar theme" (Google Doc too).
android.support.v7.app.AppCompatActivity is a subclass of the android.support.v4.app.FragmentActivity class (see AppCompatActivity ref doc).
Menu item main.xml file :
<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="dbl.contact.manager.MainActivity" >
<item
android:id="#+id/menu_search"
android:actionLayout="#layout/action_bar"
android:icon="#drawable/searchagain"
android:orderInCategory="0"
android:showAsAction="always"
android:title="search"/>
</menu>
This is Menu item. custom action bar like edittext in the action bar You have to create a custom layout. Here custom action_bar.xml file :
<?xml version="1.0" encoding="utf-8"?>
<EditText
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/inputSearch"
android:layout_width="280dp"
android:layout_height="40dp"
android:cursorVisible="true"
android:hint="Search"
android:imeOptions="actionDone"
android:inputType="text"
android:textColor="#android:color/white"
android:textCursorDrawable="#android:color/white" />
Then in the fragment class you to override. Just Copy and paste this code.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// TODO Add your menu entries here
super.onCreateOptionsMenu(menu, inflater);
getActivity().getMenuInflater().inflate(R.menu.main, menu);
View v = (View) menu.findItem(R.id.menu_search).getActionView();
inputSearch = (EditText)v.findViewById(R.id.inputSearch);
inputSearch.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
// clientAdapter.getFilter().filter(s.toString());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
// ListData.this.clientAdapter.getFilter().filter(s);
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// TODO Auto-generated method stub
adapter.getFilter().filter(s.toString());
}
});
}