在调用onSharedPreferenceChanged之后应用主题不会更改

时间:2018-08-29 18:34:31

标签: java android xml android-theme android-preferences

创建首选项活动后,我注意到尽管选中onSharedPreferenceChanged,但选中复选框首选项后,我的主活动仍未更改主题。有谁知道哪里出了问题以及如何解决?

styles.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;
    }
}

Logcat

          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;
    }
}

2 个答案:

答案 0 :(得分: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

P.S:别忘了在模拟器上卸载应用

答案 1 :(得分:1)

好,这是工作版本:

1)在MainActivity.java的onCreate方法中检查当前主题,然后调用super.onCreate() setContentView()并将其添加到私有全局布尔变量中(让我们称之为mTheme)。更多信息:Change Activity's theme programmatically

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)方法中,将上下文分配给mListener-> mListener =(ThemeXchangeListener)context;

5)在SettingsFragment的onPreferenceChange(偏好设置,对象值)中,调用mListener.update((Boolean)value);

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

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

您可以在https://github.com/csongi77/UpdateThemeOnPreferenceChange

上查看完整的工作代码(带注释)。

它在API15(IceCreamSandwich)的仿真器上可以正常工作。请让我知道它是否有效! 最好的祝福, Cs