PreferenceFragmentCompat的内部PreferenceScreen未显示或似乎忽略了点击事件。
我创建了MyPreferenceFragment
extends PreferenceFragmentCompat
public class MyPreferenceFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
}
}
然后我在styles.xml
改变了我的主题
<style name="AppTheme" parent="@style/Theme.AppCompat.Light">
<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>
</style>
最后创建我的preferences.xml
文件,如
<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>
在build.gradle
我添加了两个:
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:preference-v7:23.0.1'
活动代码
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" />
测试上面的代码我无法打开/进入首选项屏幕。我错过了什么吗?为什么这不起作用?
答案 0 :(得分:32)
在花了很多时间尝试,搜索并感谢支持库的创建者的一些帮助。我已成功地使它发挥作用。
第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;
}
}
提示。
xml
添加片段,否则会因方向更改而崩溃。onCreate
中活动/片段添加的重新创建,以避免在偏好设置屏幕内丢失您的片段。PreferenceFragmentCompat.OnPreferenceStartScreenCallback
并重新创建同一实例的片段。 第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);
}
}
提示。
setPreferencesFromResource
并利用每个屏幕的rootKey
。这样您的代码就可以正确使用。findPreference
之类的代码,则应该进行null
检查,就像您在内部屏幕中一样,这将不会给您任何信息。现在缺少的是动作栏中的后退箭头(主动作)的实现,但这本身并不起作用; - )
我还创建了一个演示应用程序,其中包含了您可以在github上找到的所有代码。
答案 1 :(得分:6)
解决方案是启动同一类但具有不同根密钥的另一个片段。不涉及任何活动。
@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();
}
答案 2 :(得分:5)
我做的略有不同,我正在为每个屏幕启动一个新活动。这似乎需要更少的黑客攻击:无需混淆交换片段和背景颜色。您还可以获得活动更改动画作为奖励!
public class MainActivity extends Activity {
public static List<ActivityManager.RunningAppProcessInfo> runningProcesses;
private static Logger logger = Logger.getLogger(MainActivity.class.getName());
private Map<Integer, String> pidMap = new TreeMap<Integer, String>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = getApplicationContext();
ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
setContentView(R.layout.activity_main);
List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
logger.info("pid: " + runningAppProcessInfo.pid + " | " + runningAppProcessInfo.processName);
}
}
的xml:
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);
}
}
}
答案 3 :(得分:1)
另一种解决方案是自己跟踪首选项屏幕并使用PreferenceFragmentCompat api
这是基本的解决方案。 (它并不涵盖所有的极端情况,请参阅下面的高级解决方案)
确保您具有configChanges =“ orientation”防止创建/销毁
<activity
android:name=".MyPreferencesActivity"
android:configChanges="orientation" />
在“活动”中,您要保留一堆PreferenceScreens并根据需要推送/弹出
/* 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());
}
}
可选:在扩展PreferenceFragmentCompat的Fragment中,添加setRetainInstance(true)。 (请注意,没有 这可能也会起作用,但偶尔会“中断”。如果您将“不要保留活动”设置为true,并且 您会看到它会被收集)
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setRetainInstance(true);
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.preferences, rootKey);
...
就是这样!除了要覆盖一些极端情况...
高级解决方案(如果将“不要将活动设置为True”,则需要确保可以从saveInstanceState重建所有内容)
请注意,接受的答案实际上并不保留状态。
使用PreferenceFragmentCompat api并保留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);
}
}
}
您还可以在Fragment(而不是Activity)中处理所有这些问题。这是https://gist.github.com/aaronvargas/0f210ad8643b512efda4acfd524e1232
的要点答案 4 :(得分:0)
基于@squirrel Intent解决方案,我以这种方式工作。它需要更少的黑客攻击 活性:
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();
}
}
片段:
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);
}
}
很遗憾你在支持appcompat库中需要这么多黑客攻击,因为它可以在标准的android中完美无缺地运行。
答案 5 :(得分:0)
使用 Navigation Component (Android Jetpack)和Kotlin,现在非常容易:
class PrefsFragment : PreferenceFragmentCompat() {
private val args: PrefsFragmentArgs by navArgs()
override fun onCreatePreferences(state: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.prefs, args.rootKey)
}
override fun onNavigateToScreen(preferenceScreen: PreferenceScreen?) {
findNavController().navigate(
PrefsFragmentDirections.changeRoot(preferenceScreen!!.key)
)
}
}
答案 6 :(得分:0)
使用导航组件+ androidx.appcomat的替代方法: https://stackoverflow.com/a/59732509/5437789
有了这个,您将不会松开后退纸叠并在按下后退按钮时返回到主页设置。
答案 7 :(得分:0)
这是来自 android documentation 的简单解决方案。要使用 PreferenceFragmentCompact 实现内部首选项屏幕导航,您所要做的就是将 fragment 属性添加到嵌入的首选项屏幕,给出要导航到的片段完整路径,例如。 com.example.FragmentName.
示例代码:
<PreferenceCategory app:title="@string/choose_theme"
android:icon="@drawable/ic_baseline_color_lens_24">
<SwitchPreference
android:title="@string/apply_night_mode"
android:key="@string/key_enable_night_mode"/>
<PreferenceScreen
android:fragment="com.example.simbokeyboard.BlankFragment"
android:title="Custom Theme"
android:summary="@string/theme_summary">
<Preference
android:key="@string/choose_theme"
android:title="@string/choose_theme"
android:layout="@layout/theme_chooser"/>
</PreferenceScreen>
</PreferenceCategory>