How to add Action Bar from support library into PreferenceActivity?
Solution 1
EDIT: In appcompat-v7 22.1.0 Google added the AppCompatDelegate abstract class as a delegate you can use to extend AppCompat's support to any activity.
Use it like this:
...
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
...
public class SettingsActivity extends PreferenceActivity {
private AppCompatDelegate mDelegate;
@Override
protected void onCreate(Bundle savedInstanceState) {
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getDelegate().onPostCreate(savedInstanceState);
}
public ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
public void setSupportActionBar(@Nullable Toolbar toolbar) {
getDelegate().setSupportActionBar(toolbar);
}
@Override
public MenuInflater getMenuInflater() {
return getDelegate().getMenuInflater();
}
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
@Override
public void setContentView(View view) {
getDelegate().setContentView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().setContentView(view, params);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().addContentView(view, params);
}
@Override
protected void onPostResume() {
super.onPostResume();
getDelegate().onPostResume();
}
@Override
protected void onTitleChanged(CharSequence title, int color) {
super.onTitleChanged(title, color);
getDelegate().setTitle(title);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getDelegate().onConfigurationChanged(newConfig);
}
@Override
protected void onStop() {
super.onStop();
getDelegate().onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
getDelegate().onDestroy();
}
public void invalidateOptionsMenu() {
getDelegate().invalidateOptionsMenu();
}
private AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, null);
}
return mDelegate;
}
}
No more hacking. Code taken from AppCompatPreferenceActivity.java.
Solution 2
There is currently no way to achieve with AppCompat. I've opened a bug internally.
Solution 3
I have managed to create a workaround similar to what the Google Play Store uses. Link to Original Answer
Please find the GitHub Repo: Here
Very Similar to your own code but added xml to allow for set title:
Continuing to use PreferenceActivity
:
settings_toolbar.xml :
<?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/action_settings"
/>
SettingsActivity.java :
public class SettingsActivity extends PreferenceActivity {
@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();
}
});
}
}
Result :
UPDATE (Gingerbread Compatibility) :
As pointed out here, Gingerbread Devices are returning NullPointerException on this line:
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
FIX:
SettingsActivity.java :
public class SettingsActivity extends PreferenceActivity {
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
Toolbar bar;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
LinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent();
bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
root.addView(bar, 0); // insert at top
} else {
ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
ListView content = (ListView) root.getChildAt(0);
root.removeAllViews();
bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
int height;
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
}else{
height = bar.getHeight();
}
content.setPadding(0, height, 0, 0);
root.addView(content);
root.addView(bar);
}
bar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}
Any issues with the above let me know!
UPDATE 2: TINTING WORKAROUND
As pointed out in many dev notes PreferenceActivity
does not support tinting of elements, however by utilising a few internal classes you CAN achieve this. That is until these classes are removed. (Works using appCompat support-v7 v21.0.3).
Add the following imports:
import android.support.v7.internal.widget.TintCheckBox;
import android.support.v7.internal.widget.TintCheckedTextView;
import android.support.v7.internal.widget.TintEditText;
import android.support.v7.internal.widget.TintRadioButton;
import android.support.v7.internal.widget.TintSpinner;
Then override the onCreateView
method:
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
// Allow super to try and create a view first
final View result = super.onCreateView(name, context, attrs);
if (result != null) {
return result;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// If we're running pre-L, we need to 'inject' our tint aware Views in place of the
// standard framework versions
switch (name) {
case "EditText":
return new TintEditText(this, attrs);
case "Spinner":
return new TintSpinner(this, attrs);
case "CheckBox":
return new TintCheckBox(this, attrs);
case "RadioButton":
return new TintRadioButton(this, attrs);
case "CheckedTextView":
return new TintCheckedTextView(this, attrs);
}
}
return null;
}
Result:
AppCompat 22.1
AppCompat 22.1 introduced new tinted elements, meaning that there is no longer a need to utilise the internal classes to achieve the same effect as the last update. Instead follow this (still overriding onCreateView
):
@Override
public View onCreateView(String name, Context context, AttributeSet attrs) {
// Allow super to try and create a view first
final View result = super.onCreateView(name, context, attrs);
if (result != null) {
return result;
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// If we're running pre-L, we need to 'inject' our tint aware Views in place of the
// standard framework versions
switch (name) {
case "EditText":
return new AppCompatEditText(this, attrs);
case "Spinner":
return new AppCompatSpinner(this, attrs);
case "CheckBox":
return new AppCompatCheckBox(this, attrs);
case "RadioButton":
return new AppCompatRadioButton(this, attrs);
case "CheckedTextView":
return new AppCompatCheckedTextView(this, attrs);
}
}
return null;
}
NESTED PREFERENCE SCREENS
A lot of people are experiencing issues with including the Toolbar in nested <PreferenceScreen />
s however, I have found a solution!! - After a lot of trial and error!
Add the following to your SettingsActivity
:
@SuppressWarnings("deprecation")
@Override
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
super.onPreferenceTreeClick(preferenceScreen, preference);
// If the user has clicked on a preference screen, set up the screen
if (preference instanceof PreferenceScreen) {
setUpNestedScreen((PreferenceScreen) preference);
}
return false;
}
public void setUpNestedScreen(PreferenceScreen preferenceScreen) {
final Dialog dialog = preferenceScreen.getDialog();
Toolbar bar;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
LinearLayout root = (LinearLayout) dialog.findViewById(android.R.id.list).getParent();
bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
root.addView(bar, 0); // insert at top
} else {
ViewGroup root = (ViewGroup) dialog.findViewById(android.R.id.content);
ListView content = (ListView) root.getChildAt(0);
root.removeAllViews();
bar = (Toolbar) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
int height;
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(R.attr.actionBarSize, tv, true)) {
height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
}else{
height = bar.getHeight();
}
content.setPadding(0, height, 0, 0);
root.addView(content);
root.addView(bar);
}
bar.setTitle(preferenceScreen.getTitle());
bar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
}
The reason that PreferenceScreen
's are such a pain is because they are based as a wrapper dialog, so we need to capture the dialog layout to add the toolbar to it.
Toolbar Shadow
By design importing the Toolbar
does not allow for elevation and shadowing in pre-v21 devices, so if you would like to have elevation on your Toolbar
you need to wrap it in a AppBarLayout
:
`settings_toolbar.xml :
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
.../>
</android.support.design.widget.AppBarLayout>
Not forgetting to add the add the Design Support library as a dependency in build.gradle
file:
compile 'com.android.support:support-v4:22.2.0'
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:design:22.2.0'
Android 6.0
I have investigated the reported overlapping issue and I cannot reproduce the issue.
The full code in use as above produces the following:
If I am missing something please let me know via this repo and I will investigate.
Solution 4
Found a PreferenceFragment implementation based on support-v4 Fragment:
https://github.com/kolavar/android-support-v4-preferencefragment
Edit: I just tested it and its working great!
Solution 5
Integrating PreferenceActivity
with ABC is not possible, at least for me. I tried the two possibilities I could find but none worked:
Option 1:
ActionBarPreferenceActivity
extends PreferenceActivity
. When you do this you get restricted by ActionBarActivityDelegate.createDelegate(ActionBarActivity activity)
. Also you need to implement ActionBar.Callbacks
which is not accessible
Option 2:
ActionBarPreferenceActivity
extends ActionBarActivity
. This approach requires rewriting a whole new PreferenceActivity
, PreferenceManager
and may be PreferenceFragment
which means you need access to hidden classes like com.android.internal.util.XmlUtils
The solution to this can only come from Google devs implementing an ActionBarWrapper
that can be added to any activity.
If you really need a preference activity, my advice for now is ActionBarSherlock
.
However, I managed to implement it here.
Comments
-
Roman over 4 years
Action Bar compatibility has been added into support library, revision 18. It now has
ActionBarActivity
class for creating activities with Action Bar on older versions of Android.Is there any way to add Action Bar from support library into
PreferenceActivity
?Previously I used ActionBarSherlock and it has
SherlockPreferenceActivity
. -
Pablo almost 11 yearsThanks @Chris. Will be nice to have this feature.
-
imbryk almost 11 years@Maarten the link doesn't work with current support library, there would be probably much more work involved to make your own.
-
imbryk almost 11 years@Chris, do you have any idea when we could expect
PreferenceActivity
to be added toActionBarCompat
? -
davidcesarino over 10 years@nsL I still remember that being a problem, and how ABS solved it neatly. But I wouldn't get my hopes up, as some important issues were declined back then. Disappointment.
-
Roman over 10 yearsI think it is very unlikely that this feature is going to be implemented. Number of devices running older versions of Android (< 4.0) is less than 30% at this point, and this number is going down every month.
-
Martin Rajniak over 10 years@Chris can you provide a bug id, so I can follow progress on that thing?
-
Someone Somewhere about 10 yearsThis was entered almost a year ago and no resolution??
-
Broak about 10 yearsw.t.f still waiting??
-
TeeTracker almost 10 yearsPer4.0 brings crash. I've forked it and improved . gist.github.com/0e9fe2119b901921b160
-
Gavriel almost 10 years@Konstantin, can you add some code example? I downloaded it, changed the import from android.preference.PreferenceFragment to android.support.v4.preference.PreferenceFragment, and I see it added some headers in the middle of the screen, but not the ActionBar on the top
-
Ostkontentitan almost 10 yearsIt doesnt add the actionbar thats the activity's job. Unfortunally i have no samplecode at hand but it should work similar to: developer.android.com/reference/android/preference/…
-
Sufian almost 10 yearsHere's a simpler solution, one which doesn't uses any workarounds stackoverflow.com/a/25603448/1276636
-
Sufian almost 10 yearsCheck my solution. It doesn't use any workaround stackoverflow.com/a/25603448/1276636
-
Sufian almost 10 yearsIt wasn't probably possible at that time but now I got it to work without any 'workarounds'. See my answer stackoverflow.com/a/25603448/1276636
-
Frozen Crayon over 9 yearsHow does this help get the action bar in Preference activity?
-
Sufian over 9 years@ArjunU. easy solution - try it and figure it out.
-
Sufian over 9 years@ArjunU. The problem was that the
PreferencesActivity
wouldn't have any way to put items toActionBar
, especially the back button. My answer is a good fix for that. -
Yohanes Khosiawan 许先汉 over 9 yearsplease put your essential code snippets in your answer. to avoid potential broken link issue.
-
Gavriel over 9 yearsThis answer is outdated, today there is a solution, see @Ľubomír Kučera 's answer
-
ahmedre over 9 yearsthanks, this works for me - i'd change new LinearLayout in the first inflate call to:
(ViewGroup) getWindow().getDecorView().getRootView()
-
3c71 over 9 yearsWorks for me too and helped me recover the settings title which got lost when upgrading to release 21.
-
Maxwell Weru over 9 yearsIt does not solve anything! Hope you understand the issue at hand
-
Sufian over 9 yearsThanks for the downvote. Any explanation how I could improve my answer or what was wrong?
-
Sufian over 9 years@ArjunU. I have updated my answer. Kindly remove your down vote.
-
hfann over 9 yearsThis works for me to bring the ActionBar back into preference activity after upgrading to release 21. I use
mActionBar.setLogo(R.drawable.icon);
to show my app icon instead of the app name. -
petrsyn over 9 yearsThis doesn't work with nested <PreferenceScreen> elements. Nested preference screens have no Toolbar.
-
Ľubomír Kučera over 9 years
-
petrsyn over 9 years@Ľubomír Kučera Thanks for answering. Your solution creates nested PreferenceScreen manually in the onCreate() method of the PreferenceActivity child class. My comment was about using <PreferenceScreen> nested under another <PreferenceScreen> in the prefs.xml. This stopped working in AppCompat v21. This used to work in older versions of AppCompat lib and also in the ActionBarSherlock lib. Anyway, thanks for responding.
-
swooby over 9 yearsIf I use <style name="Theme.Application" parent="@style/Theme.AppCompat.Light.DarkActionBar" /> then my Settings text is black on dark gray. What is the simplest way to fix this?
-
Ľubomír Kučera over 9 years@swooby You might suffer from this bug.
-
David Passmore about 9 years@Sufian Thanks for linking to my answer, glad it's helping everyone
:)
-
Tobi about 9 yearsGreat! Works like a charm :)
-
Tobi about 9 yearsWhy does this not work for the nested PreferenceScreen, but only for the main PreferenceScreen?
-
David Passmore about 9 years@Tobi I have updated my answer to solve the nested issue, and explained why.
:)
-
David Passmore about 9 years@petrsyn I have solved the nesting problem here stackoverflow.com/a/27455363/566127
-
Martin Pfeffer about 9 yearsThanks! Your advice "AppCompat 22.1" did the trick for me :) Very useful!
-
Jemshit Iskenderov almost 9 years@DavidPassmore why i cant see shadow on api 10 when i wrap toolbar with appbarlayout ?
-
Jemshit Iskenderov almost 9 years@DavidPassmore why wrap it inside AppBarLayout for shadow if it works v21+ ? why not Just use "elevation"?
-
Someone Somewhere almost 9 yearswhy is
PreferenceActivity
such a pain in the ass to use ??? it's supposed to save time. I might as well make a regular activity and manually lay out all the settings in a linear layout myself. Fuuuuck ! -
southerton over 8 yearsAny idea why ListPreferences radio button stop working after applying this approach? (android 4.0.3).
-
southerton over 8 yearsWhen I commented out the //getDelegate().installViewFactory(); ListPreference started to work. Strange behaviour.
-
f470071 over 8 years@ĽubomírKučera This gives me almost completely 2.x look. Ok, checkboxes are material style but everything else is old. What is the point of this then?
-
powder366 over 8 yearsOne of the thousands of bugs;-)
-
S.P. almost 8 yearsIn Android API 24, in NESTED PREFERENCE SCREENS, in this line ( LinearLayout root = (LinearLayout) dialog.findViewById(android.R.id.list).getParent(); ) I'm getting this error: java.lang.ClassCastException: android.widget.FrameLayout cannot be cast to android.widget.LinearLayout. Is any solution for this???
-
David Passmore almost 8 yearsHi @D.O. can you raise this on the GitHub so I can track the errors :)
-
S.P. almost 8 yearsHi @David, you mean in this github link, isn't it? github.com/davcpas1234/MaterialSettings
-
S.P. almost 8 yearsDone! raise in GitHub
-
Phan Sinh almost 8 years@petrsyn: I have problem like you. How can i fix it? I tried in the way of Ľubomír Kučerabut, but it did not work.
-
Martin Erlic over 7 yearsOkay so where do I put the toolbar in the xml? I'm getting a NullPointerException