从(子)PreferenceScreen返回时更新PreferenceActivity中的现有Preference项

时间:2010-04-12 20:34:35

标签: android preferences invalidation preferenceactivity

我有一系列(Sub)PreferenceScreens的PreferenceActivity。每个此类(子)首选项屏幕代表一个帐户,并将帐户用户名作为其标题。

PreferenceScreen root = mgr.createPreferenceScreen(this);
for (MyAccountClass account : myAccounts) {
    final PreferenceScreen accScreen = mgr.createPreferenceScreen(this);

    accScreen.setTitle(account.getUsername());

    // add Preferences to the accScreen
    // (for instance a "change username"-preference)
    ...

    root.add(accScreen);
}

当用户输入子PreferenceScreen并编辑帐户用户名时,我希望外部PreferenceScreen更新它的相关帐户的PreferenceScreen标题。

我试图添加......

usernamePref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        accScreen.setTitle(newValue.toString());
        return true;
    }
});

...但是accScreen.setTitle似乎对外部PreferenceScreen没有生效。我注意到调用onContentChanged();实际上使它工作,但我意识到这可能不是这样做的首选方式。

我怀疑我应该在某个地方调用postInvalidate(),但我真的无法弄清楚什么视图以及何时进行操作。

PreferenceScreen android:summary update !可能遇到与我相同的问题。

任何帮助表示感谢。

8 个答案:

答案 0 :(得分:16)

我找到了解决方法。我有这样的层次结构,每个都是一个PreferenceScreen:

main settings
  -> users list
    -> user1 settings
    -> user2 settings
    ...

在用户列表中,子屏幕的标题取决于用户设置。现在,当我创建用户列表时,我将列表适配器存储到PreferenceActivity中的变量。

 PreferenceScreen usersListScreen = ...
 userScreenListAdapter = (BaseAdapter)usersListScreen.getRootAdapter();

现在编辑userX-settings时,我在usersListScreen中设置了标题,然后在该调用之后:

userScreenListAdapter.notifyDataSetChanged();

更新列表UI,更改可见。

答案 1 :(得分:3)

notifyDataSetChanged()是正确的解决方案。但是我想为所有PreferenceScreens添加递归迭代器,只要我遇到问题就找到Preference的真正父级。对于复杂的首选项结构,我推荐这个杀手级代码:

private void updateAll_PrefereneScreens(PreferenceGroup group) {
    if (group instanceof PreferenceScreen) {
        BaseAdapter adapter = (BaseAdapter) ((PreferenceScreen) group).getRootAdapter();
        adapter.notifyDataSetChanged();
    }
    for (int i=0; i<group.getPreferenceCount(); i++) {
        Preference pref = group.getPreference(i);
        if (pref instanceof PreferenceGroup) {
            updateAll_PrefereneScreens((PreferenceGroup) pref);
        }
    }
}

我在每个 setSummary()之后调用它以确保它正常工作:

findPreference("KEY").setSummary(str);
updateAll_PrefereneScreens(getPreferenceScreen());

答案 2 :(得分:2)

这可能是一个迟到的答案,但仍然......我现在正在谈论它:)

我实现它的方式是,您可以使用其生命周期的 PreferenceFragmentCompat onResume()方法,并通过重置其值来手动更新所需字段

注意:在此示例中,我还会跟踪已编辑的首选项的索引,以避免重置每个首选项。

    // let's say you need to update title of the parent screen
    // when back from sub-screen(s) edition

    private int edited = -1;

    // this gets called everytime you get back to the parent screen
    @Override
    public void onResume ()
    {
        super.onResume();
        if ( edited != -1 )
        {
            PreferenceScreen root = getPreferenceScreen();
            Preference preference = root.getPreference( edited );

            if ( preference != null )
            {
                String updatedValue = getPreferenceManager()
                .getSharedPreferences()
                .getString( "your-preference-key", "your-default-value" );

                preference.setTitle( updatedValue );
            }

            edited = -1;
        }
    }

    // everytime you are about to navigate to a sub-screen 
    @Override
    public boolean onPreferenceTreeClick ( Preference preference )
    {
        // beware to save it first
        if ( preference instanceof MySubScreenPreference )
        {
            edited = preference.getOrder();
        }

        return super.onPreferenceTreeClick( preference );
    }

