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" />
上記のコードをテストすると、設定画面を開いたり、取得したりできません。何か不足していますか?なぜこれが機能しないのですか?
何時間も試行錯誤した後、サポートライブラリの作成者からの支援を受けて、ありがたいことに検索しました。私はなんとかそれを機能させることができました。
ステップ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 で見つけることができるこのすべてのコードをラップするデモアプリも作成しました。
解決策は、同じクラスの別のルートキーで別のフラグメントを開始することです。アクティビティアクションは含まれていません。
@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();
}
少し違った方法で行いました。画面ごとに新しいアクティビティを起動しています。これにより、ハッキングが少なくなるようです。フラグメントと背景色の入れ替えをいじる必要はありません。おまけとして、活動変化アニメーションも!
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>
別の解決策は、設定画面を自分で追跡し、PreferenceFragmentCompat APIを使用することです
これが基本的な解決策です。 (すべてのEdgeケースをカバーしているわけではありません。以下の高度なソリューションを参照してください)
作成/破棄を防ぐために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を拡張するフラグメントに、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に設定した場合、savedInstanceStateからすべてを再構築できることを確認する必要があります)
受け入れられた回答は実際には状態を保存しないことに注意してください。
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);
}
}
}
アクティビティの代わりに、これらすべてをフラグメントで処理することもできます。これがその要点です https://Gist.github.com/aaronvargas/0f210ad8643b512efda4acfd524e1232
@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);
}
}
標準のAndroidでそのまますぐに機能するものをサポートするappcompatライブラリーに多くのハックが必要なのは残念です。
ナビゲーションコンポーネント+ androidx.appcomatを使用した代替方法: https://stackoverflow.com/a/59732509/5437789
これにより、戻るボタンを押しても、バックスタックが失われ、メインページの設定に戻ることはありません。
ナビゲーションコンポーネント( 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)
)
}
}