1

创建首选项活动后,我注意到当我的复选框首选项被选中时,我的主要活动不会更改主题,尽管调用onSharedPreferenceChanged. 有谁知道出了什么问题以及如何解决?

样式.xml

<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
    <!--<item name="android:windowBackground">@color/colorLight</item>-->
</style>

<style name="MyDarkMaterialTheme" parent="android:Theme.Material">
    <item name="android:windowBackground">@android:color/black</item>
</style>

<style name="MyLightMaterialTheme" parent="android:Theme.Material.Light.DarkActionBar">
    <item name="android:windowBackground">@color/colorLight</item>
</style>

MainActivity 类

public class MainActivity extends Activity {

    boolean themeState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setTheme(R.style.MyDarkMaterialTheme);

        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:
                Intent settingsIntent = new Intent(this, SettingsActivity.class);
                startActivity(settingsIntent);
                return true;

            default:
                return super.onOptionsItemSelected(item);
        }
    }


    @Override
    public void onResume(){
        super.onResume();
        loadPreferences();
        displaySettings();
    }

    private void loadPreferences(){
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        themeState = sharedPreferences.getBoolean("pref_pref1", true);
    }

    public void displaySettings() {
        if (themeState) {
            setTheme(R.style.MyDarkMaterialTheme);
            recreate();
        } else {
            setTheme(R.style.MyLightMaterialTheme);
            recreate();
        }
    }
}

SettingsActivity 类

public class SettingsActivity extends Activity {

    boolean themeState;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);

        if (savedInstanceState == null) {
            Fragment preferenceFragment = new SettingsFragment();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(R.id.pref_container, preferenceFragment);
            ft.commit();
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            final Intent intent = getParentActivityIntent();
            if(intent != null){
                intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
            }
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }


    @Override
    public void onResume(){
        super.onResume();
        loadPreferences();
        displaySettings();
    }

    private void loadPreferences(){
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        themeState = sharedPreferences.getBoolean("pref_pref1", true);
    }

    public void displaySettings() {
        if (themeState) {
            getApplication().setTheme(R.style.MyDarkMaterialTheme);
            recreate();
        } else {
            getApplication().setTheme(R.style.MyLightMaterialTheme);
            recreate();
        }
    }
}

SettingsFragment 类

public class SettingsFragment extends PreferenceFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Load the Preferences from the XML file
        addPreferencesFromResource(R.xml.app_preferences);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        new SharedPreferences.OnSharedPreferenceChangeListener() {
            @Override
            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
                // Settings activity or fragment should restart with changes applied

            }
        };
    }
}

xml/app_preferences

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">

    <CheckBoxPreference
        android:key="pref_pref1"
        android:title="@string/dark_theme"
        android:defaultValue="false"
        android:layout="@layout/preference_multiline"
        />

</PreferenceScreen>

Csongi77的建议

public class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Load the Preferences from the XML file
        addPreferencesFromResource(R.xml.app_preferences);

        // Find appropriate preference
        CheckBoxPreference mThemePreference =(CheckBoxPreference)getPreferenceManager().findPreference("pref_pref1");
        // we have to set up listener in order for persisting change to new value
        mThemePreference.setOnPreferenceChangeListener(this);
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mThemePreference.getContext());
        Boolean value=sharedPreferences.getBoolean("pref_pref1",true);
        onPreferenceChange(mThemePreference, value);
    }

    // overriding onPreferenceChange - if we return true, the preference will be persisted
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        String preferenceKey = preference.getKey();
        // we have to check the preference type and key, maybe later we have more preferences....
        if(preference instanceof CheckBoxPreference){
            if(preferenceKey.equals("pref_pref1")){
                ((CheckBoxPreference)preference).setChecked((Boolean)newValue);
                // ... do other preference related stuff here - if necessary, for example setSummary, etc...
                getActivity().setTheme(R.style.MyDarkMaterialTheme);
            } else {
                getActivity().setTheme(R.style.MyLightMaterialTheme);
            }
        }
        return true;
    }
}

