25

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" />

上記のコードをテストすると、設定画面を開く/入ることができません。何か不足していますか?なぜこれが機能しないのですか?

4

8 に答える 8

8

解決策は、同じクラスの別のフラグメントを開始することですが、ルート キーは異なります。アクティビティ アクションは含まれません。

@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();
}
于 2017-09-12T14:45:11.197 に答える
5

画面ごとに新しいアクティビティを起動しています。これにより、ハックが少なくて済むようです。フラグメントと背景色を交換する必要はありません。おまけとしてアクティビティ変更アニメーションも付いてきます!

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>
于 2015-09-22T17:37:22.197 に答える
2

もう 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 に設定した場合は、savedInstanceState からすべてを再構築できることを確認する必要があります)

受け入れられた回答は、実際には状態を保持しないことに注意してください。

  1. 「アクティビティを保持しない」を True に設定します
  2. ネストされた PreferenceScreen に移動します
  3. ホームを押してから、アプリに戻ります
  4. ネストされた PreferenceScreen に「あるはず」ですが、実際にはルート画面にあります

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

于 2018-11-02T20:06:09.037 に答える
0

Android documentationからの簡単な解決策を次に示します。PreferenceFragmentCompact を使用して内部設定画面ナビゲーションを実装するには、埋め込み設定画面にフラグメント属性を追加して、フラグメントに移動先のフル パスを指定するだけです。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>
于 2021-07-24T22:39:38.847 に答える