如何管理 PreferenceFragment 中的分隔符?

How to manage dividers in a PreferenceFragment?

我在 PreferenceFragment 开始处理偏好。这是我拥有的:

我正在尝试:

  1. 取消项目之间的分隔线。我想这可以从样式中定义,但我不知道如何定义。我试着得到 偏好 ListView 在运行时调用 findViewById(android.R.id.list),正如我在某处读到的,但它 returns 空。

  2. 在 headers 的顶部设置新的 全宽 分隔线,如 here 所示。例如,在这种情况下,我希望在 "Statistiche" 上方有一个全宽分隔线,但不在列表顶部的 "Generali" 上方。

我想到的唯一方法是将分隔线设置为虚假偏好,例如:

<Preference
    android:layout="@layout/divider" //here I set width and a divider resource
    />

<PreferenceCategory ... />

这里的主要问题是我的 PreferenceFragment(或它所在的 ActionBarActivity)有一些 left/right 填充,这使得我添加到 preferences.xml 中的任何分隔符 覆盖整个宽度。

所以我的问题是:

PreferenceFragment下添加这段代码:

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // remove dividers
        View rootView = getView();
        ListView list = (ListView) rootView.findViewById(android.R.id.list);
        list.setDivider(null);

    }

完全忘记了这个问题,post现在可以回答以帮助其他人。我通过将我的代码移动到承载我的 PreferenceFragment 的 activity 的 onResume() 方法中解决了这个问题。我认为还有其他几个点,您可以使用 findViewById(android.R.id.list).

调用非空 ListView
public boolean mListStyled;

@Override
public void onResume() {
    super.onResume();
    if (!mListStyled) {
        View rootView = getView();
        if (rootView != null) {
            ListView list = (ListView) rootView.findViewById(android.R.id.list);
            list.setPadding(0, 0, 0, 0);
            list.setDivider(null);
            //any other styling call
            mListStyled = true;
        }
    }
}

您可能可以去掉 rootView 检查,但同时您甚至可能想要检查 list != null。反正我没遇到过这种NPE。

因此,setDivider(null) 取消项目分隔线。我设法通过以下方式添加了覆盖整个屏幕宽度的部分分隔符:

  • 正在删除 list 中的填充;
  • 在我的 XML 中添加自定义首选项:

n

 <Preference
     android:title="divider"
     android:selectable="false"
     android:layout="@layout/preference_divider"/>

虽然有点晚了,但我在 preff 屏幕中遇到了与分隔线相同的问题,并找到了这个解决方案: 将自定义样式设置为托管 activity 并添加到样式:

   <item name="android:listDivider">@null</item>

它实际上与通过代码设置一样,但你保存一个 findById,我认为它看起来更清晰设置槽样式

对于 PreferenceFragmentCompat,ListView 不起作用。 但这就像一个魅力:

<style name="PrefsTheme" parent="PreferenceThemeOverlay.v14.Material">
    <item name="android:divider">@null</item>
    <item name="android:dividerHeight">0dp</item>
</style>

将此添加到您的应用主题中:

<item name="preferenceTheme">@style/PrefsTheme</item>

如果你使用 PreferenceFragment,你可以使用 ListView。setDivider(null); 如果您使用 PreferenceFragmentCompat ,则可以使用 PreferenceFragmentCompat.setDivider(Drawable divider)setDividerHeight(int height);

新 android API 24 岁及以上(2017 年 12 月 25 日)的新解决方案。

我在 Whosebug 上尝试了很多方法后发现,但没有用,或者它只是工作但在 nestest PreferenceScreen.

中不起作用

首先,您需要从当前显示的片段中找到listview,并移除分隔线:

private fun removeDividerInCurrentFragment() {
    this@YourPreferenceActivity.fragmentManager.findFragmentById(android.R.id.content)?.let {
        it.view?.findViewById<ListView?>(android.R.id.list)?.let {
            it.divider = null
            it.dividerHeight = 0
        }
    }
}

