如何在Android Preference摘要中显示当前值?

476

这种情况经常出现。

当用户在Android应用程序中编辑首选项时,我希望他们能够在首选项摘要中看到当前设置的首选项值。

例如:如果我有一个Preference设置“丢弃旧消息”,指定多少天后需要清理消息。在PreferenceActivity中,我希望用户看到:

“丢弃旧消息” <- 标题

“在x天后清理消息” <- 摘要,其中x是当前首选项值

额外加分:使其可重复使用,以便我可以轻松地将其应用于所有首选项,而不考虑它们的类型(因此它使用最少的编码与EditTextPreference、ListPreference等一起使用)。

36个回答

155

如果符合您的需求,有方法使得这个解决方案更加通用。

例如,如果您想要以概述的方式通用地显示所有列表首选项的选择,则可以在您的 onSharedPreferenceChanged 实现中使用以下代码:

public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
    Preference pref = findPreference(key);

    if (pref instanceof ListPreference) {
        ListPreference listPref = (ListPreference) pref;
        pref.setSummary(listPref.getEntry());
    }
}

这很容易扩展到其他偏好类别。

通过使用PreferenceScreenPreferenceCategory中的getPreferenceCountgetPreference功能,您可以轻松编写一个通用函数来遍历偏好树,并将所需类型的所有偏好的摘要设置为它们的toString表示形式。


7
的确是一个不错的解决方案,只是缺少了初始化。 - njzk2
9
你只需要确保该活动实现了OnSharedPreferenceChangeListener接口。 - user1889890
4
注意:这里有一个更简单的方法,参见@Robertas的答案 - salmatron
7
实际上,你可以为ListPreference设置摘要为"%s",并且ListPreference.getSummary()将使用当前所选条目(如果没有选择则为"")格式化摘要。 - SOFe
findPreference(key)已经被弃用,是否有其他访问方式可用? - Nevermore

144

这是我的解决方案... 仅供参考

package com.example.PrefTest;

import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;

public class Preferences extends PreferenceActivity implements
        OnSharedPreferenceChangeListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
        PreferenceManager.setDefaultValues(Preferences.this, R.xml.preferences,
            false);
        initSummary(getPreferenceScreen());
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Set up a listener whenever a key changes
        getPreferenceScreen().getSharedPreferences()
                .registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Unregister the listener whenever a key changes
        getPreferenceScreen().getSharedPreferences()
                .unregisterOnSharedPreferenceChangeListener(this);
    }

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
            String key) {
        updatePrefSummary(findPreference(key));
    }

    private void initSummary(Preference p) {
        if (p instanceof PreferenceGroup) {
            PreferenceGroup pGrp = (PreferenceGroup) p;
            for (int i = 0; i < pGrp.getPreferenceCount(); i++) {
                initSummary(pGrp.getPreference(i));
            }
        } else {
            updatePrefSummary(p);
        }
    }

    private void updatePrefSummary(Preference p) {
        if (p instanceof ListPreference) {
            ListPreference listPref = (ListPreference) p;
            p.setSummary(listPref.getEntry());
        }
        if (p instanceof EditTextPreference) {
            EditTextPreference editTextPref = (EditTextPreference) p;
            if (p.getTitle().toString().toLowerCase().contains("password"))
            {
                p.setSummary("******");
            } else {
                p.setSummary(editTextPref.getText());
            }
        }
        if (p instanceof MultiSelectListPreference) {
            EditTextPreference editTextPref = (EditTextPreference) p;
            p.setSummary(editTextPref.getText());
        }
    }
}

