可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
My inner PreferenceScreen of PreferenceFragmentCompat is not showing, or seems to ignore tapping events.
I created MyPreferenceFragment
that extends PreferenceFragmentCompat
public class MyPreferenceFragment extends PreferenceFragmentCompat { @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.preferences); } }
then I changed my theme at styles.xml
like
And finally create my preferences.xml
file like
At the build.gradle
I have added both:
compile 'com.android.support:appcompat-v7:23.0.1' compile 'com.android.support:preference-v7:23.0.1'
code of the Activity
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
activity_main.xml
Testing the above code I cannot open / get into the preference screen. Am I missing something? Why this isn't working?
回答1:
After spending many many hours with tries, searching and thankfully with some assistance from the creators of the support library. I've managed to make it work.
Step 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; } }
Tips.
- Do not add the fragment by
xml
you will have crashes on orientation changes. - Handle the recreations of activity / fragment add in
onCreate
so as to avoid losing your fragment when inside a preference screen. - The host activity of the fragment should implement the
PreferenceFragmentCompat.OnPreferenceStartScreenCallback
and recreate fragments of the same instance.
Step 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); } }
Tips.
- Use the method
setPreferencesFromResource
and take advantage of the rootKey
of each screen. This way your code will be reused properly. - Keep in mind that if you have code like
findPreference
in your fragment it should have null
checks as when you were in inner screens this will give you nothing.
The thing that is missing now is the implementation of the back arrow in the actionbar (home action) but this never works by itself ;-)
I' also created a demo app wrapping all this code you can find it on github.
回答2:
I did it slightly differently, I'm launching a new activity for each screen. This seems to require less hacks: no need to mess with swapping fragments and background colors. You also get activity change animation as a 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:
回答3:
Based on @squirrel Intent solution, I made it work this way. It requires even less hacking.
Activity:
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); } }
It is sad you need so much hacks in the support appcompat libraries for something that works flawlessly out-of-the-box in standard android.
回答4:
Solution is to start another fragment of the same class but with different root key. No Activity actions involved.
@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(R.id.container, applicationPreferencesFragment) .addToBackStack(null) .commit(); }