您如何刷新 PreferenceActivity 以显示设置的更改?

Posted

技术标签:

【中文标题】您如何刷新 PreferenceActivity 以显示设置的更改?【英文标题】:How do you refresh PreferenceActivity to show changes in the settings? 【发布时间】:2011-12-21 15:10:08 【问题描述】:

根据下面的代码,你能告诉我如何刷新 PreferenceActivity 窗口以立即显示设置的更改吗?例如:用户点击主编钟切换复选框为真(勾选),我希望用户立即看到其他设置,例如 ChimeOn15Past 复选框也为真(勾选)

SharedPreferences.Editor prefEditor = clockSettings.edit(); // Allow the settings to be changed.

if (booleanMasterChimeToggle == true) 
    prefEditor.putBoolean("ChimeOnTheHour", true);
    prefEditor.putBoolean("ChimeOn15Past", true);
    prefEditor.putBoolean("ChimeOn30Past", true);
    prefEditor.putBoolean("ChimeOn45Past", true);

    strNotifyMessage = "Full chiming has now been set.";

 else 
    prefEditor.putBoolean("ChimeOnTheHour", false);
    prefEditor.putBoolean("ChimeOn15Past", false);
    prefEditor.putBoolean("ChimeOn30Past", false);
    prefEditor.putBoolean("ChimeOn45Past", false);

    strNotifyMessage = "Full chiming has now been disabled.";

【问题讨论】:

【参考方案1】:

代替

finish();
startActivity(getIntent());

我更喜欢下面的代码:

setPreferenceScreen(null);
addPreferencesFromResource(R.xml.preferences);

这也将重置显示,但没有整个完成过程及其后果。 此外,此代码适用于 PreferenceActivityPreferenceFragment

例如,如果您想动态更改语言环境值,这很有趣。在这种情况下,与经理合作是不够的,因为您需要完全重新加载标题和值。

编辑:setPreferenceScreenPreferenceActivity 中已弃用(仍在工作)但不在PreferenceFragment

【讨论】:

我用我的一个例子进行了尝试:在首选项屏幕中更改语言环境。而且效果很好。我没有看到副作用... 这正是我需要解决一个不同但类似的问题。如果用户在PreferenceActivity 中更改preference 然后导航回来,我想确保嵌入的PreferenceFragment 会更新。刚刚在onResume 中使用了您的代码。 这帮助我解决了我需要在应用内购买成功后刷新偏好屏幕的问题...谢谢! @Mr_and_Mrs_D,是的;屏幕已重置,因此如果您在其中部分滚动,这将在您更改首选项时使其滚动回顶部。 当您知道需要刷新特定首选项时,您可以再次绑定对特定首选项的调用 bindPreferenceSummaryToValue()。不过,这整个思路对我来说似乎有点矫枉过正。【参考方案2】:

尼古拉的回答是正确的。我只是想在这里添加一些代码来更清楚地说明他的观点。

private CheckBoxPreference mOn15Past;
private CheckBoxPreference mOn30Past;
private CheckBoxPreference mOn45Past;

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

    // Load the preferences from an XML resource
    addPreferencesFromResource(R.xml.preferences);

    mOn15Past = (CheckBoxPreference) findPreference("ChimeOn15Past");
    mOn30Past = (CheckBoxPreference) findPreference("ChimeOn30Past");
    mOn45Past = (CheckBoxPreference) findPreference("ChimeOn45Past");

    final Preference chimeMaster = findPreference("ChimeMaster");
    chimeMaster.setOnPreferenceChangeListener(new OnPreferenceChangeListener() 
        @Override
        public boolean onPreferenceChange(Preference preference, Object newVal) 
            final boolean value = (Boolean) newVal;
            mOn15Past.setChecked(value);
            mOn30Past.setChecked(value);
            mOn45Past.setChecked(value);
            return true;
        

    );

简而言之,PreferenceActivity 并非旨在在启动后从持久存储中刷新其值。与其像在代码中那样使用SharedPreferences.Editor 来修改和提交额外的更改,不如在本地PreferenceManager 对象中进行更改,该对象将由PreferenceActivity 在其正常生命周期中提交。

【讨论】:

这是“正确”的做法。让 PreferenceScreen 管理自己的刷新,而不是与 SharedPreferences 冲突并手动刷新 UI。【参考方案3】:

有一种简单的方法可以一次刷新列表中的所有项目。只需执行以下操作:

getPreferenceScreen().removeAll();
addPreferencesFromResource(R.xml.preferences);

