18

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);
    }
user1026605
  • 1,633
  • 4
  • 22
  • 58

8 Answers8

6

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);
    }
}
user1026605
  • 1,633
  • 4
  • 22
  • 58
  • 3
    This worked for me after a slight modification: The `PreferencesFragment`'s `PreferenceManager` doesn't get created until `onCreate()` is called, so trying to find and navigate to the correct screen in `setActivityIntent()` will fail. Instead, I use `setActivityIntent()` to extract and store the name of the `PreferenceScreen` to use, then later in `onCreate()` I call `openPreferenceScreen()` to navigate to it. – josh2112 Sep 18 '15 at 19:46
  • 2
    I raised a defect, please vote for it: https://code.google.com/p/android/issues/detail?id=207718 – Leos Literak Apr 21 '16 at 19:25
  • 1
    I just tried this and it just opened up another copy of my original PreferenceFragment whenever I tried opening up subscreens. – CiaranC94 Jul 13 '16 at 11:24
6

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.

szym
  • 5,606
  • 28
  • 34
1

Since google sadly didn't fixed it until now, there is actually one much easier solution:

  1. Set your SettingsActivity class to extend from just "Activity".

    public class SettingsActivity extends Activity { ...

  2. 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>

  3. Set your new theme in your Manifest.xml file:

    <activity android:name=".SettingsActivity" android:label="@string/title_activity_settingsactivity" android:theme="@style/CustomThemeSettings" > </activity>

  4. It just works :)

  5. (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.

0

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>
Bruno Carrier
  • 530
  • 5
  • 8
  • As explained in my question, I already have the actionbar displayed in the setting main screen. The problem is that it disappears as soon as I press on a 'PreferenceScreen' preference. I want to keep it and to update the actionbar display with the name of the PreferenceScreen that has been pressed – user1026605 Apr 05 '15 at 07:01
  • @Bruno Carrier, is `PreferenceFragment ` supported for API level < `Honey Comb` – Sai Phani Apr 09 '15 at 20:01
  • It is, but it doesn't work exactly the same way. See this post for a thorough answer: http://stackoverflow.com/questions/10186697/preferenceactivity-android-4-0-and-earlier – Bruno Carrier Apr 09 '15 at 22:27
0

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.

Deepu
  • 598
  • 6
  • 12
  • No I'm not. I'm just using a standard preference.xml res file, with some standard PreferenceScreen pref as explained in my question. I'm not starting another activity from the PreferenceScreen. I'm just embedding other preferences in the PreferenceScreen tags – user1026605 Apr 09 '15 at 16:23
0

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.

Community
  • 1
  • 1
klimat
  • 24,711
  • 7
  • 63
  • 70
0

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
enter image description here
enter image description here

Community
  • 1
  • 1
Sai Phani
  • 259
  • 1
  • 6
  • as explained in my question the problem isn't to display the Actionbar on the preference 'main screen'. What happen when you press on WiFi settings ? Is the actionbar still displayed ? BTW my min sdk is 14 – user1026605 Apr 09 '15 at 21:04
  • no way to make it work using the default ActionBar ? – user1026605 Apr 10 '15 at 09:05
  • I have a lot of settings embedded in a single resource file with multiple levels of PreferenceScreen. Using multiple Activities / resources files for my preferences isn't an option. – user1026605 Apr 11 '15 at 20:58
  • *addPreferencesFromResource* API is deprecated in the way you are using it from Activity. that is from API 11 and you recommend from API 14. – Ewoks Nov 23 '15 at 14:39
0

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.

Eric Kok
  • 2,042
  • 3
  • 22
  • 32