日志猫

          Process: com.companyname.appname, PID: 4505
          java.lang.NullPointerException: Attempt to invoke interface method 'void com.companyname.appname.SettingsFragment$PreferenceXchangeListener.onXchange(java.lang.Boolean)' on a null object reference
              at com.companyname.appname.SettingsFragment.onPreferenceChange(SettingsFragment.java:57)
              at android.preference.Preference.callChangeListener(Preference.java:928)
              at android.preference.TwoStatePreference.onClick(TwoStatePreference.java:64)
              at android.preference.Preference.performClick(Preference.java:983)
              at android.preference.PreferenceScreen.onItemClick(PreferenceScreen.java:214)
              at android.widget.AdapterView.performItemClick(AdapterView.java:300)
              at android.widget.AbsListView.performItemClick(AbsListView.java:1143)
              at android.widget.AbsListView$PerformClick.run(AbsListView.java:3044)
              at android.widget.AbsListView$3.run(AbsListView.java:3833)
              at android.os.Handler.handleCallback(Handler.java:739)
              at android.os.Handler.dispatchMessage(Handler.java:95)
              at android.os.Looper.loop(Looper.java:135)
              at android.app.ActivityThread.main(ActivityThread.java:5221)
              at java.lang.reflect.Method.invoke(Native Method)
              at java.lang.reflect.Method.invoke(Method.java:372)
              at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
              at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

SettingsActivity 类

public class SettingsActivity extends Activity implements SettingsFragment.PreferenceXchangeListener {
    private static final String TAG = SettingsActivity.class.getSimpleName();

    // declaring initial value for applying appropriate Theme
    private Boolean mCurrentValue;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // Checking which Theme should be used. IMPORTANT: applying Themes MUST called BEFORE super.onCreate() and setContentView!!!
        Log.d(TAG, "onCreate:::: retrieving preferences");
        SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
        mCurrentValue = mSharedPreferences.getBoolean("my_preference",false);
        Log.d(TAG, "onCreate:::: my_preference and mCurrentValue=" + mCurrentValue);
        if(mCurrentValue){
            // we have to use simple setTheme() instead getApplication.setTheme()!!!
            setTheme(R.style.DarkTheme);
            Log.d(TAG, "onCreate:::: setTheme:DarkTheme");
        } else {
            setTheme(R.style.LightTheme);
            Log.d(TAG, "onCreate:::: setTheme:LightTheme");
        }

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_settings);

        Fragment preferenceFragment = new SettingsFragment();
        getFragmentManager().beginTransaction().add(R.id.preference_container, preferenceFragment).commit();
    }

    // callback method for changing preference. It's called only if "my_preference" has changed
    @Override
    public void onXchange(Boolean value) {
        // if value differs from previous Theme, we recreate Activity
        Log.d(TAG, "onXchange:::: \n has called");
        if (value!=mCurrentValue) {
            Log.d(TAG, "onXchange:::: \n new value!=oldValue");
            mCurrentValue=value;
            recreate();
        }
    }
}

SettingsFragment 类

public class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {
    private static final String TAG = SettingsFragment.class.getSimpleName();

    // declaring PreferenceXchangeListener
    private PreferenceXchangeListener mPreferenceXchangeListener;

    public SettingsFragment() {
    }

    // declaring PreferenceXchangeListener in order to communicate with Activities
    public interface PreferenceXchangeListener {
        void onXchange(Boolean value);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Load the Preferences from the XML file
        addPreferencesFromResource(R.xml.app_preferences);

