How do I programmatically add EditTextPreferences to my PreferenceFragment? - android

I am new to Android, so I need a little guidance on how to programmatically add EditTextPreference objects to my PreferenceFragment.
I am pulling in a list of values from a web service. I have saved them successfully to my SharedPreferences, and I use them to generate URLs (path portion).
I would like the users of my app to be able to edit these values, but after lots of searching on Google, it isn't clear to me how to programmatically add EditTextPreference objects to my PreferenceFragment.
Please note, my PreferenceFragment is working fine for the SharedPreferences values I hard code as names into the preference xml file (PreferenceScreen). I also know how to get my SharedPreferences, so don't worry about having to explain that portion to me.
I use addPreferencesFromResource in onCreate of my PreferenceFragment. Should I add them in the onCreateView? I was thinking I could get the PreferenceCategory and add them there? But again, I am not sure how to do that. I would really be grateful for the help!
// Code
PrefsFragment.java:
package com.example.lorddoineedhelp;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class PrefsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = super.onCreateView(inflater, container, savedInstanceState);
// I am guessing I need to do something here?
return v;
}
}
XML File for PreferenceFragment:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Hard coded values -->
<PreferenceCategory
android:title="General">
<CheckBoxPreference
android:key="debug"
android:title="Debug"
android:summary="Enable Debug" />
</PreferenceCategory>
<PreferenceCategory android:title="Address">
<EditTextPreference
android:key="ipAddress"
android:title="IP Address"
android:summary="IP Address used for Image pings"
/>
<EditTextPreference
android:key="port"
android:title="Port"
android:summary="Port used for Image pings" />
</PreferenceCategory>
<!-- Where I want to add the values from my web service -->
<PreferenceCategory
android:title="Paths"
android:key="urlPaths">
</PreferenceCategory>
</PreferenceScreen>

You can add preferences, e.g. EditTextPreference, CheckBox, etc, programmatically in the "onCreate" method of the PreferenceFragment.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load "dummy" (empty) preferences from an XML resource
addPreferencesFromResource(R.xml.preferences_channelconfig);
PreferenceScreen screen = this.getPreferenceScreen(); // "null". See onViewCreated.
// Create the Preferences Manually - so that the key can be set programatically.
PreferenceCategory category = new PreferenceCategory(screen.getContext());
category.setTitle("Channel Configuration");
screen.addPreference(category);
CheckBoxPreference checkBoxPref = new CheckBoxPreference(screen.getContext());
checkBoxPref.setKey(channelConfig.getName() + "_ENABLED");
checkBoxPref.setTitle(channelConfig.getShortname() + "Enabled");
checkBoxPref.setSummary(channelConfig.getDescription());
checkBoxPref.setChecked(channelConfig.isEnabled());
category.addPreference(checkBoxPref);
}
The crucial step is the addPreferencesFromResource(...), with a dummy xml to attach an empty PreferenceScreen to the fragment. Without this, there is no "top-level Preference that is the root of a Preference hierarchy", thus this.getPreferenceScreen() returns Null.
The XML I used was just:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:orderingFromXml="true">
</PreferenceScreen>
Hope that helps someone.

Here is the code to create a PreferenceFragment programmatically:
public class MyPreferenceFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(getActivity());
setPreferenceScreen(screen);
EditTextPreference preference = new EditTextPreference(screen.getContext());
preference.setKey("EditTextPreference");
preference.setTitle("Edit Text Preference");
preference.setSummary("Click the preference to edit text.");
screen.addPreference(preference);
}
}

I don't know if it works in the onCreateView method but it does in the onViewCreated.
Here is my code (Inside a subclass of PreferenceFragment):
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.getPreferenceScreen().addPreference(new EditTextPreference(getActivity()));
}

Related

Android PreferencesActivity showing previous fragment

