No ActionBar in PreferenceActivity after upgrade to Support Library v21
Solution 1
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.JELLY_BEAN) {
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.JELLY_BEAN) {
// 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
xmlns:android="http://schemas.android.com/apk/res/android"
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 2
Use AppCompatActivity & PreferenceFragment to solve the issue:
AppCompatActivity:
public class SettingsActivity extends AppCompatActivity {
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, new SettingsFragment()).commit();
}}
PreferenceFragment:
public class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.settings_preferences);
}}
Solution 3
I ended in adding the Toolbar myself with this simple code:
// get the root container of the preferences list
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
Toolbar bar = (Toolbar)LayoutInflater.from(this).inflate(R.layout.preferences_toolbar, root, false);
root.addView(bar, 0); // insert at top
bar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
Here is my preferences_toolbar.xml:
<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
app:navigationContentDescription="@string/abc_action_bar_up_description"
android:background="?attr/colorPrimary"
app:navigationIcon="?attr/homeAsUpIndicator"
app:theme="@style/Theme.Toolbar" />
Solution 4
A better solution than "rolling your own" action bar is to use the AppCompatDelegate class, which lets you bring in an actual-factual action bar from the support library. Here is example code to use it, taken from Ľubomír Kučera's answer to this question.
...
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;
}
}
Solution 5
Hi I am not if you still have this issue. But I figure I will post what I did to resolve this and hope it will help someone.
1) First off, you might have noticed that PreferenceActivity extends ListActivity which in turns extends from Activity.
According to the comments on the developers blog (http://android-developers.blogspot.com/2014/10/appcompat-v21-material-design-for-pre.html), to use v21 all your activities must inherit from ActionBarActivity. So there is your issue.
2) The steps I used to resolve are :
a) Make sure that you set the Theme of your PreferenceActivity to inherits one ot the Theme.AppCompat Themes.
b) Make your class PreferenceActivity extends ActionBarActivity.
c) Use the PreferenceFragment as your container for all your preferences.
This should resolve it.
Cheers!
Related videos on Youtube
![rekire](https://i.stack.imgur.com/UPzx8.png?s=256&g=1)
rekire
Check also my gists and projects on GitHub. Voice related Apps: REWE✝ (Google, Amazon) CentralStation CRM (Google (DE), Google (US), Amazon (DE), Amazon (US)) List of my Apps or Apps I worked on: snabble by snabble CentralPlanner by 42he (flutter) gooods by snabble toom by snabble CentralStation CRM by 42he Zykluskalender✝ by NetMoms Hotel Search by HRS REWE Lieferservice, Supermarkt by REWE Digital BILLA Online Shop✝ by REWE Digital CentralStation CRM by 42he ✝ = Reached end of life Open source projects where I am involved: Author of Konversation intent-schema-generation provisioning cli kotlin-multi-platform Contributor of dialog dialogflow alexa kotlin Author of LazyWorker android tool Contributor of Futter flutter You want to hire me? Check my careers profile.
Updated on January 04, 2020Comments
-
rekire over 4 years
After I upgraded to the Support Library v21 my ActionBar in my
PreferenceActivity
is gone.Did I miss some attributes in my theme to activate it again? I had some similar trouble with a black ActionBar.
I also tried to add it a little hackish by adding a
Toolbar
to the root layout, but that did not work as expected.-
Jared Burrows over 9 yearsYou should be using Preference Fragment: developer.android.com/reference/android/preference/…
-
tyczj over 9 years@JaredBurrows you cant use PreferenceFragment pre 3.0 though
-
rekire over 9 yearsI'm using them too. AFIK I need to link to a PreferenceActivity which uses the PreferenceFragments. However as tycyj points out I need them it also for legacy support.
-
Jared Burrows over 9 years@tyczj Yes, there are alternatives: stackoverflow.com/questions/9783368/…
-
tyczj over 9 years@JaredBurrows well yeah with a 3rd party library, there is nothing native for that though
-
rekire over 9 years@JaredBurrows so you mean that
android-support-v4-preferencefragment
supports also the support library v21 I want that matrial like actionbar aka toolbar. -
Jared Burrows over 9 yearsThe toolbar is just a view. You can add it anywhere.
-
rekire over 9 yearsI already tried it the result looked like a normal TextView.
-
android developer about 9 yearsYou can check out the example I've made here: github.com/AndroidDeveloperLB/MaterialPreferenceLibrary
-
rekire about 9 years@Android that library looks nice, I'll possible use it for my next project
-
android developer about 9 years@rekire Thank you. You can also see it in action on my "app manager" app. Can you show the project you'll work on? or the project you wanted to have this?
-
rekire about 9 years@androiddeveloper I'm developing on this app.
-
android developer about 9 years@rekire This looks awesome (on the screenshots). It's for fertility ?
-
rekire about 9 years@androiddeveloper yes it is. If you have more question, please join this chat.
-
android developer about 9 years@rekire Well I'm not a woman and not married, and also the app isn't available for my device for some reason, but could be useful in the future. Does it have an English language in it? If so, I might be able to translate to Hebrew.
-
android developer about 9 yearsChat doesn't work well. The blocking shows as if it's the device's fault and not country. Anyway thanks and good luck !
-
f470071 over 8 yearsPreferences in support.v7 23 are pretty much screwed.
-
Leos Literak about 8 yearsI raised a defect, please vote for it: code.google.com/p/android/issues/detail?id=207718
-
Rk215 Tech over 6 yearsin case this might help to you. stackoverflow.com/a/49532356/8203967
-
Rk215 Tech over 6 yearsi case above solution not work then use my answer . stackoverflow.com/a/49532356/8203967
-
-
vbence over 9 years"Make your class PreferenceActivity extends ActionBarActivity" - but HOW?
-
JJ86 over 9 yearsNice one: i have used the first approach (Gingerbread is not on my target). Thank you!
-
AfrikAndroid over 9 yearsCheck out this link here: code.hootsuite.com/…
-
vbence over 9 yearsThanks, I think this comes as close as possible. The real pain is that PreferenceActivity has built-in navigation through loadHeadersFromResource, with fragments you have to build the navigation yourself.
-
idunnololz over 9 yearsIt appears that the up button is broken on Gingerbread using this example.
-
idunnololz over 9 yearsUpdate: The issue was that the ToolBar was placed underneath the content view due to the order in which they were added. The fix is to simply call
root.addView(bar);
afterroot.addView(content);
-
Tony Morello over 9 yearsThanks, after 3 nights of frustration I finally got it to work... :)
-
David Passmore over 9 years@TonyMorello Glad to be of assistance :)
-
David Vávra over 9 yearsAmazing! I tried to solve this problem for a long time and this is by far the easiest approach. How about wrap it in a library?
-
rekire about 9 yearsThe ActionBarActivity is deprecated.
-
Abdullah about 9 years@rekire its android.support.v7.app.ActionBarActivity
-
rekire about 9 yearsThat one is deprecated since the version 22.1.0
-
andQlimax about 9 yearsWhat about with appcompact 22.1.0 ? It should be easier to theme PreferenceActivity now. Maybe you could update the code? :)
-
Sufian about 9 yearsDropping by to say thanks. Your comment on my question guided me here (I fixed my problem following your answer) and I'm glad you've kept this answer updated. I was getting that NPE on Gingerbread, if by any chance you were curious. ;)
-
rekire about 9 yearsThank you for your great answer, I gave you some days ago the accepted state. I just copied the tinting part, to make sure that it will look great :)
-
David Passmore about 9 yearsThanks for the feedback @rekire I really appreciate it
-
David Passmore about 9 yearsThanks suffian also :)
-
SammyT about 9 yearsJust extend AppCompatActivity instead of ActionBarActivity.
-
Sufian about 9 yearsI recently got a crash report in Dev console. Optimus Pad (l06c) running Android 3.1 crashed and the exception was
Caused by: java.lang.ClassCastException: android.widget.LinearLayout cannot be cast to android.widget.ListView
and the line it pointed to wasLinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent();
. -
David Passmore about 9 years@suffian I will create a virtual machine with 3.1 and test.
-
tony about 9 yearsI tried this approach and it works great, thank you. But I have an issue when using nested Preferences (by defining a
<PreferenceScreen>
in xml: The Actionbar won't show here. Is there any workaround (I'd like to keep using aPreferenceActivity
). -
David Passmore about 9 years@tony UPDATE: Wish I could find a solution, but as of yet there is none...
:(
-
tony about 9 years@David Passmore Thank you anyways for trying! There are a couple methods mentioned here, I guess I have to use another approach. Too bad, your solution is the cleanest in my opinion, but ultimately we should blame Google..
-
David Passmore about 9 years@tony I will continue to investigate the options of using a custom layout as a possible workaround
-
tony about 9 years@DavidPassmore I'm wondering if it's possible to somehow include this: stackoverflow.com/a/26705551/1613083
-
tony about 9 years@DavidPassmore You are a genius! It works like a charm. I had to slightly adapt it to my setup by moving the
onPreferenceTreeClick
to my PreferenceFragment and it does what it should! (My SettingsActivity inherits from Activity rather than SettingsActivity, the same code will result in the toolbar overlaying the text if I don't - but that's due to my setup and not for you to worry about I think). One note: With AppCombat v22.1.0+ you need to changeapp:theme
to android:theme or the toolbar text will be black, see here. -
tony about 9 yearsI have to correct myself. The
android:theme
workaround only fixes the black text color at the toolbar for Android 5.0+. It appears as something changed with the new support library (I'm using 22.2.0). -
David Passmore about 9 years@tony i saw the sam bug, i revertwd to 22.1 to resolve
-
Aaron Blenkush about 9 yearsWorked for me, and much simpler than the above.
-
Avinash R almost 9 yearsa.k.a AppCompatPreferenceActivity from official v7 samples.
-
roundhill almost 9 yearsWe got a crash on Android 4.0.X, It appears that the
gingerbread compatibility
code needs to also be run for that version, so:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
-
southerton over 8 yearsListPreferences stop to work inside PreferenceFragment. They do show up but the radio buttons cannot be selected.
-
Bere over 8 yearswhat about for nested preference screens? This doesn't seem to work for them
-
codelearner over 8 yearsOn my Android 6, the above code displays the list with overlapping the
Toolbar
-
David Passmore over 8 years@codelearner I will investigate as Android 6 has not been tested yet
;)
-
codelearner over 8 years@DavidPassmore after spending few hours on code, I finally found a very simple solution... :D
-
codelearner over 8 years@DavidPassmore I updated above post with my working codes for API > 10... :)
-
David Passmore over 8 years@codelearner I have reviewed you edit.. thanks for the input. But I am deliberately trying to avoid fragments
:)
-
Roel over 8 yearsHow to change the Title of the PerferenceActivity to the selected preferencefragment Title? Because the toolbar is added onPostCreate() it does not excist on all the lifecycle events I tried in the fragments...
-
David Passmore over 8 years@DalvikVM Can you elaborate as my examples do not use fragments... If there is an issue please raise an issue here github.com/davcpas1234/MaterialSettings/issues/new Thanks!
:)
-
Roel over 8 yearsI fixed it by updating the title in a handler.post( new Runnable { ... } ); I included your code in default generated code of Android studio (with fragments).
-
rekire over 8 yearsI guess I put it in onCreate()
-
Karthik about 8 yearsI have followed the first approach given here. But i am getting a transparent toolbar and under that my preference items are displayed. Can anybody provide a solution for this?
-
zys about 8 yearsandroid.R.id.list 's parent is FrameLayout in android N
-
Chris Cirefice over 7 yearsFor simple preference screens, this is by far the easiest solution to implement.
-
Simon over 7 yearsIt has to be that complicated?
-
Krzysztof Wołowski about 7 yearsJust for the record. If you are using fragments you should modify this code a bit. Otherwise you will encounter overlapping problem that was mentioned. First of all you put the code in fragment's
onActivityCreated
method. Then you callgetParent()
twice instead of three times (layouts forPreferenceActivity
andPreferenceFragment
are slightly different). Other changes are minor and pretty obvious - because the code is in a fragment you usegetView().findViewById()
andgetActivity().getLayoutInflater()
. -
Krzysztof Wołowski about 7 years@zys - it isn't. Most probably you were using fragments. Look at my comment above.
-
Ryan Burbidge over 6 years@Bere for nested preference screens I believe you'd have to re-wrap them in another AppCompatActvity and setup the ActionBar in a similar way. You can have a preference act as a button to launch the nested screen by using
<intent android:action="com.foo.MyNestedPreferenceActivity"/>
in your preferences.xml file. Note that this would require you to separate your nested preferences XML content into a new file.