@taraloca - 更改 if (p instanceof PreferenceCategory) { PreferenceCategory pCat = (PreferenceCategory) p;为 if (p instanceof PreferenceGroup) { final PreferenceGroup pCat = (PreferenceGroup) p; - Martin Ždila
这个工作得很好,如果EditTextPreferenceandroid:password="true",它会显示值。 - Chathura Wijesinghe
1
我应该如何修改代码,以便在没有定义值的情况下(即if editTextPref.getText() == ""),从资源(xml)文件中显示摘要?如何在updatePrefSummary中获取摘要值?p.getSummary()editTextPref.getSummary()已经返回了修改后的值。 - LA_
findPreference(CharSequence key)... This method was deprecated in API level 11. This function is not relevant for a modern fragment-based PreferenceActivity. - DSlomer64
最后四行是不正确的,它们总是会抛出一个“ClassCastException”。 - Chad Bingham
显示剩余5条评论

104

Android 文档 指出可以在getSummary()中使用字符串格式标记:

如果摘要中有一个字符串格式标记(即“%s”或“%1$s”),则当前条目值将替换该位置。

在ListPreference的XML声明中简单地指定android:summary =“在%s天后清理消息”对我有效。

注意:这仅适用于ListPreference


如果我错了,请有人纠正我,但是getSummary()的文档说明它是在API级别1中添加的。你是在暗示最初发布时行为不同吗? - TheIT
10
很遗憾,这仅适用于ListPreference。我希望EditTextPreference也能有相同的行为。让人惊讶的是,谷歌并没有为所有DialogPreference添加这个功能。 - Vicki
2
此外,只要值没有被设置,I就不会起作用——在首次调用Preference活动时,它只显示%s,这真的很愚蠢。 - Zordid
3
这个问题可以通过在你的ListPreference中指定默认值来解决,例如在你的xml中加入android:defaultValue="0"。这是最明智的答案,并且对我有效。 - Mohit Singh
这就是我一直在寻找的答案。至少如果你正在使用ListPreference,那么这比其他解决方案要简单/干净得多(并且它遵循标准API,这对于向前兼容性总是更可取的)。 - micheal65536
显示剩余4条评论

84
如果您使用PreferenceFragment,这是我解决它的方法。它很容易理解。
public static class SettingsFragment extends PreferenceFragment implements OnSharedPreferenceChangeListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      addPreferencesFromResource(R.xml.settings);
      getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onResume() {
      super.onResume();
      for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); ++i) {
        Preference preference = getPreferenceScreen().getPreference(i);
        if (preference instanceof PreferenceGroup) {
          PreferenceGroup preferenceGroup = (PreferenceGroup) preference;
          for (int j = 0; j < preferenceGroup.getPreferenceCount(); ++j) {
            Preference singlePref = preferenceGroup.getPreference(j);
            updatePreference(singlePref, singlePref.getKey());
          }
        } else {
          updatePreference(preference, preference.getKey());
        }
      }
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
      updatePreference(findPreference(key), key);
    }

    private void updatePreference(Preference preference, String key) {
      if (preference == null) return;
      if (preference instanceof ListPreference) {
        ListPreference listPreference = (ListPreference) preference;
        listPreference.setSummary(listPreference.getEntry());
        return;
      }
      SharedPreferences sharedPrefs = getPreferenceManager().getSharedPreferences();
      preference.setSummary(sharedPrefs.getString(key, "Default"));
    }
  }

3
这个解决方案需要更多的投票支持... 开发者网站上的示例展示了如何完成,但却完全省略了它,这让我很困扰。 - Justin Smith
3
为了使EditTextPreferences也得到更新,请在updatePreference代码中添加以下内容:如果(preference instanceof EditTextPreference) { EditTextPreference editTextPref = (EditTextPreference) preference; preference.setSummary(editTextPref.getText()); }感谢@EddieB - Eugene van der Merwe
6
你在哪里调用 unregisterOnSharedPreferenceChangeListener 方法? - CAMOBAP
1
findPreference(CharSequence key) This method was deprecated in API level 11. This function is not relevant for a modern fragment-based PreferenceActivity. - DSlomer64
2
@DSlomer64 我很清楚你引用的是什么。它告诉你不要使用 PreferenceActivity.findPreference(以及其他几种方法),因为 Google 希望你切换到这个答案中展示的技术,该技术使用 PreferenceFragment.findPreference - Dan Hulme
显示剩余6条评论

33

我的选择是扩展ListPreference,这样很简洁:

public class ListPreferenceShowSummary extends ListPreference {

    private final static String TAG = ListPreferenceShowSummary.class.getName();

    public ListPreferenceShowSummary(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ListPreferenceShowSummary(Context context) {
        super(context);
        init();
    }

    private void init() {

        setOnPreferenceChangeListener(new OnPreferenceChangeListener() {

            @Override
            public boolean onPreferenceChange(Preference arg0, Object arg1) {
                arg0.setSummary(getEntry());
                return true;
            }
        });
    }

    @Override
    public CharSequence getSummary() {
        return super.getEntry();
    }
}

然后你需要在你的settings.xml文件中添加:

<yourpackage.ListPreferenceShowSummary
    android:key="key" android:title="title"
    android:entries="@array/entries" android:entryValues="@array/values"
    android:defaultValue="first value"/>

我基于这个做出了一个解决方案。在我的解决方案中,我有一个方法来设置一个entrySummaries数组,而不是在菜单和摘要中使用entries字符串。这里有一个问题:getEntry()返回的是先前的值而不是新的值。 - pzulw
1
实际上,init()有必要吗?我已经重写了getSummary()方法,看起来它能完美工作! - Andy
2
“init()”函数是必要的,以便在用户更改偏好设置时更新“summary”。如果没有它,它将保持最初的输入。 - anthonylawson

23

您可以重写默认的Preference类并实现该功能。

public class MyListPreference extends ListPreference  {
    public MyListPreference(Context context) { super(context); }
    public MyListPreference(Context context, AttributeSet attrs) { super(context, attrs); }
    @Override
    public void setValue(String value) {
        super.setValue(value);
        setSummary(getEntry());
    }
}

以后在您的xml中,您可以使用自定义偏好设置,如下:

<your.package.name.MyListPreference 
    android:key="noteInterval"
    android:defaultValue="60"
    android:title="Notification Interval"
    android:entries="@array/noteInterval"
    android:entryValues="@array/noteIntervalValues"
    />

非常好的简单方法。只需注意像 getEntry 字符串中的 % 这样的东西,因为它们可能会引起问题(或者对我来说确实如此)。 - Andrew

21

经过几个小时的努力解决问题后,我实现了以下代码:

[更新:最终版本清单]

public class MyPreferencesActivity extends PreferenceActivity {
    ...
    ListPreference m_updateList;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);