如文档中所述:

  

的onResume

     

当片段对用户可见并且正在运行时调用。这通常与包含Activity的生命周期的Activity.onResume相关联。

当然,它也适用于 FragmentManager 交易

希望这有帮助,快乐编码! :)

答案 3 :(得分:1)

遇到同样的问题,但onContentChanged()对我不起作用。我的问题是PreferenceScreens从根目录深度超过一级。

要按照您的示例,如果您首先创建了“帐户”首选项屏幕,然后在其下添加了每个单独的帐户PreferenceScreen对象。像这样:

Root Screen
  -> "Accounts" screen
    -> "foo@example.com" screen
      -> edit username
      -> edit password
      -> etc...
    -> "bar@example.com" screen
    -> "baz@example.com" screen
    -> etc...

如果用户编辑了他们的用户名并单击了save,则调用PreferenceActivity.onContentChanged()似乎只会影响根PreferenceScreen的直接后代。第三代屏幕的标题和摘要没有重新绘制,仍然反映了旧的价值观。

查看onContentChanged()的代码,看起来它只是将根屏幕重新绑定()到ListActivity的ListView,虽然我不认为后续的PreferenceScreens曾经绑定到ListView(是吗?) ,所以我们不能手动重新绑定任何东西......

我能想到的唯一解决方法是将子菜单创建为独立的PreferenceActivity而不是PreferenceScreens,因此我们可以故意在我们的直接祖先上调用onContentChanged()。但这比目前的解决方案更像是一个混乱。有什么想法吗?

答案 4 :(得分:1)

PreferenceActivity#onContentChanged()将刷新整个屏幕,发生一些 闪烁效果

但是,您可以使用PreferenceActivity#onPreferenceTreeClick(...)方法在给定的偏好设置上实现相同的目标选择性

请注意,此方法现已弃用:请考虑使用fragments,这似乎解决了自定义首选项的许多问题(我还没有测试过自己)。旧版SDK有a compatibility package

public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {

Log.v(TAG, "onSharedPreferenceChanged(...," + key + ")");

if (key.equals("myPref")) {
    onPreferenceTreeClick(getPreferenceScreen(), getPreferenceManager().findPreference("myPref"));
    Log.v(TAG, "Do whatever else you need...");
}

//onContentChanged();    // this could be used but occurs screen flickering
}

答案 5 :(得分:1)

我只是把

((BaseAdapter)getPreferenceScreen().getRootAdapter()).notifyDataSetChanged();

在更新我的父偏好项目的摘要后立即。

答案 6 :(得分:0)

Source

// Import required classes (Win: CTRL+SHIFT+O & Mac: CMD+SHIFT+O)
public class YourCustomPreference extends PreferenceActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }

    // some logic goes above, when you want to reset value and update
    // EditTextPreference value. For convenience, I am going to wrap two
    // different task in different methods
    private void resetPreferenceValue() {
        SharedPreferences sharedPref = PreferenceManager
                .getDefaultSharedPreferences(this.getApplicationContext());
        // Get preference in editor mode
        SharedPreferences.Editor prefEditor = sharedPref.edit();
        // set your default value here (could be empty as well)
        prefEditor.putString("your_edit_text_pref_key", "DEFAULT-VALUE");
        prefEditor.commit(); // finally save changes
        // Now we have updated shared preference value, but in activity it
        // still hold the old value
        this.resetElementValue();
    }

    private void resetElementValue() {
        // First get reference to edit-text view elements
        EditTextPreference myPrefText = (EditTextPreference) super
                .findPreference("your_edit_text_pref_key");
        // Now, manually update it's value to default/empty
        myPrefText.setText("DEFAULT-VALUE");
        // Now, if you click on the item, you'll see the value you've just
        // set here
    }
}

答案 7 :(得分:-1)

这对我有用,你必须抓住PreferenceScreen的底层对话框并从那里设置标题,非常简单。

somePrefScreen.getDialog().setTitle("Whatever you want");