        CheckBoxPreference mCheckBoxPreference = (CheckBoxPreference)findPreference("my_preference");
        mCheckBoxPreference.setOnPreferenceChangeListener(this);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // on Attch we assign parent Activity as PreferenceXchangeListener
        try {
            mPreferenceXchangeListener = (PreferenceXchangeListener) context;
        } catch (ClassCastException e) {
            Log.e(TAG, "onAttach::::: PreferenceXchangeListener must be set in parent Activity");
        }
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        String preferenceKey=preference.getKey();
        // only my_preference is checked in this case. Later you may add another behaviour to another preference change
        if(preferenceKey.equals("my_preference")){
            ((CheckBoxPreference)preference).setChecked((Boolean)newValue);
            // executing parent Activity's callback with the new value
            mPreferenceXchangeListener.onXchange((Boolean)newValue);
            return true;
        }
        // ... check other preferences here
        return false;
    }
}
4

2 回答 2

1

好的,这是工作版本:

1) 在 MainActivity.java 的 onCreate 方法中,在调用 super.onCreate()和setContentView()之前检查当前主题,并将其添加到私有全局布尔变量中(我们称之为 mTheme)。更多信息:以编程方式更改 Activity 的主题

2) 在 MainActivity.java 的 onStart() 方法中,您应该检查设置是否已更改,因为当您从另一个 Activity 返回时不会调用 onCreate。如果 mTheme!=newSettingValue,调用 recreate()。重要提示:它类似于 onDestroy 方法,因此之前设置的值可能会丢失! https://developer.android.com/reference/android/app/Activity#recreate()

3) 在您的 SettingsFragment 中,您必须使用 update(Boolean value) 方法定义一个接口 (ThemeXchangeListener)。您还必须声明 ThemeXchangeListener mListener 字段。

4) 在 SettingsFragment 的 onAttach(Context context) 方法中将 context 分配给 mListener -> mListener=(ThemeXchangeListener)context;

5) 在 SettingsFragment 的 onPreferenceChange(Preference pref, Object value) 中调用 mListener.update((Boolean)value);

6) 在 SettingsActivity 中声明用于存储当前主题值的布尔值 (boolean mTheme)。在 onCreate() 加载首选项并为其赋值。super.onCreate() 和 setContentView() 之前分配适当的主题。

7) 在 SettingsActivity 中实现 ThemeXchangeListener 并覆盖 update(Boolean value) 方法。如果 value!=mTheme,调用 recreate() 方法。这将立即更新您的主题。

您可以在以下位置查看完整的工作代码(带注释):https ://github.com/csongi77/UpdateThemeOnPreferenceChange

它在我的 API15(IceCreamSandwich)模拟器上运行良好。请让我知道它是否有效!最好的问候, Cs

于 2018-08-31T05:35:45.067 回答
1

尝试这个:

public class SettingsFragment extends PreferenceFragment implements Preference.OnPreferenceChangeListener {

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    //Load the Preferences from the XML file
    addPreferencesFromResource(R.xml.app_preferences);

    // Find appropriate preference
    CheckBoxPreference mThemePreference =(CheckBoxPreference)getPreferenceManager().findPreference("pref_pref1");
    // we have to set up listener in order for persisting change to new value
    mThemePreference.setOnPreferenceChangeListener(this);
    SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mThemePreference.getContext());
    Boolean value=sharedPreferences.getBoolean("pref_pref1",true);
    onPreferenceChange(mThemePreference, value);
    }

// overriding onPreferenceChange - if we return 'true', the preference will be persisted
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
    String preferenceKey = preference.getKey();
    // we have to check the preference type and key, maybe later we have more preferences....
    if(preference instanceof CheckBoxPreference){
        if(preferenceKey.equals("pref_pref1")){
      // Edited this line *******               
        ((CheckBoxPreference)preference).setChecked((Boolean)newValue);   
            // ... do other preference related stuff here - if necessary, for example setSummary, etc...       
        }
    }
return true;
}

}

简而言之:在您的 PreferenceFragment 中实现 OnPreferenceChange。当您覆盖 onPreferenceChane 时,返回 true。在这种情况下,旧的首选项将被覆盖。希望它有帮助(如果是,请不要忘记接受我的回答)!最好的问候, Cs

PS:不要忘记在模拟器上卸载应用程序

于 2018-08-30T06:53:39.243 回答