I know this has been covered elsewhere, but I'm new to the Android platform and am having a hard time figuring out how to add basic menu options to my first app.
I have an options menu setup using
#Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
and
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/menuPrefs" android:icon="#android:drawable/ic_menu_preferences" android:title="Settings"></item>
</menu>
Then within my main java class I have
#Override
public boolean onOptionsItemSelected(MenuItem item){
if(item.getItemId()==R.id.menuPrefs) {
showPrefs();
}
private void showPrefs() {
Intent i = new Intent(this, Prefs.class);
startActivity(i);
}
And then in Prefs.java I have
public class Prefs extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Toast.makeText(getBaseContext(), "FNORD", Toast.LENGTH_LONG).show();
}
}
Now from this I would expect to see the Toast message "FNORD" when the menu option is pressed, however the application stops unexpectedly.
If I move the toast statement into the showPrefs() function in place of the startActivity call it works.
You forgot to call super.onCreate(Bundle savedInstanceState); on Prefs' onCreate() method.
You need to learn how to Debug in Eclipse and how to use the ADB and DDMS tools.
In order to get more details about an exception/force close you need to look for a view in Eclipse called Logcat(you will find in the DDMS perspective) there you will find a detailed traceback when/what and on what line is the issue.
For this you should read a complete article about Debugging in Android using Eclipse
(source: droidnova.com)
Solution was that I needed to add the Prefs.xml to the manifest.
Related
As I was following an old tutorial (Créez des applications pour Android -> openclassroom) I got stuck on this deprecated method addPreferencesFromResource(int id) from the PreferenceActivity class.
So my question is :
What is the new way of creating Preferences in Android ?
I found this post (What to use instead of “addPreferencesFromResource” in a PreferenceActivity?) that help me understand that you have to go through a PreferenceFragment in order to do it.
In the following explanation I use your.package. just to show that you have to put the package name. Everybody has its own package so please replace it with your package.
lets begin :
1. Preference Fragment
Create your PreferenceFragment class
MyPreferenceFragment
public class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.fragment_preference);
}
}
Then the associated xml resource
fragment_preference.xml (in the folder res/xml of your project)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="FOO">
<CheckBoxPreference
android:key="checkBoxPref"
android:title="check it out"
android:summary="click this little box"/>
</PreferenceCategory>
</PreferenceScreen>
That's all for the Fragment part.
2. Preference Activity
Create the PreferenceActivity class
MyPreferenceActivity
public class MyPreferenceActivity extends PreferenceActivity
{
#Override
public void onBuildHeaders(List<Header> target)
{
loadHeadersFromResource(R.xml.headers_preference, target);
}
#Override
protected boolean isValidFragment(String fragmentName)
{
return MyPreferenceFragment.class.getName().equals(fragmentName);
}
}
Do not forget to override isValidFragment(String fragmentName) method as you will get punched in the face by your application ! ;) More seriously I have no idea why you need to do this but it is needed. If someone has an explanation about this I'd gladly read it :)
EDIT :
Thanks to kirtan403 I now know why it is needed : it has to be set because of an (android framework fragment injection).
As you can see in the onBuildHeaders(List<Header> target) we load another xml file that contain the headers of the preference. In short, headers are the left part of the preference and the fragment are the right part (for tablet). For a phone you will first have the headers and when you click on an item the corresponding fragment will be put on top of the headers list.
Read this article (Multi-pane development in Android with Fragments - Tutorial) the images explain themselves.
Then the associated xml resource
headers_preference.xml (in the folder res/xml of your project)
<?xml version="1.0" encoding="utf-8"?>
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment="your.package.MyPreferenceFragment"
android:title="Goto: Preference fragment"
android:summary="An example of some preferences." />
</preference-headers>
As you may have noticed in the header section you have :
android:fragment="your.package.MyPreferenceFragment"
This will act as a Link to the fragment you want to show. On Tablet it will load on the right part and on the phone it will load on top of the current view.
3. Android Manifest
Now what you should do is to add your Activity to the AndroidManifest.xml file.
Inside the application section add these lines :
<activity
android:name="your.package.MyPreferenceActivity"
android:label="Preferences">
</activity>
You will probably tell me :
"Oh darling you forgot to put android:launchMode="singleTask" in your actvity"
But DO NOT PUT THIS as you will never load your fragment on phone. This error was solved by a great man ! This is the link to his blog (Android header preferences on small screen/phone).
4. Start the Preferences from Menu
Finally you need to add the ability to show this Preference !! To do so you will need 3 things :
The Menu
menu.xml (in the folder res/menu of your project)
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/preferences"
android:title="Preferences" />
</menu>
Loading this Menu in your Main activity (not the PreferenceActivity) under the method onCreateOptionsMenu(Menu menu)
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu, menu);
return true;
}
Starting the MyPreferenceActivity Activity when you click on that button.
For that you will need to override the onOptionsItemSelected(MenuItem item) method in your Main activity.
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch(item.getItemId())
{
case R.id.preferences:
{
Intent intent = new Intent();
intent.setClassName(this, "your.package.MyPreferenceActivity");
startActivity(intent);
return true;
}
}
return super.onOptionsItemSelected(item);
}
Et voila les amis !
I haven't tested this code. I took it and modified it from my own code so I may have not well copy pasted things. If you encounter errors tell me, I'll try to figure out the problem and fix this.
I hope this post will help some people out there :D
Cheers !
I liked the solution from this post: http://alvinalexander.com/android/android-tutorial-preferencescreen-preferenceactivity-preferencefragment
.. because it seems the most compact for someone that just needs something very basic up and running quickly. It has only one .java file and two small xml files.
Activity Config REMINDERS
After adding the 3 files to your project, Don't forget to
A) Add the Prefs Activity to Manifest file
B) Add some way to launch the Prefs Activity .. e.g., a Button or Menu item
Add the following files to your project. Use the order they are listed in to avoid compile errors.
Add /res/values/array.xml
<resources>
<string-array name="listArray">
<item>Ace</item>
<item>Club</item>
</string-array>
<string-array name="listValues">
<item>Ace</item>
<item>Club</item>
</string-array>
</resources>
Add /res/xml/preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference android:title="Your Name"
android:key="username"
android:summary="Please provide your username"></EditTextPreference>
<CheckBoxPreference android:title="Application Updates"
android:defaultValue="false"
android:summary="This option if selected will allow the application to check for latest versions."
android:key="applicationUpdates" />
<ListPreference android:title="Download Details"
android:summary="Select the kind of data that you would like to download"
android:key="downloadType"
android:defaultValue="Ace"
android:entries="#array/listArray"
android:entryValues="#array/listValues" />
</PreferenceScreen>
Add the Activity code
public class AppPreferenceActivity extends PreferenceActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
checkValues();
}
public static class MyPreferenceFragment extends PreferenceFragment
{
#Override
public void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
private void checkValues()
{
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
String strUserName = sharedPrefs.getString("username", "NA");
boolean bAppUpdates = sharedPrefs.getBoolean("applicationUpdates",false);
String downloadType = sharedPrefs.getString("downloadType","1");
String msg = "Cur Values: ";
msg += "\n userName = " + strUserName;
msg += "\n bAppUpdates = " + bAppUpdates;
msg += "\n downloadType = " + downloadType;
Toaster.shortDebug(msg);
}
}
I'm trying to override the options menu in my android.support.v4.app.Fragment. The onCreateOptionsMenu method gets called when using an emulator with Android 4.0, but not if I run it on an actual device running 4.4.2.
My main Activity extends ActionBarActivity, and I make the required setHasOptionsMenu(true) call in the onCreate of my Fragment. I've tried both ActionBarCompat and ActionBarSherlock, and I have the same exact issue with both libraries.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if(DEBUG) Log.v("Debug", "This method doesn't get called on a device");
super.onCreateOptionsMenu(menu, inflater);
...
try moving setHasOptionsMenu(true); in onResume().
I'm not sure however I'll try to help you. With ABS, I answered on a "same" behaviour and it worked well. Then, with AppCompat, you have to call super method after all your stuff as follows:
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if(DEBUG) Log.v("Debug", "This method doesn't get called on a device");
... // I thought this "..." was your stuff, so do it here
super.onCreateOptionsMenu(menu, inflater);
}
You have a great explaination and example on this answer. You should also check your imports. However it's maybe related to your device and not with the way you do it (not sure, because on 4.2.2 with ABS, it worked well).
The new ShareActionProvider available in Android 4.0 (or in earlier versions if you're using ActionBarSherlock) has a feature where the last used item is displayed in the action bar. Is there anyway to turn this off?
For me, the best solution for avoid the history icon is don't use ShareActionProvider, instead of it, create it as any other action:
<item
android:id="#+id/menu_action_share"
android:showAsAction="ifRoom"
android:icon="#drawable/ic_action_share"
android:title="#string/share"/>
at the menu/activity_actions.xml put a item with the ic_action_share icon...
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_actions, menu);
return super.onCreateOptionsMenu(menu);
}
Inflate the menu normally...
private void actionShare(){
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_SUBJECT, "my string");
i.putExtra(Intent.EXTRA_TEXT, "another string");
startActivity(i);
//Or like above will always display the chooser
//startActivity(Intent.createChooser(i, getResources().getText(R.string.share)));
}
Create a method with an ACTION_SEND intent
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_share:
actionShare();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
And finally call to this method from onOptionsItemSelected
more info ->Sending Simple Data to Other Apps
Start the share activity by yourself:
shareActionProvider.setShareIntent(intent);
shareActionProvider.setOnShareTargetSelectedListener(this);
#Override
public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
// start activity ourself to prevent search history
context.startActivity(intent);
return true;
}
Then the ShareActionProvider will not add the chosen activity to the share history.
I created my own version of the ShareActionProvider (and supporting classes), you can copy them into your project (https://gist.github.com/saulpower/10557956). This not only adds the ability to turn off history, but also to filter the apps you would like to share with (if you know the package name).
private final String[] INTENT_FILTER = new String[] {
"com.twitter.android",
"com.facebook.katana"
};
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.journal_entry_menu, menu);
// Set up ShareActionProvider's default share intent
MenuItem shareItem = menu.findItem(R.id.action_share);
if (shareItem instanceof SupportMenuItem) {
mShareActionProvider = new ShareActionProvider(this);
mShareActionProvider.setShareIntent(ShareUtils.share(mJournalEntry));
mShareActionProvider.setIntentFilter(Arrays.asList(INTENT_FILTER));
mShareActionProvider.setShowHistory(false);
((SupportMenuItem) shareItem).setSupportActionProvider(mShareActionProvider);
}
return super.onCreateOptionsMenu(menu);
}
There is no API to do this. However, the class is really simple and you could very easily create your own version of ShareActionProvider that did not keep a history. You would just have to determine the sort order of the possible targets using some other means of ordering (e.g., alphabetically).
Point of clarification: It's not the "last used", it's "most often used", across a sliding window period of time.
If you prefer not to use history, then before creating your views, call
yourShareActionProvider.setShareHistoryFileName(null);
Description of this method, from the official docs (emphasis mine):
Sets the file name of a file for persisting the share history which history will be used for ordering share targets. This file will be used for all view created by onCreateActionView(). Defaults to DEFAULT_SHARE_HISTORY_FILE_NAME. Set to null if share history should not be persisted between sessions.
EDIT: I should clarify — The "most often used" item won't show up if there's no history, so this is currently the only way of removing that button.
Although It's been 2 years ago today but I'd like to share my experience as I made a custom ShareActionProvider class and add this line chooserView.getDataModel().setHistoryMaxSize(0); inside onCreateActionView() which did all the magic for me ! .. I tested it on Lollipop device and on API 16 emulator device and it works perfectly. here is my custom class :
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v7.internal.widget.ActivityChooserView;
import android.support.v7.widget.ShareActionProvider;
import android.view.View;
public class MyShareActionProvider extends ShareActionProvider {
/**
* Creates a new instance.
*
* #param context Context for accessing resources.
*/
public MyShareActionProvider(Context context) {
super(context);
}
#Override
public View onCreateActionView() {
ActivityChooserView chooserView = (ActivityChooserView) super.onCreateActionView();
Drawable icon;
if (Build.VERSION.SDK_INT >= 21) {
icon = getContext().getDrawable(R.drawable.share_icon);
}else{
icon = getContext().getResources().getDrawable(R.drawable.share_icon);
}
chooserView.setExpandActivityOverflowButtonDrawable(icon);
chooserView.getDataModel().setHistoryMaxSize(0);
return chooserView;
}
}
add the code like this:
private void addShareSelectedListener() {
if (null == mShareActionProvider) return;
OnShareTargetSelectedListener listener = new OnShareTargetSelectedListener() {
public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
mContex.startActivity(intent);
return true;
}
};
//Set to null if share history should not be persisted between sessions.
mShareActionProvider.setShareHistoryFileName(null);
mShareActionProvider.setOnShareTargetSelectedListener(listener);
}
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(null);
OnShareTargetSelectedListener listener = new OnShareTargetSelectedListener() {
public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
startActivity(intent);
return true;
}
};
actionProvider.setOnShareTargetSelectedListener(listener);***
I'm writing an application which I want to be able to launch a second class with a different layout when a menu button is pressed. the code I have to switch classes is:
case Menubutton1:
Intent i = new Intent(Budgeter.this, Outgoings.class);
startActivity(i);
return true;
(Obviously within a case statement)
How do I create an xml file which only relates to the second class? Also do I need to edit AndroidManifest.xml?
Finally if anyone could point me towards some good tutorials on intents I would greatly appreciate it.
in first class write a method
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuItem mnuHome =menu.add(0, 0, 0, "Home");
mnuHome.setAlphabeticShortcut('h');
mnuHome.setIcon(R.drawable.home_icon);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if(item.getTitle() == "Home") {
Intent i = new Intent(Budgeter.this, Outgoings.class);
startActivity(i);
finish();
}
}
And in second class you have to override onCreate() method and there you can set Layout as
setContentView(R.Layout.XML);
Also for second class you have to define in menifest.xml
I'm guessing here that you're confused about thinking the layout file for an Activity has to be called main.xml??? If so, this is not the case...
You can have budgeter.xml, outgoings.xml etc etc.
Just use setContentView(R.layout.budgeter) in the Budgeter Activity's onCreate(...) method and setContentView(R.layout.outgoings) in the Outgoings Activity onCreate(...) and so on.
Also do I need to edit AndroidManifest.xml?
Yes, all Activities must be registered in AndroidManifest.xml
As for working with Intents, try this as a starter...
Intents and Intent Filters
you should add the second(any) class in AndroidManifest.xml file if the class extended from android core components(Activity,Service,ContentProvider,BroadcastReceiver,BroadcastReceiver is a little different).It is possible to create another XML file in "layout" under "res".
I have a bit of an issue that I am not sure how to fix. I have an options menu which has code like this
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.main_menu_settings:
startActivity(new Intent(MainMenuActivity.this, BackofficePreferencesActivity.class));
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
No error happens if I comment out the line
startActivity(new Intent(MainMenuActivity.this, BackofficePreferencesActivity.class));
My preferences activity looks like this
public class BackofficePreferencesActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.layout.preferences);
}
}
and my layout
<PreferenceCategory android:title="System Configuration">
<ListPreference
android:title="Environment"
android:summary="Select the environment"
android:key="#string/pref_current_environment"
android:defaultValue="Production"
android:entries="#array/environment_list"
android:entryValues="#array/environment_list"
android:dialogTitle="Select Environment" />
</PreferenceCategory>
I even tried removing the list preference to see if the screen would load empty, but it still errors. In eclipse, usually I can see what went wrong by looking in the LogCat tab, but for some reason nothing is being logged there anymore. I tried rebooting my AVD and that hasn't helped.
You need to declare the Activty in your manifest file.
Sample XML code:
<activity android:label="#string/sample"
android:name=".Sample"
android:icon="#drawable/sample">
</activity>