其次,要在提交片段时删除分隔符,请调用上面的方法(removeDividerInCurrentFragment)删除列表视图的分隔符。

确定您是否有 嵌套测试 PreferenceScreen。通过实现 FragmentManager.OnBackStackChangedListener 协议:

PreferenceActivity 中的片段发生变化时注册监听器
class YourPreferenceActivity : PreferenceActivity(), FragmentManager.OnBackStackChangedListener {
    override fun onBackStackChanged() {
        this@YourPreferenceActivity.removeDividerInCurrentFragment()
    }
}

最后,在onCreate调用fragmentManager.addOnBackStackChangedListener(this@ YourPreferenceActivity)注册backstack changes listener。并通过在 onDestroyed 方法中调用 fragmentManager.removeOnBackStackChangedListener 来删除 backstack 更改侦听器。

祝你好运!

您可以使用此主题重新设置分隔线的样式。

<style name="PreferenceFragment.Material">
    <item name="android:divider">@drawable/preference_list_divider_material</item>
</style>

我遇到了这个确切的问题,并且想要在偏好类别之间而不是在项目本身之间进行分隔。我发现接受的解决方案通过从偏好项中删除分隔线来满足问题 1,但没有修复问题 2 并在偏好类别之间添加分隔线。

修复以下问题。 基本上重写 PreferenceFragmentCompat 的 onCreateAdapter 方法,并为其提供一个自定义 PreferenceGroupAdapter,该方法具有重写的 onBindViewHolder 方法,该方法使用位置以及您需要为每个视图持有者设置上下权限的任何其他内容。当两个视图持有者都允许它们之间有分隔线时,将绘制分隔线。

这是我的修复方法

public class SettingsFragment extends PreferenceFragmentCompat {

    @Override
    protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
        return new CustomPreferenceGroupAdapter(preferenceScreen);
    }

    static class CustomPreferenceGroupAdapter extends PreferenceGroupAdapter {

    @SuppressLint("RestrictedApi")
    public CustomPreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
        super(preferenceGroup);
    }

    @SuppressLint("RestrictedApi")
    @Override
    public void onBindViewHolder(PreferenceViewHolder holder, int position) {
        super.onBindViewHolder(holder, position);
        Preference currentPreference = getItem(position);
        //For a preference category we want the divider shown above.
        if(position != 0 && currentPreference instanceof PreferenceCategory) {
            holder.setDividerAllowedAbove(true);
            holder.setDividerAllowedBelow(false);
        } else {
            //For other dividers we do not want to show divider above 
            //but allow dividers below for CategoryPreference dividers.
            holder.setDividerAllowedAbove(false);
            holder.setDividerAllowedBelow(true);
        }
    }
}

AndroidX 让它变得简单,但我希望它有更好的文档记录。

在XML

add/remove XML 中首选项之间的分隔符,使用以下属性:

<androidx.preference.PreferenceScreen
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <Preference
        ...
        app:allowDividerAbove="true/false"
        app:allowDividerBelow="true/false"
        ... />

</androidx.preference.PreferenceScreen>

请注意,如果顶部分隔线 allowDividerBelow 设置为 true 且底部分隔线 allowDividerAbove 设置为 true,则分隔线只会在两个首选项之间显示.

在代码中

您还可以在 PreferenceFragmentCompatonActivityCreated 中使用以下方法以编程方式 change/remove 分隔符:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    // To remove:
    setDivider(null);

    // To change:
    setDivider(ContextCompat.getDrawable(getActivity(), R.drawable.your_drawable));
    setDividerHeight(your_height);
}

(仅限 AndroidX)

让我完成了大部分工作。但是要仅针对代码中创建的特定首选项删除分隔符,我必须这样做:

val pref = object : Preference(activity) {
    override fun onBindViewHolder(holder: PreferenceViewHolder) {
        super.onBindViewHolder(holder)
        // By default, preferences created in code show dividers
        holder.setDividerAllowedAbove(false)
        holder.setDividerAllowedBelow(false)
    }
}