Colleagues, MainActivity class is derived from FragmentActivity, and for some reason activity's onCreateOptionsMenu() is not getting called. I have the first breakpoint in onCreate(), which is getting triggered, and the second one in onCreateOptionsMenu(), which is not getting triggered.
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
private FragmentPagerAdapter m_fragmentPagerAdapter;
private ViewPager m_viewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Create the adapter that will return a fragment for each of the primary sections of the activity.
m_fragmentPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
m_viewPager = (ViewPager) findViewById(R.id.container);
m_viewPager.setAdapter(m_fragmentPagerAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu); // Inflate the menu; this adds items to the action bar if it is present.
return true;
}
}
Fragments in this app don't have their own menus. One menu belonging to the activity "covers" everything.
Theme is Holo.Light
What prevent onCreateOptionsMenu() from getting called? What am I missing?
Double-check that you have really set the app theme to Holo.Light. I can't explain what is causing the problem, but I was able to reproduce it. After experimenting with changes between FragmentActivity and AppCompatActivity, I accidentally ran with FragmentActivity and theme set to Theme.AppCompat.Light. This created the behavior you are seeing, with onCreateOptionsMenu() not called.
As a side issue, you should fix your onCreateOptionsMenu() to call through to super as noted in the documentation: "Deriving classes should always call through to the base implementation".
Try:
setHasOptionsMenu();//call it from onCreate(); or onViewCreated();
This method only used in fragment to tell the activity that this fragment has an option menu.
Add setHasOptionsMenu(true); in your onCreate() method.
Related
I'm trying to add ActionBar buttons to a FragmentActivity and I can't figure out what I'm doing wrong.
When running the app all I see is the default menu button on the ActionBar and not my button..
The FragmentActivity :
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.animalsmenu,menu);
return true;
}
The xml file:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/dogs"
android:title="Dogs"
android:orderInCategory="1"
app:showAsAction="always">
</item>
Make your MainActivity extend ActionBarActivity instead of FragmentActivity.
ActionBarActivity itself extends FragmentActivity so you should be fine.
The FragmentActivity class extends (derives from) the Activity class. The documentation for the Activity onCreateOptionsMenu(Menu menu) method states the following...
The default implementation populates the menu with standard system menu items. These are placed in the CATEGORY_SYSTEM group so that they will be correctly ordered with application-defined menu items. Deriving classes should always call through to the base implementation.
In other words, change your code to...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.animalsmenu, menu);
super.onCreateOptionsMenu(menu);
return true;
}
This will inflate your menu item into the Menu passed in to your overridden method and then you pass it to the parent (super) version of the method.
From the Fragment Documents
public void setHasOptionsMenu (boolean hasMenu) Report that this fragment would like to participate in populating the options menu by receiving a call to onCreateOptionsMenu(Menu, MenuInflater) and related methods.
Hence you should call setHasOptionsMenu(true) in your onCreate().
Or for backwards compatibility it's better to place this call as late as possible at the end of onCreate() or even later in onActivityCreated().Try using this in either onCreate() or onActivityCreated().
Hope this helps.
I'm starting a new project that uses the AppCompat/ActionBarCompat in v7 support library. I'm trying to figure out how to use the getSupportActionBar from within a fragment. My activity that hosts the fragment extends ActionBarActivity, but I don't see a similar support class for Fragments.
From within my fragment
public class CrimeFragment extends Fragment {
//...
getActivity().getSupportActionBar().setSubtitle(R.string.subtitle); // getSupportActionBar is not defined in the v4 version of Fragment
//...
}
The google page for using it (http://android-developers.blogspot.in/2013/08/actionbarcompat-and-io-2013-app-source.html) says there should be no changes for the v4 fragment. Do I need to cast all my getActivity() calls to an ActionBarActivity? That seems like poor design.
After Fragment.onActivityCreated(...) you'll have a valid activity accessible through getActivity().
You'll need to cast it to an ActionBarActivity then make the call to getSupportActionBar().
((AppCompatActivity)getActivity()).getSupportActionBar().setSubtitle(R.string.subtitle);
You do need the cast. It's not poor design, it's backwards compatibility.
While this question has an accepted answer already, I must point out that it isn't totally correct: calling getSupportActionBar() from Fragment.onAttach() will cause a NullPointerException when the activity is rotated.
Short answer:
Use ((ActionBarActivity)getActivity()).getSupportActionBar() in onActivityCreated() (or any point afterwards in its lifecycle) instead of onAttach().
Long answer:
The reason is that if an ActionBarActivity is recreated after a rotation, it will restore all Fragments before actually creating the ActionBar object.
Source code for ActionBarActivity in the support-v7 library:
#Override
protected void onCreate(Bundle savedInstanceState) {
mImpl = ActionBarActivityDelegate.createDelegate(this);
super.onCreate(savedInstanceState);
mImpl.onCreate(savedInstanceState);
}
ActionBarActivityDelegate.createDelegate() creates the mImpl object depending on the Android version.
super.onCreate() is FragmentActivity.onCreate(), which restores any previous fragments after a rotation (FragmentManagerImpl.dispatchCreate(), &c).
mImpl.onCreate(savedInstanceState) is ActionBarActivityDelegate.onCreate(), which reads the mHasActionBar variable from the window style.
Before mHasActionBar is true, getSupportActionBar() will always return null.
Source for ActionBarActivityDelegate.getSupportActionBar():
final ActionBar getSupportActionBar() {
// The Action Bar should be lazily created as mHasActionBar or mOverlayActionBar
// could change after onCreate
if (mHasActionBar || mOverlayActionBar) {
if (mActionBar == null) {
... creates the action bar ...
}
} else {
// If we're not set to have a Action Bar, null it just in case it's been set
mActionBar = null;
}
return mActionBar;
}
If someone uses com.android.support:appcompat-v7: and AppCompatActivity as activity then this will work
((AppCompatActivity)getActivity()).getSupportActionBar().setSubtitle(R.string.subtitle);
For those using kotlin,
(activity as AppCompatActivity).supportActionBar.setSubtitle(R.string.subtitle)
As an updated answer for Pierre-Antoine LaFayette's answer
ActionBarActivity is deprecated; use AppCompatActivity instead
((AppCompatActivity)getActivity()).getSupportActionBar();
in your fragment.xml add Toolbar Tag from support library
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
Now how we can control it from MyFragment class? let's see
inside onCreateView function add the following
mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(mToolbar);
//add this line if you want to provide Up Navigation but don't forget to to
//identify parent activity in manifest file
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
and if you want to add items to the toolbar within MyFragment
you must add this line inside onCreateView function
setHasOptionsMenu(true);
this line is important, if you forget it, android will not populate your menu Items.
assume we identify them in menu/fragment_menu.xml
after that override the following functions
#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;
}
hope this helps
As an addendum to GzDev's answer, if you already have the string, you can use kotlin's auto-setter:
(activity as AppCompatActivity).supportActionBar?.subtitle = my_string
And you can turn it off by simply using an empty string.
Note that this works for both the title and the subtitle.
I am getting this error when adding this method to my class:
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate your menu.
getSupportMenuInflater().inflate(R.menu.share_action_provider, menu);
// Set file with share history to the provider and set the share intent.
MenuItem actionItem = menu.findItem(R.id.menu_item_share_action_provider_action_bar);
ShareActionProvider actionProvider = (ShareActionProvider) actionItem.getActionProvider();
//actionProvider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
// Note that you can set/change the intent any time,
// say when the user has selected an image.
actionProvider.setShareIntent(createShareIntent());
return true;
}
Here is how I declare the class:
public class MainActivity extends SherlockActivity implements ActionBar.OnNavigationListener
and I have this method in other projects and I dont' get that error. Would anyone know why this error started happening here?
Thanks!
SherlockActivity does override that methods as final so the framework can handle itself.
To fix this, only delete the imports from Menu, MenuItems and etc.. and import from com.sherlockbar since you are overriding the correct method.
You are likely using the wrong Menu class.
SherlockActivity overrides and finalizes the Android-standard onCreateOptionsMenu(android.view.Menu menu) method.
It also declares the (new, overloaded) method onCreateOptionsMenu(Menu menu), which takes a com.actionbarsherlock.view.Menu.
Can anyone give a quick example of how to change the contents of an Activity action bar based on something that takes place in a fragment? My intent:
Normal menu items -> Something in the fragment is moved -> menu items change to save / discard buttons.
My first impulse is to setup Broadcast Receivers in both the activity and the fragment to cross talk, but I am not sure if this is correct.
Fragments can change menu in actionbar. For that you have to add necessary flag in fragment's oncreate() using method setHasOptionsMenu(true);
When your fragment is loaded you will get call at onCreateOptionsMenu(Menu menu, MenuInflater inflater) just like in an activity. Then do necessary changes to your menu.
Save you menu as global in fragment, and whenever you want to make a change, apply on it.
The following works for me. I have a custom class that implements ListView.MultiChoiceModeListener inside a Fragment:
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
// Choose the correct Action Bar menu to display
int menu = myCondition == true ? R.menu.my_default_menu : R.menu.my_menu_2;
// Configure to use the desired menu
mode.getMenu().clear();
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(menu);
}
Given how you detect 'something in the fragment has moved', extending ListView.MultiChoiceModeListener may not work for you, but hopefully this illustrates how to change the menu. The key is to get access to a ActionMode instance.
I think you want to use a contextual action mode. On the drag event, you will start a new ActionMode which can replace the contents of the action bar with menu items specific to what you want to allow the user to do. Once the user chooses an action, you finish the action mode and the action bar returns to its previous state.
Not sure if an ActionBar instance would help with the menu you but would surely be useful.. Here's a way to get about it
Try this to get the ActionBar from the FragmentActivity using the onAttach(Activity activity) method in the Fragment.
First of all make a global object of your FragmentActivity in the Fragment like this
public class YourFragment extends Fragment {
private YourFragmentActivity context;
}
Override this in the YourFragment class
#Override
public void onAttach(Activity activity){
context = (YourFragmentActivity)activity;
super.onAttach(activity);
}
Then in the OnCreate method in the YourFragment do this
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState){
...
android.support.v7.ActionBar actionBar = context.getSupportActionBar();
...
}
I am using a TabActivity (Main) with 3 TabSpecs
I am using Intents for the content of the 3 Tabs
TabA, TabB, TabC for example.
All these tab activities use common data that is stored in SharedPreferences
In the Main TabActivity I have an options menu which has a refresh option.
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.refresh_menu:
new updateCommonDataFromWeb().execute();
...
}
}
this refresh uses an AsyncTask (updateCommonDataFromWeb) to reload the common data from the web.
I need a way to tell the 3 tab activities to refresh their views and rebuild their content from the newly downloaded data.
When the tab activities are first created they load the data from SharedPreferences like so:
public class TabA extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.taba);
SharedPreferences prefs = getSharedPreferences(this.getString(R.string.prefs_name), 0);
String usage_data = prefs.getString("common_data", "");
}
}
I thought about making a common method on each of the tab activities
like.... reloadViewData()
I thought maybe I could use the activity manager from the Main TabActivity to get the activity of the current tab like so:
String tabTag = tabHost.getCurrentTabTag();
Activity activity = getLocalActivityManager().getActivity(tabTag);
# then call the reloadData() method of the tab activity
activity.reloadViewData();
Unfortunately i cant get this approach to work, whilst activity is the correct instance its an Activity instead of a TabA,TabB or TabC
Maybe i've completely taken the wrong approach to the whole thing.
I have also read alot about not using Activities for tab content instead using views.
However I dont know what view to use to replace my <RelativeLayout /> as i cant use my R.layout.* as views.
If you change the common_data in the sharedPreferences you can also implement OnSharedPreferenceChangeListener in the Activites and check for key.equals("common_data").
You can extend Activity and make all your child activities to inherit from your extended activity, as this way you will implement the same code (service handling) in 1 activity(the parent).
Activity -> MyParentActivity -> MyChildActivity1
This way you will be able to cast all your child activities to MyParentActivity and run your custom method (you want to be implemented)