使用这种方法,您不会失去 ListView 的位置。

【讨论】:

确保正确设置依赖项的任何设置(父级优先),否则这将导致IllegalStateException 这将删除您偏好的监听器(或任何附加的)。 不错,但是如果打开偏好子屏幕,它就没有效果了。【参考方案4】:

实现onPreferenceChange 并从那里切换相关性能。要“刷新”整个活动,您需要finish() 并重新启动它。

【讨论】:

感谢您的回复。我可以从可以调用 PreferecActivity 方法“RefreshMe”的小部件中执行此操作吗?如果答案是肯定的,您能告诉我执行此操作所需的代码吗?谢谢。 并非如此。您只能从小部件启动 Activity,您需要从 Activity 进行处理。为什么要从小部件执行此操作? 因为用户可能之前打开了我的应用设置屏幕,然后按下主屏幕按钮快速返回主屏幕。当天晚些时候,用户可能会使用小部件来关闭主铃声开/关切换,然后如果用户点击应用程序图标来调用应用程序,则偏好屏幕将成为用户查看的最后一个屏幕。我希望该屏幕能反映用户在小部件的主铃声开/关切换中所做的更改。 首选项是从磁盘加载的,因此如果保存正确,您的首选项活动应该会反映更改。 首选项保存在 AppWidgetProvider 中的 PreferenceEditor 中。目前,用户查看 PreferenceActivity 更改的唯一方法是使用设备后退按钮并再次调用 PreferenceActivity。有没有办法从 AppWidgetProvider 刷新屏幕?【参考方案5】:

您可以简单地调用 onCreate 方法。如果你愿意,你可以调用 onStart 和 onResume 方法。

onCreate(null);

我已经用这种方法解决了我的问题。

【讨论】:

【参考方案6】:

经过一天的努力,我想通了。 这是针对多个级别的偏好。

parent 是包含您尝试更新的首选项的首选项屏幕。

由于设置屏幕显示为对话框,您可以从对话框中获取列表视图,然后告诉它的适配器更新 child

PreferenceScreen parent  // The screen holding the child
PreferenceScreen child   // The entry changing

child.setSummary(isEnabled?"Enabled":"Not Enabled");
ListView v = (ListView)parent.getDialog().findViewById(android.R.id.list);
BaseAdapter ba = (BaseAdapter)v.getAdapter();
ba.notifyDataSetChanged();

我发现 R.id.list 在旧手机上不可用,但它有效

    ListAdapter adapter = parent.getRootAdapter();
    if (adapter instanceof BaseAdapter) 
        ((BaseAdapter)adapter).notifyDataSetChanged();
    

【讨论】:

无法使用 notifyDataSetChanged() 更新设置子屏幕【参考方案7】:

这个类表示简单的偏好屏幕。这可能对你有帮助。

public class PreferenceActivitySettings extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener 
        private SharedPreferences preferences;
        @SuppressWarnings("deprecation")
        @Override
        public void onCreate(Bundle savedInstanceState) 
            super.onCreate(savedInstanceState);
            addPreferencesFromResource(R.xml.settings);
            preferences = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
            preferences.registerOnSharedPreferenceChangeListener(this);
        

        @SuppressWarnings("deprecation")
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) 
            setPreferenceScreen(null);
            addPreferencesFromResource(R.xml.settings);
        
    

【讨论】:

【参考方案8】:

我设法编写了一个实际上非常简单的方法,并且不需要使 ListViews 或任何可能的情况无效。此方法调用public ViewGroup removeView(View view)View requestLayout()View forceLayout()ViewGroup addView(View view),最后调用Fragment OnCreate(Bundle savedInstanceState) 方法并进行一些空检查和一个final View 临时变量来存储实际视图视图被移除。

这种方法仍然很新,我将在接下来的 2 天内对其进行一些调整和改进,但它可以完美地工作,没有任何副作用。由于我编写的包含代码的类更改了整个 rom 的实际首选项标题和摘要文本样式(这是我添加的选项的重点),我还添加了一个公共构造函数,这些类是位于首选项样式屏幕下方的活动堆栈中的活动,因此可以同时更新它们。我很可能会将这个新方法移到框架视图类中,以便在系统范围内使用它。