I created a Preferences screen by extending PreferencesActivity as per the example here.
The first screen appears okay but when I click to see the second screen I see them one on top of the other.
So, taking advice from a number of other thread, I changed the background of the second fragment to black.
This worked in as much as I no longer see them both. But instead I see the FIRST one only except for the header.
The first screen looks like this:
and the second one looks like this:
Only the header line changed whereas the rest remained the same.
This is the code in my activity:
public class SettingsActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
getFragmentManager().beginTransaction().replace(android.R.id.content, new PreferencesMain()).commit();
}
/**
* This fragment contains a second-level set of preference that you
* can get to by tapping an item in the first preferences fragment.
*/
public static class PreferencesMain extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
}
#Override
public void onStart() {
super.onStart();
View view = getView();
view.setBackgroundColor(ResourcesCompat.getColor(getResources(),android.R.color.black, null));
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setBackgroundColor(ResourcesCompat.getColor(getResources(),android.R.color.black, null));
}
}
/**
* This fragment contains a second-level set of preference that you
* can get to by tapping an item in the first preferences fragment.
*/
public static class PreferencesNotifications extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences_notifications);
}
#Override
public void onStart() {
super.onStart();
View view = getView();
view.setBackgroundColor(ResourcesCompat.getColor(getResources(),android.R.color.black, null));
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setBackgroundColor(ResourcesCompat.getColor(getResources(),android.R.color.black, null));
}
}
#Override
protected boolean isValidFragment (String fragmentName) {
return true;
}
}
preferences.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/settings_notifications">
<!-- This PreferenceScreen tag sends the user to a new fragment of
preferences. If running in a large screen, they can be embedded
inside of the overall preferences UI. -->
<PreferenceScreen
android:fragment="activities.SettingsActivity$PreferencesNotifications"
android:title="#string/settings_notifications_managePushTitle"
android:summary="#string/settings_notifications_managePushSummary">
android:background="#android:color/black"
<!-- Arbitrary key/value pairs can be included for fragment arguments -->
<extra android:name="someKey" android:value="somePrefValue" />
</PreferenceScreen>
</PreferenceCategory>
preferences_notifications.xml
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/settings_notifications_managePushHeader">
<CheckBoxPreference
android:key="checkbox_preference"
android:title="#string/settings_notifications_managePushEntry1Title"
android:summary="#string/settings_notifications_managePushEntry1Summary" />
</PreferenceCategory>
Add a header.xml to manage settings, and use onBuildHeaders method.
Resource headers.xml:
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header android:title="Digle"
android:fragment="activities.SettingsActivity$PreferencesMain"/>
</preference-headers>
Class SettingsActivity:
public class SettingsActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Remove all code in here
}
#Override
public void onBuildHeaders(List<Header> target) {
super.onBuildHeaders(target);
loadHeadersFromResource(R.xml.headers, target);
}
...
}
EDIT:
An alternative could be use subscreens. You have to place the group of Preference objects inside a PreferenceScreen.
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="Settings"
android:title="#string/app_name">
<PreferenceCategory
android:title="#string/settings_notifications">
<PreferenceScreen
android:background="#android:color/black"
android:summary="#string/settings_notifications_managePushSummary"
android:title="#string/settings_notifications_managePushTitle">
<PreferenceCategory
android:title="#string/settings_notifications_managePushHeader">
<CheckBoxPreference
android:key="checkbox_preference"
android:summary="#string/settings_notifications_managePushEntry1Summary"
android:title="#string/settings_notifications_managePushEntry1Title"/>
</PreferenceCategory>
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>

Android: How to show title bar in Preferences Fragment

I have searched the forums, but can't seem to figure out how to show a title bar in the Android Preferences Fragment. I see questions about how to hide it, but not about how to show it.
I have a preferences activity
public class SettingsActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content,new SettingsFragment()).commit();
}
}
And I have a preferences fragment:
public class SettingsFragment extends PreferenceFragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
And I have a preferences xml:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="#string/title">
<CheckBoxPreference
android:key="regular"
However there is no title bar at the top of the preferences screen. Any ideas how to make one show?
You mean show title bar in the fragment?
you can try to inherit AppCompatActivity.

Implementing PreferenceFragment?