        m_updateList = (ListPreference) findPreference(getString(R.string.pref_update_interval_key));
        String currentValue = m_updateList.getValue();
        if (currentValue == null) {
            m_updateList.setValue((String)m_updateList.getEntryValues()[DEFAULT_UPDATE_TIME_INDEX]);
            currentValue = m_updateList.getValue();
        }
        updateListSummary(currentValue);    

        m_updateList.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                updateListSummary(newValue.toString());
                return true;
            }       
        });     
    }

    private void updateListSummary(String newValue) {
        int index = m_updateList.findIndexOfValue(newValue);
        CharSequence entry = m_updateList.getEntries()[index];
        m_updateList.setSummary(entry);
    }
}

那是我唯一行之有效的解决方案。 在此之前,我尝试从ListPreferences进行子类化并实现 android:summary="bla bla bla %s"。都没有起作用。


你的方式比其他人更清晰,代码也更少。最好的答案使用“代理”——首选项管理器。但它不如直接侦听列表中的更改那么清晰。 - Paul Annekov
@Subtle Fox,不错的解决方案。只是好奇如果你做了“setDefaultValue()”,为什么要检查(ss==null)。 - Samuel
因为 setDefaultValue() 不应该在那里 :) 我在重构之前放置了这段代码。 - Subtle Fox

21

也许像ListPreference一样:修改getSummary以获取您想要的内容:

package your.package.preference;

import android.content.Context;
import android.util.AttributeSet;

public class EditTextPreference extends android.preference.EditTextPreference{
        public EditTextPreference(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

        public EditTextPreference(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public EditTextPreference(Context context) {
            super(context);
        }

        @Override
        public CharSequence getSummary() {
            if(super.getSummary() == null) return null;

            String summary = super.getSummary().toString();
            return String.format(summary, getText());
        }
    }

并在您的XML中使用此代码:

<your.package.EditTextPreference
                android:key="pref_alpha"
                android:summary="Actual value: %s"
                android:title="Title"
                android:defaultValue="default"
                />

所以您可以使用%s代替实际值来撰写摘要。


1
@Eruvanos @serv-inc 至少在 android.support.v7.preference.EditTextPreference 的子类中,当首选项更改时,它不会更新摘要,只有在首选项活动创建时才会更新。在不实现首选项片段类中的 OnSharedPreferenceChangeListener.onSharedPreferenceChanged 的情况下解决此问题的方法是还要覆盖 android.support.v7.preference.EditTextPreference.setText(String text)@Override public void setText(String text) { super.setText(text); this.setSummary(this.getText()); } - PointedEars
@PointedEars:已完成。但这似乎也是 https://dev59.com/P3RB5IYBdhLWcg3wvpic#21906829 的副本。 - serv-inc

18

以下是你需要设置摘要所需的代码。 它还在启动时设置值并尊重默认值,不仅在更改时。只需将“R.layout.prefs”更改为您的xml文件,并根据需要扩展setSummary方法即可。 它实际上只处理ListPreferences,但很容易自定义以尊重其他Preferences。

package de.koem.timetunnel;

import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;

public class Prefs 
    extends PreferenceActivity 
    implements OnSharedPreferenceChangeListener {

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

       this.addPreferencesFromResource(R.layout.prefs);
       this.initSummaries(this.getPreferenceScreen());

       this.getPreferenceScreen().getSharedPreferences()
           .registerOnSharedPreferenceChangeListener(this);
    }

  /**
    * Set the summaries of all preferences
    */
  private void initSummaries(PreferenceGroup pg) {
      for (int i = 0; i < pg.getPreferenceCount(); ++i) {
          Preference p = pg.getPreference(i);
          if (p instanceof PreferenceGroup)
              this.initSummaries((PreferenceGroup) p); // recursion
          else
              this.setSummary(p);
      }
  }

  /**
    * Set the summaries of the given preference
    */
  private void setSummary(Preference pref) {
      // react on type or key
      if (pref instanceof ListPreference) {
          ListPreference listPref = (ListPreference) pref;
          pref.setSummary(listPref.getEntry());
      }
  }

  /**
    * used to change the summary of a preference
    */
  public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
     Preference pref = findPreference(key);
     this.setSummary(pref);
  }

  // private static final String LOGTAG = "Prefs";
}

koem

:koem

非常感谢!我从这个创建了一个类,将资源ID作为参数传递给onCreate()方法--这样一来,在我的设置界面中显示摘要值只需要更改一行代码。 - Asmo Soinio
2
顺便提一下:请注意,非持久化的首选项不会使用此系统进行更新。必须使用单个首选项的 setOnPreferenceChangeListener(Preference.OnPreferenceChangeListener onPreferenceChangeListener) 方法监听它们的更改。 - Asmo Soinio

12
根据Android文档,您可以在ListPreference和EditTextPreference组件中使用app:useSimpleSummaryProvider="true"

1
如果您使用androidx.preference.EditTextPreference,那么这实际上应该是被接受的答案。 - Roar Grønmo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接