如何在PreferenceFragment中管理分隔符?

27

我开始使用 PreferenceFragment 处理偏好设置。这是我的代码:

my_preferences

我想要:

  1. 去掉项目之间的分隔线。我想这可以通过样式来定义,但我想不出怎么做。我尝试在运行时获取 preference ListView ,调用 findViewById(android.R.id.list), 如我在某个地方读到的那样,但它返回 null。

  2. 在标题正上方设置新的、全宽度的分隔线,如此处所示。例如,在这个例子中,我想在“Statistiche”正上方放一个全宽度的分隔线,但不要在列表顶部的“Generali”上方放置分隔线。

唯一想到的方法是使用虚拟偏好设置设置分隔线,例如:

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

<PreferenceCategory ... />

这里的主要问题是,我的PreferenceFragment(或者它所在的ActionBarActivity)具有一些左/右内边距,这会使我在preferences.xml中添加的任何分隔线不能够覆盖整个宽度。

因此,我的问题是:

  • 如何摆脱图像中所见默认的项目-项目分隔线?

  • 如何设置完全覆盖标题的全宽度分隔线,或者如何消除内部片段/活动的填充?当然,我的活动布局没有任何(显式的)填充。


你好,你得到答案了吗?如果你有的话,你能帮忙解决问题吗? - Naruto
@Naruto 简而言之,我使用了 list = findViewById(android.R.id.list) 然后调用了 list.setDivider(null)。为了避免 NPE,你需要在 onResume 中调用它,而不是在 onCreate 中。至于另一个问题,我会尽快发布完整的答案。 - natario
嘿,谢谢回复。我正在使用片段(fragment)而不是活动(activity)。即我的类看起来像public class SettingsFragment extends PreferenceFragment。那么在这种情况下,我们该如何实现呢?有什么帮助吗? - Naruto
如果我像这样调用View rootView = getView();,那么getview()会返回NULL。 - Naruto
@Naruto,请看下面我的回答。 - natario
10个回答

56

AndroidX让事情变得简单了,但我希望文档能够更好地说明。

在XML中:

要在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中以下方法以编程方式更改/删除分隔符:

@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);
}

好的回答:快速问题:1. 你知道如何在Android Studio的布局预览中显示分隔符吗?2. 是否有一种方法可以在XML主题中调用setDivider(),而不是在代码中调用?对于第二个问题,我已经尝试了“android:listDivider”和“android:divider”,但没有效果。 - Mr-IDE

28
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);

    }

4
完美!快速,简单,简洁。我遇到的其它答案(甚至根本行不通)似乎都太过冗长。谢谢! - TJ Biddle
在 Android 21 之后,使用 RecyclerView 来显示基于列表的内容,因此 R.id.list 提供了一个 RecyclerView 引用,它不能像 ListView 一样处理。 - Fattum
@Fattum 这不是我所经历的...也许这取决于使用的首选项类型? 普通 vs 支持v4等? - CamHart
@CamHart,你到底没有经历什么? - Fattum
@Fattum 我正在使用 Android > 21,而 R.id.list 仍然有效。 - CamHart
显示剩余2条评论

11

虽然有点晚,但我也遇到了首选屏幕分割器的问题,并找到了解决方案: 将自定义样式设置为托管活动,并添加到样式中:

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

实际上它所做的与通过代码设置的相同,但你可以省略一个findById,而且我认为通过样式设置更清晰


太棒了。最简单的一个。 - Alex Newman
最佳解决方案,因为它还考虑了子屏幕。 - Learn OpenGL ES

9

我完全忘记了这个问题,现在回答一下以帮助其他人。我通过将代码移动到托管我的PreferenceFragment的活动的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"/>

我已经做了,但列表内没有显示分隔符,请帮忙。谢谢! - Ben Jima
1
在API 21之后,将返回一个recyclerview,如果搜索'android.R.id.list'。在PreferenceFragmentCompat的onStart中设置setDivider(null)应该有所帮助。 - Fattum
1
你可以分享一下你的 preference_divider.xml 文件吗? - Zoker
抱歉 @Zoker,我不能。 - natario

4

在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>

没有这样的父级“PreferenceThemeOverlay.v14.Material”。 - Chandler
抱歉,您需要先在build.gradle中添加依赖项。请参见https://dev59.com/ClsX5IYBdhLWcg3wHcbA#34220290。 - formatBCE

3
我有过这个确切的问题,希望在偏好类别之间添加分隔符,但不要在项目本身之间添加。我发现被接受的解决方案通过从偏好项中删除分隔符来满足问题1,但未解决问题2并在偏好类别之间添加了分隔符。
以下是修复方法。 基本上覆盖PreferenceFragmentCompat的onCreateAdapter方法,并给它一个自定义的PreferenceGroupAdapter,该Adapter具有重写的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);
        }
    }
}

3

(仅适用于AndroidX)

Maksim Ivanov的答案让我实现了大部分功能。但是,如果要仅针对在代码中创建的特定首选项移除间隔线,则需要执行以下操作:

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)
    }
}

0

针对新的Android API 24及以上版本(2017年12月25日),提供新的解决方案。

在stackoverflow上尝试了很多方法,但都无法正常工作,或者只能在嵌套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)注册返回堆栈更改监听器。在onDestroyed方法中通过调用fragmentManager.removeOnBackStackChangedListener删除返回堆栈更改监听器。
祝你好运!

0

您可以使用此主题重新设计分隔符。

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

0

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


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