2010-04-12 3 views
8

У меня есть PreferenceActivity с кучей (Sub) PreferenceScreens. Каждый такой (Sub) PreferenceScreen представляет собой учетную запись и имеет имя учетной записи в качестве ее названия.Обновите существующий элемент предпочтений в PreferenceActivity после возврата из (sub) PreferenceScreen

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 ! может испытывать ту же проблему, что и я.

Любая помощь приветствуется.

ответ

16

Я нашел решение. У меня есть иерархия, как это, каждый из них является PreferenceScreen:

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

В списке пользователей, название подэкрана зависит от настроек пользователя. Теперь, когда я создаю список пользователей, я сохраняю адаптер списка переменной в моей PreferenceActivity.

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

Теперь, когда userX-настройки редактируются, я поставил названия в usersListScreen и после этого вызова:

userScreenListAdapter.notifyDataSetChanged(); 

который обновляет список UI и изменения видны.

+0

Ничего себе, звучит как путь. На данный момент у меня нет SDK для Android, но я возьму ваше слово за то, что он работает. Большой! – aioobe

+0

Хорошее обходное решение для ошибки - спасибо! – James

+0

это также сбросит все изменения, внесенные в предпочтения в фрагменте предпочтения (например, слушатели) после вызова! (или в других изменениях слова, сделанных от вызова этого до тех пор, пока он не закончит восстановление, будет потеряно :)) – ceph3us

1

Опытная эта проблема, но onContentChanged() не работает для меня. Моя проблема связана с PreferenceScreens, которые находятся на глубине более одного уровня от корня.

Чтобы следовать вашему примеру, если вы сначала создали экран настроек «Учетные записи», а затем добавили к нему каждый объект PreferenceScreen вашей индивидуальной учетной записи. Как это:

 
Root Screen 
    -> "Accounts" screen 
    -> "[email protected]" screen 
     -> edit username 
     -> edit password 
     -> etc... 
    -> "[email protected]" screen 
    -> "[email protected]" screen 
    -> etc... 

Если пользователь отредактирован свое имя пользователя и нажал сохранить, вызывая PreferenceActivity.onContentChanged(), кажется, влияют только прямые потомки коренных PreferenceScreen. Заголовки и резюме экранов третьего поколения не перерисовываются, все еще отражающие старые значения.

Просматривая код для onContentChanged(), похоже, что он просто повторно привязывает() к корневому экрану к ListView ListActivity, хотя я не думаю, что последующие PreferenceScreens привязаны к ListView (они?), поэтому мы не можем вручную повторно связать что-либо ...

Единственным обходным решением, которое я могу придумать, было бы создание подменю как изолированного PreferenceActivitys вместо PreferenceScreens, поэтому мы можем намеренно вызвать onContentChanged() на нашего прямого предка. Но это даже больше, чем текущее обходное решение. Есть идеи?

+1

Обходной способ, с которым я работал, состоит в том, чтобы перегрузить метод onResume и перестроить интерфейс внутри него. – aioobe

+0

aioobe, не могли бы вы привести пример кода, как вы это делаете? –

-1

Это работает для меня, вы должны захватить основной диалог вашего экрана предпочтений и установить заголовок оттуда, очень просто.

somePrefScreen.getDialog().setTitle("Whatever you want"); 
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 
    } 
} 
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 
} 
+0

Ну, угадайте, что ... они оставили предпочтения ** Из библиотеки поддержки ** !! Итак, для более старых уровней API вам все равно нужно использовать PreferenceActivity :( –

1

Я просто положить

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

сразу после обновления резюме моего родительского элемента предпочтения.

+0

нет getRootAdapter на базе поддержки lib – Frank

3

notifyDataSetChanged() - правильное решение. Но я хочу добавить рекурсивный итератор для всех PreferenceScreens, поскольку у меня есть проблема, чтобы найти настоящего родителя предпочтения. Для сложной структуры предпочтений, я рекомендую этот убийца-код:

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()); 
1

это может быть поздно ответ, но все-таки ... Я на это прямо сейчас :)

Так я достиг этого, является то, что вы можете подключить в PreferenceFragmentCompat «s 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.onResume жизненного цикла содержащейся активности.

это хорошо, что он также работает, конечно, с FragmentManager «s Сделки.

Надеюсь, что это поможет, счастливое кодирование! :)