I'm creating an app that has a settings activity. The settings activity extends from AppCompatPreferenceActivity which I have imported myself. I want a toolbar at the top of the activity for the user to go back.
I have tried to change the setContentView to an activity with a toolbar but it comes up with the error No view found for id 0x10203a1 (android:id/prefs) for fragment GeneralPreferenceFragment{1fac983 #0 id=0x10203a1}
Here is my current code for settings activity (most of the code is simply from the basic settings activity you get as a template in Android Studio.)
public class PreferencesActivity extends AppCompatPreferenceActivity implements PreferenceFragment.OnPreferenceStartFragmentCallback{
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
ListPreference listPreference = (ListPreference) preference;
int index = listPreference.findIndexOfValue(stringValue);
// Set the summary to reflect the new value.
preference.setSummary(
index >= 0
? listPreference.getEntries()[index]
: null);
} else if (preference instanceof RingtonePreference) {
// For ringtone preferences, look up the correct display value
// using RingtoneManager.
if (TextUtils.isEmpty(stringValue)) {
// Empty values correspond to 'silent' (no ringtone).
preference.setSummary(R.string.pref_ringtone_silent);
} else {
Ringtone ringtone = RingtoneManager.getRingtone(
preference.getContext(), Uri.parse(stringValue));
if (ringtone == null) {
// Clear the summary if there was a lookup error.
preference.setSummary(null);
} else {
// Set the summary to reflect the new ringtone display
// name.
String name = ringtone.getTitle(preference.getContext());
preference.setSummary(name);
}
}
} else {
// For all other preferences, set the summary to the value's
// simple string representation.
preference.setSummary(stringValue);
}
return true;
}
};
/**
* Helper method to determine if the device has an extra-large screen. For
* example, 10" tablets are extra-large.
*/
private static boolean isXLargeTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
}
/**
* Binds a preference's summary to its value. More specifically, when the
* preference's value is changed, its summary (line of text below the
* preference title) is updated to reflect the value. The summary is also
* immediately updated upon calling this method. The exact display format is
* dependent on the type of preference.
*
* #see #sBindPreferenceSummaryToValueListener
*/
private static void bindPreferenceSummaryToValue(Preference preference) {
// Set the listener to watch for value changes.
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
// Trigger the listener immediately with the preference's
// current value.
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getString(preference.getKey(), ""));
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupActionBar();
}
private void setupActionBar() {
ViewGroup rootView = (ViewGroup)findViewById(R.id.action_bar_root); //id from appcompat
if (rootView != null) {
Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
toolbar.setTitleTextColor(Color.WHITE);
setSupportActionBar(toolbar);
}
android.support.v7.app.ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
// Show the Up button in the action bar.
Log.v(getApplication().getClass().getSimpleName(), "The action bar isn't null");
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setTitle("idk");
// actionBar.setTitleColor(R.color.white);
actionBar.setHomeAsUpIndicator(R.drawable.ic_arrow);
}
}
/**
* {#inheritDoc}
*/
#Override
public boolean onIsMultiPane() {
return isXLargeTablet(this);
}
/**
* {#inheritDoc}
*/
#Override
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onBuildHeaders(List<Header> target) {
loadHeadersFromResource(R.xml.pref_headers, target);
}
/**
* This method stops fragment injection in malicious applications.
* Make sure to deny any unknown fragments here.
*/
protected boolean isValidFragment(String fragmentName) {
return GeneralPreferenceFragment.class.getName().equals(fragmentName)
|| DataSyncPreferenceFragment.class.getName().equals(fragmentName)
|| NotificationPreferenceFragment.class.getName().equals(fragmentName);
}
/**
* This fragment shows general preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class GeneralPreferenceFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_general);
setHasOptionsMenu(true);
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
// to their values. When their values change, their summaries are
// updated to reflect the new value, per the Android Design
// guidelines.
bindPreferenceSummaryToValue(findPreference("example_text"));
bindPreferenceSummaryToValue(findPreference("example_list"));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
startActivity(new Intent(getActivity(), SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
/**
* This fragment shows notification preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class NotificationPreferenceFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_notification);
setHasOptionsMenu(true);
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
// to their values. When their values change, their summaries are
// updated to reflect the new value, per the Android Design
// guidelines.
bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
startActivity(new Intent(getActivity(), SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
/**
* This fragment shows data and sync preferences only. It is used when the
* activity is showing a two-pane settings UI.
*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class DataSyncPreferenceFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_data_sync);
setHasOptionsMenu(true);
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
// to their values. When their values change, their summaries are
// updated to reflect the new value, per the Android Design
// guidelines.
bindPreferenceSummaryToValue(findPreference("sync_frequency"));
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
startActivity(new Intent(getActivity(), SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
}
here is code of my activity_settings.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/content_frame"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
<LinearLayout
android:id="#+id/root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
The root view is where my content will go and the there on top.
after a lot of experimenting and trying things out. I think that I have worked out how to create a toolbar for a settings page. It was actually much easier than expected and the main way I did it was through different themes. Using this method, saves you the hassle of getting errors such as No view found for android:id/prefs or No list view found (android.R.id.list)
Declaring the themes in styles.xml
You need to have one theme which is your regular app theme. It should already be created and customised by you, and one other theme with the Toolbar added to it.
In my case I wanted to use my own custom toolbar for the other activities but for the SettingsActivity, I wanted a basic toolbar.
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
<!-- Customised App Theme with the Toolbar-->
<style name="AppTheme_Toolbar" parent="Theme.AppCompat.Light.DarkActionBar" xmlns:tools="http://schemas.android.com/tools">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
</resources>
Suffice to say, the only difference between the two themes were that the one without the Toolbar already there was Theme.AppCompat.Light.NoActionBar and the other was Theme.AppCompat.Light.DarkActionBar
Change the themes of your activity
There are two ways to do this. First, is to go to your SettingsActivity.class and in the onCreate method, add this line of code
setTheme(R.style.AppTheme_Toolbar);
Otherwise, go to your AndroidManifest.xml:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".SettingsActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme_Toolbar">
</activity>
Here my app's theme itself is #style/AppTheme. But my settings activity page's theme is set to #style/AppTheme_Toolbar
Hope this helps!
I'm using SettingsActivity as preference activity for my application like so:
public class SettingsActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(getString(R.string.dark_theme_key), false)) {
setTheme(R.style.AppThemeDark);
} else {
setTheme(R.style.AppThemeLight);
}
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment())
.commit();
}
}
public static class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_preferences);
...
}
}
}
If I use getActivity().recreate(); (I have a SwitchPreference for dark and light theme. Dark theme is Theme.AppCompat and light is same but with .Light. If I change the theme I use recreate.) or change orientation of the screen (Resulting in onCreate of course), I get a weird problem when I'm on my light theme.
The problem is as follows -after a recreate, the text content of the EditTextPreference's turn white instead of black.
If I simply ignore savedInstanceState == null and just create a new fragment every time on SettingsActivity.onCreate, I lose the "screen state" (for example if I was scrolled all the way down before, after recreate Im scrolled back to the top. If I had a EditTextPreference open before, after recreate its closed).
I'm not sure how to restore a fragment screen state in case of recreation, or why my problem even occurs. Would appreciate help.
I'm using editTextPreference to implement a Setting Activity. My problem is that when I click in an EditTextPreference it displays a dialog with the preference title but not appear an edittext to write.
I have searched for this problem but It seems it hasn't happened to anyone. Any help would be appreciated
I have this code :
#EActivity
#OptionsMenu(R.menu.settings_activity_menu)
public class ActivitySettings extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new FragSettings_())
.commit();
}
}
#EFragment
public class FragSettings extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener
{
#App
MyApplication app;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
#AfterViews
void init()
{
EditTextPreference url =(EditTextPreference)findPreference(app.getResource(R.string.CONFIG_URL_SERVER));
EditTextPreference user =(EditTextPreference)findPreference(app.getResource(R.string.CONFIG_USER_);
if(url!=null)
url.setSummary(app.getSettings().getSharedPreferences().getString(app.getResource(R.string.CONFIG_URL_SERVER), ""));
if(user!=null)
user.setSummary(app.getSettings().getSharedPreferences().getString(app.getResource(R.string.CONFIG_USER_), ""));
}
#Override
public void onResume() {
super.onResume();
// Register as listener to validate settings values
app.getSettings().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
#Override
public void onPause() {
super.onPause();
// Remove listener
app.getSettings().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
#Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
EditTextPreference connectionPref = (EditTextPreference) findPreference(key);
// Set summary to be the user-description for the selected value
connectionPref.setSummary(app.getSettings().getSharedPreferences().getString(key, ""));
}
}
My preferences.xml, in these editTextPreference is where I have the problem, the input doesn't appear :
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/settings_server">
<EditTextPreference
android:key="#string/CONFIG_USER_"
android:title="#string/settings_user"/>
<EditTextPreference
android:inputType="textPassword"
android:key="#string/CONFIG_PASS_"
android:title="#string/settings_password"/>
<EditTextPreference
android:key="#string/CONFIG_URL_SERVER"
android:title="#string/settings_url"/>
</PreferenceCategory>
<Preference
android:key="#string/AREA"
android:title="#string/settings_area"/>
</PreferenceScreen>
What I really not understand is Your Parameters, but let me suspect You are giving the correct references with these to the Preferences. But usually, You have to initialize them with the correct Object:
Preference url =findPreference(Parameters.CONFIG_URL_SERVER);
Preference user =findPreference(Parameters.CONFIG_USER_);
must be
EditTextPreference url =(EditTextPreference)findPreference(Parameters.CONFIG_URL_SERVER);
EditTextPreference user =(EditTextPreference)findPreference(Parameters.CONFIG_USER_);
And it´s better to set the keys inside Your xml layout as a reference to a string in strings.xml, for example:
<EditTextPreference
android:key="#string/user_pref_key"
android:title="#string/settings_user"/>
EDIT
Be sure to use the correct context. For this, You have to wait until the Activity is attached by Override onAttachActivity. Make a global Context:
private Context mContext;
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
mContext=activity;
}
Then You can refer it in Your PreferenceFragment.
EditTextPreference url =(EditTextPreference)findPreference(mContext.getResources().getString(R.string.user_pref_key));
At the end I found what was happening. I wanted to use the same text style that I was using in all my application, so in ManifestFIle.xml I had this:
<activity
android:name=".mobile.settings.ActivitySettings_"
android:theme="#style/PreferenceActivity"
android:label="#string/title_activity_settings"/>
Which refers to this style:
<style name="PreferenceActivity" parent="AppTheme">
<item name="android:editTextPreferenceStyle">#style/CodeFont</item>
<item name="android:preferenceCategoryStyle">#style/CodeFont</item>
</style>
<style name="CodeFont" parent="#android:style/TextAppearance.Medium">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">#dimen/text_size_medium</item>
</style>
It seems that if I use editTextPreferenceStyle it makes disappear the box input. This is my solution:
<activity
android:name=".mobile.settings.ActivitySettings_"
android:theme="#style/AppTheme"
android:label="#string/title_activity_settings"/>
And to custom the edit text I use this http://secutyhf.org/wordpress/zeegers/2014/12/16/android-edittextpreference-style/
<style name="AppTheme" parent="android:Theme.Holo">
<item name="android:editTextStyle">#style/EdittEXTsTYLE</item>
</style>
<style name="EdittEXTsTYLE" parent="android:Widget.EditText">
</style>
I am trying to add theme support to app. It is working, but I want user to choose between themes. The problem is, I cannot do that I tried so many things. I am using ListPreference to define a list of arrays for user to choose. I cannot link those listpreference entry values with util.
If I edit "0" in Util with any number, themes work but it doesnt work when I change those entry values (from list in phone)IDK why.
Below is the code
Settings.java
public class Settings extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
Util.setAppTheme(this);
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
FragmentManager mFragmentManager = getFragmentManager();
FragmentTransaction mFragmentTransaction = mFragmentManager
.beginTransaction();
PrefsFragment mPrefsFragment = new PrefsFragment();
mFragmentTransaction.replace(android.R.id.content, mPrefsFragment);
mFragmentTransaction.commit();
}
public static class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
}
}
Util.java
public class Util extends Activity {
public static void setAppTheme(Activity a) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(a);
int mTheme = Integer.parseInt(sp.getString("theme", "0"));
if(mTheme==0)
{
a.setTheme(R.style.Dark);
}
if(mTheme==1)
{
a.setTheme(R.style.Light);
}
if(mTheme==2)
{
a.setTheme(R.style.LimeLight);
}
if(mTheme==3)
{
a.setTheme(R.style.MojoLight);
}
if(mTheme==4)
{
a.setTheme(R.style.SanMarinoLight);
}
if(mTheme==5)
{
a.setTheme(R.style.LimeDark);
}
if(mTheme==6)
{
a.setTheme(R.style.MojoDark);
}
if(mTheme==7)
{
a.setTheme(R.style.SanMarinoDark);
}
}
}
preferences.xml
<PreferenceCategory
android:title="#string/preference_category2_title">
<ListPreference android:key="list2_preference"
android:title="#string/list2_title"
android:summary="#string/list2_summary"
android:entries="#array/list2_preferences"
android:entryValues="#array/list2_preferences_values"
android:dialogTitle="#string/list2_dialog_title"/>
<SwitchPreference android:key="switch1_preference"
android:title="#string/switch1_title"
android:switchTextOff="#string/switch1_textoff"
android:switchTextOn="#string/switch1_texton"
/>
</PreferenceCategory>
styles.xml
<!-- Dark -->
<style name="Dark" parent="android:Theme.Holo">
<item name="android:windowBackground">#drawable/activity_background_dark</item>
</style>
<!-- Light -->
<style name="Light" parent="android:Theme.Holo.Light">
<item name="android:actionBarStyle">#style/ActionBar_Light</item>
<item name="android:windowBackground">#drawable/activity_background_light</item>
</style>
arrays.xml
<resources>
<string-array name="list2_preferences">
<item>Dark</item>
<item>Light</item>
<item>Light Lime</item>
<item>Light Mojo</item>
<item>Light San Marino</item>
<item>Dark Lime</item>
<item>Dark Mojo</item>
<item>Dark San Marino</item>
</string-array>
<string-array name="list2_preferences_values">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
</string-array>
I will be thankful if anyone can help me out?
What is your on code for the onItemSelected method of your list?
Also you can't change the theme of an activity after you've initialized it's layout.
In my current application I also allow the user to change the theme. To achieve this I simply start the activity again passing an argument (the selected theme) and finish the current one.
In the activity onCreate() I then check the argument. If it's -1 (activity first time start) I get the default theme.
private static final String INTENT_EXTRA = "theme";
private boolean onCreate;
#Override
protected void onCreate(Bundle savedInstanceState) {
onCreate = true;
themeId = getIntent().getIntExtra(INTENT_EXTRA, -1);
if (themeId == -1) {
themeId = SharedPreferencesManagment
.getApplicationThemeResourceId(SharedPreferencesManagment
.getIntApplicationTheme(this));
} else {
themeId = SharedPreferencesManagment
.getApplicationThemeResourceId(themeId);
}
setTheme(themeId);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_options_theme);
initSpinner();
}
In the list's onItemSelected() method I call something like:
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (!onCreate) {
Intent intent = new Intent(this, OptionsThemeActivity.class);
intent.putExtra(INTENT_EXTRA, getSelectedTheme());
startActivity(intent);
finish();
}
onCreate = false;
}
Also note the onCreate-boolean as the onItemSelected()-method is also called when the activity inits the spinner.
(Was the simpliest workaround to prevent a infinte loop)
ALSO:
Why is Util a Activity? If the code you've posted is everything you do with Util you don't need to extend Activity as you pass the context.
I have an activity which extends PreferenceActivity.
I'm loading preferences from the xml file.
But in some cases i need completely hide one of the preferences from the screen based on my app state. There is a setEnabled method, but it's not exactly what i want. I want to remove that preference from the screen completely.
Is it possible ?
If your Preference is within a PreferenceCategory, you have to do this:
XML:
<PreferenceCategory
android:key="category_foo"
android:title="foo">
<CheckBoxPreference
android:key="checkPref" />
Java:
CheckBoxPreference mCheckBoxPref = (CheckBoxPreference) findPreference("checkPref");
PreferenceCategory mCategory = (PreferenceCategory) findPreference("category_foo");
mCategory.removePreference(mCheckBoxPref);
Yes, if you have a reference to both the Preference, and its parent (a PreferenceCategory, or PreferenceScreen)
myPreferenceScreen.removePreference(myPreference);
In the case where the Preference is a direct child of the preference screen, here is some stand-alone code:
PreferenceScreen screen = getPreferenceScreen();
Preference pref = getPreferenceManager().findPreference("mypreference");
screen.removePreference(pref);
If you are using PreferenceFragmentCompat you can set the visiblity in xml.
The preferences in your xml will be converted to AppCompat versions automatically. You can then use the 'app:isPreferenceVisible' attribute in your xml
preferences.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<CheckBoxPreference
android:defaultValue="false"
android:key="show.navigation"
android:title="Show navigation"
app:isPreferenceVisible="false" />
...
The attribute is documented at https://developer.android.com/guide/topics/ui/settings/components-and-attributes
Adding PreferenceFragmentCompat is documented at https://developer.android.com/guide/topics/ui/settings/#inflate_the_hierarchy
Example:
public class MySettingsActivity extends AppCompatActivity {
public static class MySettingsFragment extends PreferenceFragmentCompat {
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.settings_container, new MySettingsFragment())
.commit();
}
}
If you want something that will dynamically change the prefs for example on a SwitchPreference, I have found the best way is to put all my sub options into two category containers. Initially you'll have everything shown, then you just remove the bits you don't want. The clever bit, is you just trigger recreate when something changes and then you don't have to manually create anything or worry about putting things back in in the correct order.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
PreferenceCategory prefCatOne= (PreferenceCategory)findPreference("prefCatOne");
PreferenceCategory prefCatTwo= (PreferenceCategory)findPreference("prefCatTwo");
SwitchPreference mySwitchPref= (SwitchPreference)findPreference("mySwitchPref");
PreferenceScreen screen = getPreferenceScreen();
if (mySwitchPref.isChecked()) {
screen.removePreference(prefCatOne);
} else {
screen.removePreference(prefCatTwo);
}
}
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key.equals("mySwitchPref")) {
this.recreate();
}
}
The only downside that I can see with this, is there is a flash as the screen is recreated from scratch.
In your XML file:
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:key="preferenceScreen">
<PreferenceCategory
android:key="personalisation"
android:title="your title here">
<ThemedPreference
android:key="animation" />
</PreferenceScreen>
In your code:
PreferenceScreen pPreferenceScreen = (PreferenceScreen) findPreference("preferenceScreen");
PreferenceCategory pCategory = (PreferenceCategory) findPreference("personalisation");
ThemedPreference pThemePref = (ThemedPreference) findPreference("animation");
pPreferenceScreen.removePreference(pCategory); //remove category
pCategory.removePreference(pThemePref); // remove preference
I recommend using v7 preference, it has setVisible() method. But I have not tried it yet. Accordingly you have to use PreferenceFragment instead of PreferenceActivity.
https://developer.android.google.cn/reference/android/support/v7/preference/Preference.html#setVisible(boolean)
In the XML file you can make a hidden preference by leaving the title and summary tags empty.
<EditTextPreference
android:defaultValue="toddlerCam"
android:key="save_photo_dir"
/>
Since Android API 26 getParent() method is available: https://developer.android.com/reference/android/preference/Preference.html#getParent()
Though you can do the following:
preference.getParent().removePreference(preference);
Here's a generic way to do this that works regardless of whether the preference is under a PreferenceCategory or PreferenceScreen.
private void removePreference(Preference preference) {
PreferenceGroup parent = getParent(getPreferenceScreen(), preference);
if (parent == null)
throw new RuntimeException("Couldn't find preference");
parent.removePreference(preference);
}
private PreferenceGroup getParent(PreferenceGroup groupToSearchIn, Preference preference) {
for (int i = 0; i < groupToSearchIn.getPreferenceCount(); ++i) {
Preference child = groupToSearchIn.getPreference(i);
if (child == preference)
return groupToSearchIn;
if (child instanceof PreferenceGroup) {
PreferenceGroup childGroup = (PreferenceGroup)child;
PreferenceGroup result = getParent(childGroup, preference);
if (result != null)
return result;
}
}
return null;
}
There is a simple workaround:
//In your Activity code after finding the preference to hide:
if(pref!=null) {
pref.setEnabled(false);
pref.setSelectable(false);
//Following line will replace the layout of your preference by an empty one
pref.setLayoutResource(R.layout.preference_hidden);
}
And create a preference_hidden layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="0dp"/>
Wherever is your Preference to hide (in a PreferenceGroup or at root) it will work!
You can do this in 2 ways:
1.If you use support library, you can build a map of the tree of preferences and their parents, and then remove a preference by using its parent. Here's a function to generate such a map:
public static Map<Preference, PreferenceGroup> buildPreferenceParentTree(#NonNull final PreferenceScreen preferenceScreen) {
final Map<Preference, PreferenceGroup> result = new HashMap<>();
final Stack<PreferenceGroup> curParents = new Stack<>();
curParents.add(preferenceScreen);
while (!curParents.isEmpty()) {
final PreferenceGroup parent = curParents.pop();
final int childCount = parent.getPreferenceCount();
for (int i = 0; i < childCount; ++i) {
final Preference child = parent.getPreference(i);
result.put(child, parent);
if (child instanceof PreferenceGroup)
curParents.push((PreferenceGroup) child);
}
}
return result;
}
If you use the new android-x preference API, you can just set the visibility, by using setVisible function on it.
If you want to evaluate, and based on that mask, an alternative may be
SwitchPreference autenticacionUsuario =
(SwitchPreference) findPreference("key_autenticacion_usuario");
final EditTextPreference Username =
(EditTextPreference) findPreference("key_username_mqtt");
final EditTextPreference Password =
(EditTextPreference) findPreference("key_password_mqtt");
if (!autenticacionUsuario.isChecked()) {
PreferenceCategory preferenceCategory =
(PreferenceCategory) findPreference("category_mqtt");
preferenceCategory.removePreference(Username);
preferenceCategory.removePreference(Password);
}
All this must be within
public static class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
If you're doing what I think you're trying to do (because I'm trying to do it now) it might be better to enable/disable the preference. Because removing it takes it out of the preference screen and you might not be able to add it back where you want it if you made the screen programmatically.
pref.setEnabled(false);
pref.setEnabled(true);
although this might be deprecated. It works for the use case that I'm going through right now.
If all you need is not to show the preference i.e. hide the preference then do the following
findPreference<Preference>("keyName").isVisible = false
code is in kotlin
Note : This is AndroidX preferences (don't know if same with hold with earlier Preference)
Instead of doing this in onCreate in the settings activity:
getSupportFragmentManager().beginTransaction().replace(R.id.settings_container, new SettingsFragment()).commit();
You can initialize a global variable for the settings fragment and set it up like this:
settingsFragment = new SettingsFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.settings_container, settingsFragment).commit();
Then you can do something like this further down in onCreate to set what should be hidden based on existing preferences, or to change what is hidden/visible based on conditions in your OnSharedPreferenceChangeListener:
settingsFragment.findPreference("setting key").setVisible(false);