I've been struggling to implement PreferenceFragment in my app. My goal is to have the preferences view replace my main_activity fragment container so I can keep the same nav drawer, action bar, etc.
I have created a Preference Fragment class like so:
public class MadlibsSettings extends PreferenceFragment {
android.support.v7.app.ActionBar actionBar;
CheckBoxPreference themeSwitch;
ListPreference fontSize;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
view.setBackgroundColor(getResources().getColor(android.R.color.white));
actionBar = (android.support.v7.app.ActionBar) ((MainActivity)getActivity()).getSupportActionBar();
actionBar.setTitle("Settings");
addPreferencesFromResource(R.layout.madlibs_settings);
//fontSize = (ListPreference) findPreference("list");
return view;
}
}
And my prefs in R.layout.madlibs_settings are:
<PreferenceCategory android:title="PreferenceCategory A" >
<CheckBoxPreference
android:id="#+id/checkbox1"
android:defaultValue="false"
android:key="checkbox1"
android:summary="Switches App Layout to Dark Theme"
android:title="Darker Theme" />
</PreferenceCategory>
<PreferenceCategory android:title="PreferenceCategory B" >
<ListPreference
android:id="#+id/ListPreference"
android:defaultValue="8"
android:entries="#array/list"
android:entryValues="#array/LValues"
android:key="list"
android:summary="Choose Font Size"
android:title="Font Size" />
</PreferenceCategory>
</PreferenceScreen>
I'm not really sure what to do in my main activity in order to inflate preferences and then access the data from my prefs usings haredpreferences. Any help would be great, I'm definitely a rookie with fragments.
Because the preferenceFragment is an actual Fragment, you can simply exchange it using a FragmentTransation as you would with your other fragments in your Nav drawer. In whatever onClick event or otherwise, use something like the following to go to your PreferenceFragment:
getFragmentManager().beginTransaction()
.replace(R.id.fragmentContainer, new MadlibSettings())
.commit();
Source: http://developer.android.com/guide/topics/ui/settings.html#Fragment
And then for accessing your preferences from anywhere, the following code should be able to get you started.
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext());
String val1 = settings.getString("KEY", "default_value");
settings.putString("key", "new_value");
If you decide to make your own preferences file in addition to the settings one, then you will use:
SharedPreferences settings = getContext().getSharedPreferences("pref_file_name", 0);
String val1 = settings.getString("KEY", "default_value");
settings.putString("KEY", "new_value");
Above method has been seen to run into issues like having a transparent preference window. Only way I could avoid those issues was to use a preferenceActivity class:
How do you create Preference Activity and Preference Fragment on Android? by WannaGetHigh

SharedPreferences sub PreferenceScreen with custom fragment class

I have a PreferenceScreen with a sub PreferenceScreen:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="SOUND">
<PreferenceScreen android:title="Sound Options"
android:fragment="com.test.SoundPreferenceFragment">
</PreferenceScreen>
</PreferenceCategory>
...
The sub preference is a custom implementation of a PreferenceFragment
public class SoundPreferencesFragment extends PreferenceFragment implements
OnSharedPreferenceChangeListener {
...
I am wondering how I am supposed to start the custom fragment. In my main activity I do this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFragmentManager().beginTransaction().replace(android.R.id.content, settingsPreference).commit();
}
Which works great to start my main settings preference screen.
I then override:
#Override
public boolean onPreferenceStartFragment(PreferenceFragment fragment, Preference preference) {
getFragmentManager().beginTransaction().replace(android.R.id.content, new SoundPreferenceFragment()).commit();
return true;
}
That seems to work but I have a few questions.
Is that the right way?
When I go into the Sounds preference and hit the back button it takes me all the way out of settings. Is that how it should work?
I think I can also tell the sub preference to start a new intent, i.e. create a new SoundPreferencesActivity. Is that a more 'correct' way.
EDIT:
I also tried not to extend 'onPreferenceStartFragment' in my main activity hoping that android would take care of starting the new preference correctly' It does seem to start the new PreferenceFragment but it overlays the UI right on top of the main preferences UI.
You have to create a new activity and attach the SoundPreferencesFragment .
when you select the item in main setting activity , start the new activity

Remove/hide a preference from the screen

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);

Categories

Resources