public void reLoadView(View view) 
    if (view == null) 
        view = mView;
        Log.i(TAG, "The current View is: " + view);
    
    final View tmpView = view;
    try 
        setStyleChanged(1);
        setReset(0);
        Log.i(TAG, "The Temporary View is: " + tmpView);
        Log.i(TAG, "The current View is: " + mView);
        Log.i(TAG, "The current ViewGroup is: " + mViewGroup);
        if (mView != null) 
            if (mViewGroup != null) 
                mActivity.runOnUiThread(new Runnable() 
                    @Override 
                    public void run() 
                        mViewGroup.removeView(mView);
                        mView.requestLayout();
                        mView.forceLayout();
                        mViewGroup.addView(tmpView);
                        onCreate(new Bundle());
                     
                );
            
        
        View tmp = null;
        mDemented.reLoadView(tmp);
     catch (Exception e) 
    

视图变量本身最初在类初始化中设置,并在View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)View onViewCreated(View view, Bundle savedInstanceState) 中初始化和定义。视图变量本身最初是在类初始化中设置的。

private View mView;
private ViewGroup mViewGroup;

private int mLayoutResId = R.layout.preference_list_fragment;//holds the layout reference just to keep it clean
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) 
    View rootView = inflater.inflate(mLayoutResId, parent, false);
    mViewGroup = parent;
    return rootView;


@Override
public void onViewCreated(View view, Bundle savedInstanceState) 
    mView = view;

在下面堆栈中的活动中,我的 PreferenceStyle 类,我设置了一个空的构造函数,它可以在应用程序的任何地方初始化。

public class DEMENTED extends SettingsPreferenceFragment implements
        Preference.OnPreferenceChangeListener, View.OnClickListener 

    public DEMENTED() 
         super();
    

    //other code to do whatever here

在我的 PreferenceStyle 类中,我导入了我的 DEMENTED 类,然后将其设置为 onCreate(Bundle savedInstanceState) 之前的变量:

private DEMENTED mDemented;

然后初始化onCreate(Bundle savedInstanceState)中的变量:

mDemented = new DEMENTED();

调用我的 DEMENTED 类以重新加载其视图是在我的 reloadView(View view) 方法中完成的,但用于进行调用的变量视图是在调用之前立即设置的视图变量并设置为 null:

View tmp = null;
mDemented.reLoadView(tmp);

我的 DEMENTED 类检查方法调用中包含的 View 是否为 null,如果是,则将其设置为本地化 View 变量,以便该方法可以使用局部变量进行工作:

public void reLoadView(View view) 
    if (view == null) 
        view = mView;
        Log.i(TAG, "The current View is: " + view);
    
    //all the other good stuff here

基本上,这使用 onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) 中定义的 ViewGroup:

    mViewGroup = parent;

View 变量设置在onViewCreated(View view, Bundle savedInstanceState)

    mView = view;

希望这对某人有所帮助,因为这是我在这里一遍又一遍地看到的一个问题,而且我基本上还没有在不涉及用作实用程序的多个类的任何地方找到一个可靠的解决方案。如果有人有任何意见建议以简化或任何意见,请随时发表评论。

【讨论】:

【参考方案9】:

在您提交对 sharedPreferences 的更改后,我认为您应该调用 invalidateHeaders() 。关于它的文档:

当您需要更改正在显示的标题时调用。将导致 在 onBuildHeaders() 之后被调用以检索新列表。

这应该会刷新 UI,以显示新值。

【讨论】:

请在您的答案中添加一些解释 - 为什么您认为它可以解决问题? @NicoHaase OK 更新了答案。文档解释了为什么它应该刷新 UI。 @nandur93 如所写:“当您需要更改正在显示的标题时”。我认为它应该在 UI 线程上。【参考方案10】:

如果您的目标是 API 11 及更高版本,您可以使用invalidateHeaders

来自文档:

当您需要更改正在显示的标题时调用。将导致 在 onBuildHeaders() 之后被调用以检索新列表。

【讨论】:

没有为我更改 Android 4.3 中活动中显示的设置值。 检查你的 onBuildHeaders() 方法,这是你应该放置你的头初始化代码的地方,然后 invalidateHeaders 应该可以工作

以上是关于您如何刷新 PreferenceActivity 以显示设置的更改?的主要内容,如果未能解决你的问题,请参考以下文章

Android之设置页面(PreferenceActivity使用)

PreferenceActivity:将值保存为整数

在 API 15 及更高版本中使用 PreferenceActivity

android中SharedPreferences和PreferenceActivity的存取数据

PreferenceActivity 中的 ActionBar

过时的PreferenceActivity导致Fragment显示问题