Mon écran de préférences interne de PreferenceFragmentCompat ne s'affiche pas ou semble ignorer les événements de tapotement.
J'ai créé MyPreferenceFragment
que extends PreferenceFragmentCompat
public class MyPreferenceFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
}
}
alors j'ai changé mon thème à styles.xml
comme
<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
Et enfin, créez mon fichier preferences.xml
comme
<PreferenceScreen xmlns:Android="http://schemas.Android.com/apk/res/Android">
<CheckBoxPreference Android:title="Check Me"/>
<PreferenceScreen Android:title="My Screen"> <!-- This is not opening -->
<EditTextPreference Android:title="Edit text" />
</PreferenceScreen>
</PreferenceScreen>
Au build.gradle
j'ai ajouté les deux:
compile 'com.Android.support:appcompat-v7:23.0.1'
compile 'com.Android.support:preference-v7:23.0.1'
code de l'activité
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main.xml
<fragment xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:id="@+id/fragment"
Android:name="com.mando.preferenceapp.MyPreferenceFragment"
Android:layout_width="match_parent"
Android:layout_height="match_parent" />
Test du code ci-dessus Je ne peux pas ouvrir/entrer dans l'écran des préférences. Est-ce que je manque quelque chose? Pourquoi ça ne marche pas?
Après avoir passé de nombreuses heures à essayer, à chercher et heureusement avec l’aide des créateurs de la bibliothèque de support. J'ai réussi à le faire fonctionner.
Étape 1. Activity
public class MyActivity extends AppCompatActivity implements
PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
// Create the fragment only when the activity is created for the first time.
// ie. not after orientation changes
Fragment fragment = getSupportFragmentManager().findFragmentByTag(MyPreferenceFragment.FRAGMENT_TAG);
if (fragment == null) {
fragment = new MyPreferenceFragment();
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, fragment, MyPreferenceFragment.FRAGMENT_TAG);
ft.commit();
}
}
@Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat,
PreferenceScreen preferenceScreen) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyPreferenceFragment fragment = new MyPreferenceFragment();
Bundle args = new Bundle();
args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey());
fragment.setArguments(args);
ft.replace(R.id.fragment_container, fragment, preferenceScreen.getKey());
ft.addToBackStack(preferenceScreen.getKey());
ft.commit();
return true;
}
}
Conseils.
xml
, vous aurez des plantages lors de changements d'orientation.onCreate
afin d'éviter de perdre votre fragment lorsque vous vous trouvez dans un écran de préférences.PreferenceFragmentCompat.OnPreferenceStartScreenCallback
et recréer des fragments de la même instance. Étape 2. PreferenceFragment
public class MyPreferenceFragment extends PreferenceFragmentCompat {
public static final String FRAGMENT_TAG = "my_preference_fragment";
public MyPreferenceFragment() {
}
@Override
public void onCreatePreferences(Bundle bundle, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
}
}
Conseils.
setPreferencesFromResource
et tirez parti de la rootKey
de chaque écran. De cette façon, votre code sera réutilisé correctement.findPreference
dans votre fragment, il devrait avoir des vérifications null
comme lorsque vous étiez dans les écrans intérieurs, cela ne vous donnerait rien.Ce qui manque maintenant, c’est l’implémentation de la flèche arrière dans la barre d’action (action home) mais cela ne marche jamais tout seul ;-)
J'ai également créé une application de démonstration contenant tout ce code que vous pouvez trouver sur github .
La solution consiste à démarrer un autre fragment de la même classe mais avec une clé racine différente. Aucune activité d'activité impliquée.
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey){
if(getArguments() != null){
String key = getArguments().getString("rootKey");
setPreferencesFromResource(R.xml.preferences, key);
}else{
setPreferencesFromResource(R.xml.preferences, rootKey);
}
}
@Override
public void onNavigateToScreen(PreferenceScreen preferenceScreen){
ApplicationPreferencesFragment applicationPreferencesFragment = new ApplicationPreferencesFragment();
Bundle args = new Bundle();
args.putString("rootKey", preferenceScreen.getKey());
applicationPreferencesFragment.setArguments(args);
getFragmentManager()
.beginTransaction()
.replace(getId(), applicationPreferencesFragment)
.addToBackStack(null)
.commit();
}
Je l'ai fait légèrement différemment, je lance une nouvelle activité pour chaque écran. Cela semble nécessiter moins de piratage: inutile de gâcher l'échange de fragments et de couleurs d'arrière-plan. Vous bénéficiez également d'une animation de changement d'activité en bonus!
public class PreferencesActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
final static private String KEY = "key";
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences);
setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) actionBar.setDisplayHomeAsUpEnabled(true);
if (savedInstanceState != null)
return;
Fragment p = new PreferencesFragment();
String key = getIntent().getStringExtra(KEY);
if (key != null) {
Bundle args = new Bundle();
args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, key);
p.setArguments(args);
}
getSupportFragmentManager().beginTransaction()
.add(R.id.preferences, p, null)
.commit();
}
@Override public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
Intent intent = new Intent(PreferencesActivity.this, PreferencesActivity.class);
intent.putExtra(KEY, preferenceScreen.getKey());
startActivity(intent);
return true;
}
@Override public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == Android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
public static class PreferencesFragment extends PreferenceFragmentCompat implements ... {
private static final String FRAGMENT_DIALOG_TAG = "Android.support.v7.preference.PreferenceFragment.DIALOG";
private String key;
@Override public void onCreatePreferences(Bundle bundle, String key) {
setPreferencesFromResource(R.xml.preferences, this.key = key);
}
// this only sets the title of the action bar
@Override public void onActivityCreated(Bundle savedInstanceState) {
ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
if (actionBar != null) actionBar.setTitle((key == null) ? "Settings" : findPreference(key).getTitle());
super.onActivityCreated(savedInstanceState);
}
}
}
xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:layout_margin="0dp"
Android:orientation="vertical"
Android:padding="0dp"
Android:id="@+id/preferences">
<Android.support.v7.widget.Toolbar
Android:id="@+id/toolbar"
Android:layout_width="match_parent"
Android:layout_height="?attr/actionBarSize"
Android:background="?attr/colorPrimary" />
<!-- preference fragment will be inserted here programmatically -->
</LinearLayout>
Une autre solution consiste à suivre vous-même les écrans de préférences et à utiliser l’application PreferenceFragmentCompat api.
Voici la solution de base. (Cela ne couvre pas tous les cas Edge, voir la solution avancée ci-dessous)
Assurez-vous d'avoir configChanges = "orientation" pour empêcher la création/destruction
<activity
Android:name=".MyPreferencesActivity"
Android:configChanges="orientation" />
Dans l'activité, vous souhaitez conserver une pile d'écrans de préférence et de push/pop selon vos besoins
/* track the screens as a Stack */
private Stack<PreferenceScreen> preferenceScreens = new Stack<>();
// ensure your Activity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback
@Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
preferenceScreens.Push(preferenceFragmentCompat.getPreferenceScreen());
preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
return true;
}
@Override
public void onBackPressed() {
if (preferenceScreens.empty()) {
super.onBackPressed();
} else {
prefsFragment.setPreferenceScreen(preferenceScreens.pop());
}
}
Facultatif: dans votre fragment qui étend PreferenceFragmentCompat, ajoutez setRetainInstance (true). (Notez que sans , Cela fonctionnera probablement aussi, mais il pourrait "se" casser de temps en temps. Si vous définissez "Ne pas garder les activités" sur true, et Vous verrez que cela va être collecté)
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setRetainInstance(true);
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.preferences, rootKey);
...
C'est tout! Sauf que si vous voulez couvrir les cas Edge ...
Solution avancée (si vous définissez 'Ne pas garder les activités sur True, vous devez vous assurer de pouvoir tout reconstruire à partir de savedInstanceState)
Notez que la réponse acceptée ne conserve pas réellement l'état.
Solution complète et avancée utilisant PreferenceFragmentCompat api et préservant la pile PreferenceScreen
import Android.os.Bundle;
import Android.support.v7.app.AppCompatActivity;
import Android.support.v7.preference.PreferenceFragmentCompat;
import Android.support.v7.preference.PreferenceScreen;
import Java.util.ArrayList;
import Java.util.Objects;
import Java.util.Stack;
/**
* Class to Show the preference screen with Activity keeping state
* @author Aaron Vargas
*/
public class MyPreferencesActivityStateful extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
private static final String PREFERENCE_SCREENS = "PREFERENCE_SCREENS";
private PrefsFragment prefsFragment;
private Stack<PreferenceScreen> preferenceScreens = new Stack<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Display the fragment as the main content. Re-Use if possible
String tag = PrefsFragment.class.getName();
prefsFragment = (PrefsFragment) getSupportFragmentManager().findFragmentByTag(tag);
if (prefsFragment == null) prefsFragment = new PrefsFragment();
getSupportFragmentManager().beginTransaction().replace(Android.R.id.content,
prefsFragment, tag).commit();
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// rebuild preferenceScreen stack
for (String screenKey : Objects.requireNonNull(savedInstanceState.getStringArrayList(PREFERENCE_SCREENS))) {
preferenceScreens.Push((PreferenceScreen) prefsFragment.findPreference(screenKey));
}
PreferenceScreen preferenceScreen = preferenceScreens.pop();
if (preferenceScreen != prefsFragment.getPreferenceScreen()) { // optimize if same
prefsFragment.setPreferenceScreen(preferenceScreen);
}
}
@Override
public boolean onPreferenceStartScreen(PreferenceFragmentCompat preferenceFragmentCompat, PreferenceScreen preferenceScreen) {
preferenceScreens.Push(preferenceFragmentCompat.getPreferenceScreen());
preferenceFragmentCompat.setPreferenceScreen(preferenceScreen);
return true;
}
@Override
public void onBackPressed() {
// account for onRestore not getting called equally to onSave
while (preferenceScreens.contains(prefsFragment.getPreferenceScreen())) {
preferenceScreens.remove(prefsFragment.getPreferenceScreen());
}
if (preferenceScreens.empty()) {
super.onBackPressed();
} else {
prefsFragment.setPreferenceScreen(preferenceScreens.pop());
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
preferenceScreens.Push(prefsFragment.getPreferenceScreen());
ArrayList<String> keys = new ArrayList<>(preferenceScreens.size());
for (PreferenceScreen screen : preferenceScreens) {
keys.add(screen.getKey());
}
outState.putStringArrayList(PREFERENCE_SCREENS, keys);
}
public static class PrefsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setRetainInstance(true); // ensure in manifest - Android:configChanges="orientation"
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.preferences, rootKey);
}
}
}
Vous pouvez également gérer tout cela dans votre fragment au lieu de l'activité. En voici un résumé https://Gist.github.com/aaronvargas/0f210ad8643b512efda4acfd524e1232
Basé sur la solution @squirrel Intent, je l’ai fait fonctionner de cette façon. Cela nécessite encore moins de piratage.
Activité:
import Android.support.v7.app.AppCompatActivity;
public class SettingsActivity extends AppCompatActivity {
public static final String TARGET_SETTING_PAGE = "target";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SettingsFragment settingsFragment = new SettingsFragment();
Intent intent = getIntent();
if (intent != null) {
String rootKey = intent.getStringExtra(TARGET_SETTING_PAGE);
if (rootKey != null) {
settingsFragment.setArguments(Bundler.single(TARGET_SETTING_PAGE, rootKey));
}
}
getFragmentManager().beginTransaction()
.replace(Android.R.id.content, settingsFragment)
.commit();
}
}
Fragment:
import Android.support.v14.preference.PreferenceFragment;
public class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle arguments = getArguments();
if (arguments != null && arguments.getString(TARGET_SETTING_PAGE) != null) {
setPreferencesFromResource(R.xml.preferences, arguments.getString(TARGET_SETTING_PAGE));
} else {
addPreferencesFromResource(R.xml.preferences);
}
}
@Override
public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
Intent intent = new Intent(getActivity(), SettingsActivity.class)
.putExtra(TARGET_SETTING_PAGE, preferenceScreen.getKey());
startActivity(intent);
super.onNavigateToScreen(preferenceScreen);
}
}
Il est regrettable que vous ayez besoin de tant de hacks dans les bibliothèques de support d’appcompat pour obtenir une solution qui fonctionne parfaitement avec Android standard.