I have an android app with a UserSettingsActivity derived from PreferenceActivity. When I open this activity, no menu bar is shown.
In order to have the 'bar' or 'action bar' on top of the settings screen, I searched for it in google on found this link and this link. Because the first answer seems outdated, I tried the suggestion in the second link which does not work. Therefore I am posting this question again.. Here are the pieces of code I am using:
preferences.xml ( I added just the android.support.v7.widget.Toolbar part):
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationContentDescription="#string/abc_action_bar_up_description"
android:background="?attr/colorPrimary"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="#string/action_settings"
/>
<ListPreference
android:title="#string/UpdateInterval"
android:summary="#string/UpdateIntervalText"
android:key="updateInterval"
android:defaultValue="24"
android:entries="#array/listArray"
android:entryValues="#array/listValues" />
<ListPreference
android:title="#string/LimitSetting"
android:summary="#string/LimitSettingText"
android:key="limitSetting"
android:entries="#array/limitArray"
android:entryValues="#array/limitValues" />
<Preference
android:key="reset"
android:title="Reset database"
android:summary="This will remove every entry in the database"
/>
</PreferenceScreen>
and UserSettingsActivity.java (I added the onPostCreate part):
public class UserSettingActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new MyPreferenceFragment()).commit();
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.xml.preferences, root, false);
root.addView(bar, 0); // insert at top
bar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
public class MyPreferenceFragment extends PreferenceFragment {
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences); // <<-- ERROR HERE
// doing something here
}
}
}
However, I get the following error:
Caused by: java.lang.ClassCastException: android.support.v7.widget.Toolbar cannot be cast to android.preference.Preference
on the marked line. Without the added pieces in those two files the app did work, without the (action)-bar, however...
What seems to be the problem here?
Try this.
Extend your activity with AppCompatActivity
Then add toolbar. Refer here: -http://coderzpassion.com/android-working-with-material-design/
PreferenceActivity is now deprecated. Use PreferenceFragment inside any other activity, which can be extended from any parent activity other than PreferenceActivity
I'm trying to display a actionbar in my preference screen.
In order to do so I added the following code in my SettingActivity
public class PreferencesActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences_activity);
getFragmentManager().beginTransaction()
.replace(R.id.container, new PreferencesFragment()).commit();
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
then the rest of my code in PreferencesFragment.
This works fine, but as soon as I press on a PreferenceScreen preference, the actionbar is hidden. If I go back to the preference main screen I can see it again.
Any idea how to keep the actionbar displayed (and updated with the PreferenceScreen label) ?
Edit: Looking at the PreferenceScreen code it looks like a full screen Dialog is opened when the PreferenceScreen is clicked on. Because my preference has a title the Dialog should display a title as well... but it doesn't
// Set the title bar if title is available, else no title bar
final CharSequence title = getTitle();
Dialog dialog = mDialog = new Dialog(context, context.getThemeResId());
if (TextUtils.isEmpty(title)) {
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
} else {
dialog.setTitle(title);
}
I finally managed to find a way to do this.
It's kind of ugly but it works.
First I add an the same Intent to every PreferenceScreen definition in my preferences.xml file (make sure to update the value of the extra parameter)
<PreferenceScreen
android:key="pref1"
android:summary="Summary1"
android:title="Title1" >
<intent
android:action="android.intent.action.VIEW"
android:targetPackage="my.package"
android:targetClass="my.package.activity.PreferencesActivity" >
<extra android:name="page" android:value="pref1" />
</intent>
...
</PreferenceScreen>
BTW my.package.activity.PreferencesActivity is my current Preference Activity
Then I add an intent-filter in the Manifest
<activity
android:name=".activity.PreferencesActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="#string/settings" >
<intent-filter android:label="Pref" >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.PREFERENCE" />
</intent-filter>
</activity>
I add some code in the PreferenceActivity to handle this
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences_activity);
this.fragment = new PreferencesFragment();
this.fragment.setActivityIntent(getIntent());
getFragmentManager().beginTransaction()
.replace(R.id.container, this.fragment).commit();
}
Finally I add the following code in my PreferencesFragment class
public void setActivityIntent(final Intent activityIntent) {
if (activityIntent != null) {
if (Intent.ACTION_VIEW.equals(activityIntent.getAction())) {
if (intent.getExtras() != null) {
final String page = intent.getExtras().getString("page");
if (!TextUtils.isEmpty(page)) {
openPreferenceScreen(page);
}
}
}
}
private void openPreferenceScreen(final String screenName) {
final Preference pref = findPreference(screenName);
if (pref instanceof PreferenceScreen) {
final PreferenceScreen preferenceScreen = (PreferenceScreen) pref;
((PreferencesActivity) getActivity()).setTitle(preferenceScreen.getTitle());
setPreferenceScreen((PreferenceScreen) pref);
}
}
Had the same issue. Nested PreferenceScreens did not have an ActionBar. After stepping through the code, it appears to be caused by a conflict between AppCompatActivity and PreferenceScreen.
On one hand AppCompatActivity provides its own action bar, and therefore requires a theme descending from Theme.AppCompat which specifies windowNoTitle = true somewhere (could not pinpoint exactly where). On the other -- PreferenceScreen uses platform Dialog with the activity theme (rather than sub-theme, e.g., dialogTheme). Could be a bug.
If you don't care about Theme.AppCompat, here's a simple workaround that works on API 21+:
use android.preference.PreferenceActivity as the base class for your activity
create a theme for that activity:
<!-- This theme is used to show ActionBar on sub-PreferenceScreens -->
<style name="PreferenceTheme" parent="">
<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>
</style>
specify android:theme="#style/PreferenceTheme" for this activity in the AndroidManifest.xml
What you'll get is more like a standard window title than a full ActionBar. I haven't yet figured out how to add a working back button, etc.
If you want to remain compatible with AppCompatActivity and the related themes, you'll need to request FEATURE_NO_TITLE on the activity window. Otherwise, you'll end up with two action bars (the built-in on top, and the support on bottom) in the top-level PreferenceScreen.
Since google sadly didn't fixed it until now, there is actually one much easier solution:
Set your SettingsActivity class to extend from just "Activity".
public class SettingsActivity extends Activity { ...
Create a new Theme in your v21/styles folder for your SettingsActivty and set the parent to "Theme.Material.*"
<style name="CustomThemeSettings" parent="android:Theme.Material">
<item name="android:colorPrimary">#color/...</item>
<item name="android:colorPrimaryDark">#color/...</item>
<item name="android:colorAccent">#color/...</item>
</style>
Set your new theme in your Manifest.xml file:
<activity
android:name=".SettingsActivity"
android:label="#string/title_activity_settingsactivity"
android:theme="#style/CustomThemeSettings" >
</activity>
It just works :)
(Optional) If you want to provide Material Design support for older Devices
you can put the SettingsActivity in an v21+ folder and create a other
SettingsActivity for older devices which has the parent AppCompat.
I have made an app that does have an action bar in the preferences activity. I can't seem to see the key to doing that, although I do remember it took me some time to nail it right.
It seems like our codes are quite similar. The only thing that gets to my attention is this import: import android.support.v7.app.ActionBarActivity;
Let me know if that helps any
public class SettingsActivity extends ActionBarActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new PrefsFragment() ).commit();
} // End of onCreate
static public class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences); // Load the preferences from an XML resource
}
} // end of PrefsFragment
}
Addition: do you have this in your styles.xml?
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
</style>
but as soon as I press on a PreferenceScreen preference, the actionbar is hidden. If I go back to the preference main screen I can see it again.
I guess you are launching a new activity when clicked on the preference
Your Preference fragment should look like this
public static class PreferencesFragment extends PreferenceFragment {
public PlaceholderFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.your_preferences);
}
}
Then sample your_preferences as below
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent" >
<PreferenceCategory
android:title="#string/pref_category_title">
<PreferenceScreen
android:title="#string/title"
android:summary="#string/summary">
<intent
android:targetClass="com.your.package.Youractivity "
android:targetPackage="com.your.package"/>
</PreferenceScreen>
<PreferenceScreen
android:title="#string/another_title">
<intent
android:targetClass="com.your.package.activity2"
android:targetPackage="com.your.package.activity"/>
</PreferenceScreen>
</PreferenceCategory>
And finally the main thing Youractivity should extend from ActionBarActivity
public class Youractivity extends ActionBarActivity {
}
The above code works for me.
As you mentioned in the question the fullscreen Dialog might be a problem.
Try to change the Dialog style to:
<style name="Theme.Holo.Dialog">
or to style which has these properties:
<item name="windowActionBar">true</item>
<item name="windowNoTitle">false</item>
Here you can read how to get reference to the Dialog.
Note: Only for API>=14
Source1 and Source2
PreferenceActivity1.java
public class PreferenceActivity1 extends android.preference.PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref1);
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
root.addView(bar, 0); // insert at top
bar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
}
PreferenceActivity2.java
public class PreferenceActivity2 extends android.preference.PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref2);
}
#Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
Toolbar bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
root.addView(bar, 0); // insert at top
bar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});
}
}
settings_toolbar.xml(layout)
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:navigationContentDescription="#string/abc_action_bar_up_description"
android:background="?attr/colorPrimary"
app:navigationIcon="?attr/homeAsUpIndicator"
app:title="#string/app_name"
/>
pref1.xml(xml)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="#android:style/Theme.Light" >
<PreferenceCategory android:title="Main Preferences" >
<CheckBoxPreference
android:key="wifi enabled"
android:title="WiFi" />
</PreferenceCategory>
<PreferenceScreen
android:key="key1"
android:summary=""
android:title="Wifi Settings" >
<intent
android:action="android.intent.action.VIEW"
android:targetClass="com.example.PreferenceActivity2"
android:targetPackage="com.example" />
</PreferenceScreen>
</PreferenceScreen>
pref2.xml(xml)
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="#android:style/Theme.Light" >
<PreferenceCategory android:title="Wifi Settings" >
<CheckBoxPreference
android:key="prefer wifi"
android:title="Prefer WiFi" />
</PreferenceCategory>
</PreferenceScreen>
Manifest
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name="com.example.PreferenceActivity1"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.PreferenceActivity2"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
Result
I created PreferenceCompatActivity that is an AndroidX-compatible drop-in replacement for the old PreferenceActivity.
It uses a compat fragment internally. Be sure to use the import only the androidx.preference.XXX classes instead of the android.preference.XXX ones.
public class PreferenceCompatActivity extends AppCompatActivity implements AppCompatCallback, PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
private PreferenceFragmentCompat fragment;
public void addPreferencesFromResource(#XmlRes int preferencesResId) {
fragment = new RootPreferencesFragment(preferencesResId);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commitNow();
}
public PreferenceManager getPreferenceManager() {
return fragment.getPreferenceManager();
}
public PreferenceScreen getPreferenceScreen() {
return fragment.getPreferenceScreen();
}
public Preference findPreference(CharSequence key) {
return fragment.findPreference(key);
}
#Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
LowerPreferencesFragment lowerFragment = new LowerPreferencesFragment(pref);
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, lowerFragment).addToBackStack("lower").commit();
return true;
}
public static class RootPreferencesFragment extends PreferenceFragmentCompat {
private int preferencesResId;
public RootPreferencesFragment(int preferencesResId) {
this.preferencesResId = preferencesResId;
}
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(preferencesResId);
}
}
public static class LowerPreferencesFragment extends PreferenceFragmentCompat {
private PreferenceScreen prefs;
public LowerPreferencesFragment() {
}
public LowerPreferencesFragment(PreferenceScreen prefs) {
this.prefs = prefs;
}
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
if (prefs != null) {
setPreferenceScreen(prefs);
prefs = null;
}
}
}
}
Or see this gist.
I am making a Plain Text Daydream on android and cannot open the settings screen. The rest of the daydream runs fine, but the settings don't. This is the settings xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<EditTextPreference
android:key="pref_text"
android:title="Text on daydream"
android:dialogTitle="Text on daydream:"
android:defaultValue="Hello world!" />
</PreferenceScreen>
I'm using PreferenceFragments too. I assume it's the XML, but just in case here are the fragment and activity too:
public class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.settings);
}
}
and
public class SettingsActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
}
How do I fix it?
All I had to do was set the settings activity as the default. Then it's fixed!
I use a PreferenceActivity with encapsulated preference screens.
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="prefs" >
<PreferenceScreen android:title="#string/pref_user_1" >
some text preference
</PreferenceScreen>
<PreferenceScreen android:title="#string/pref_user_2" >
some prefs
<PreferenceScreen android:title="#string/pref_user_2_1" >
some prefs
</PreferenceScreen>
</PreferenceScreen>
</PreferenceScreen>
my inherited preferenceActivity class has the folowing onCreate method
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new PrefsFragment()).commit();
}
and has an inner class
public static class PrefsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
public PrefsFragment() {
super();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.myprefs);
...
}
in the first screen I get the correct icon in the action bar (I call it Action Bar even if I haven't the <) but I would like to change the icon in the following screen.
I have tried getActivity().getActionBar().setIcon(R.drawable.ic_act); in the inner class and also in the preferenceActivity, but it doesn't change anything.
Does someone know how to do it, if possible?
I've an activity to represent a preference screen using one FragmentPreference.
Activity layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/frag"
android:name="com.test.preference.Frag"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Preference xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="General">
<SwitchPreference android:title="Enable module" android:key="asd" android:summaryOff="Turn on module" android:summaryOn="Turn off module"/>
<SwitchPreference android:title="Post in application" android:key="qwe" android:summary="Show posts in application"/>
<SwitchPreference android:title="Post in widget" android:key="zxc" android:summary="Show posts in widget"/>
</PreferenceCategory>
Activity class:
public class TestActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
and finally Fragment class:
public class Frag extends PreferenceFragment{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.test);
}
}
Activity starts normally but when I click on a first SwitchPreference, its status remains equal and the other two SwitchPreference change their status!
If I use deprecated method (PreferenceActivity without fragment) it works.
Someone can help me?
Giulio