如何管理 PreferenceFragment 中的分隔符?

Posted

技术标签:

【中文标题】如何管理 PreferenceFragment 中的分隔符?【英文标题】:How to manage dividers in a PreferenceFragment? 【发布时间】:2015-03-01 07:13:23 【问题描述】:

我开始在PreferenceFragment 中处理首选项。这是我所拥有的:

我正在尝试:

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

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

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

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

<PreferenceCategory ... />

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

所以我的问题是:

如何摆脱您在图片中看到的默认项目-项目分隔符?

如何在标题上方设置全宽分隔线,或者如何摆脱内部片段/活动填充?当然,我的活动布局没有任何(显式)填充。

【问题讨论】:

嗨,你得到答案了吗?如果有的话,你能帮忙解决一下吗? @Naruto 简而言之,我使用了list = findViewById(android.R.id.list),然后使用了list.setDivider(null)。为避免 NPE,您需要将此称为 onResume 而不是 onCreate。至于其他问题,我今天会尝试发布完整的答案。 您好,谢谢您的回复。我使用的是片段而不是活动。即我的班级看起来像public class SettingsFragment extends PreferenceFragment。那么在这种情况下我们该如何实现呢?任何帮助。 getview() 返回 NULL,如果我像这样调用View rootView = getView(); @Naruto 在下面看到我的回答。 【参考方案1】:

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,则分隔符只会在两个首选项之间显示。

在代码中

您还可以在onActivityCreatedPreferenceFragmentCompat 中使用以下方法以编程方式更改/删除分隔线:

@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()?对于第 2 项,我到处都尝试过"android:listDivider""android:divider",但没有任何效果。【参考方案2】:

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

    

【讨论】:

完美!快速,简单,简短。我遇到的所有其他答案(甚至都没有用)似乎过分了。谢谢! 我们是否必须创建一个列表视图然后将每个元素设置为相同的布局?! 在 ANDROID 21 之后,RecyclerView 用于显示基于列表的内容,因此 R.id.list 传递了 RecyclerView 引用,不能像使用 ListView 那样处理。 @Fattum 这不是我所经历的......也许这取决于所使用的偏好类型?正常与支持 v4 等? @CamHart 你没有遇到什么?【参考方案3】:

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

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

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

【讨论】:

太棒了。最简单的一个。 最好的解决方案,因为它还考虑了子屏幕。【参考方案4】:

完全忘记了这个问题,现在将发布答案以帮助其他人。我通过在托管我的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"/>

【讨论】:

我做到了,但没有在列表中显示分隔符,请帮助我。谢谢! 在 API 21 之后返回一个 recyclerview,如果 'android.R.id.list' 将被搜索。 PreferenceFragmentCompat 的 onStart 中的 setDivider(null) 应该会有所帮助。 你能分享你的preference_divider.xml吗? 对不起@Zoker,我不能【参考方案5】:

对于 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” 呃,对不起,您必须先将依赖项添加到 build.gradle。见***.com/a/34220290/1259029【参考方案6】:

我遇到了这个确切的问题,想要在偏好类别之间设置分隔符,而不是在项目本身之间。我发现接受的解决方案通过从偏好项中删除分隔符来满足问题 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);
        
    

【讨论】:

【参考方案7】:

(仅限 AndroidX)

Maksim Ivanov's answer 让我大部分时间到达那里。但是要删除仅针对在代码中创建的特定首选项的分隔符,我必须这样做:

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)
    

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

针对新的 android API 24 及更高版本的新解决方案(2017 年 12 月 25 日)。

我在***上尝试了很多方法后发现,但不起作用,或者它只是起作用但在nestest中不起作用PreferenceScreen

首先,您需要从当前显示的片段中找到列表视图,并删除分隔符:

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
        
    

其次,要在fragment提交时移除分隔符,调用上面的方法(removeDividerInCurrentFragment)移除listview的分隔符。

确定您是否有 nestest PreferenceScreen。通过实现FragmentManager.OnBackStackChangedListener 协议在您的PreferenceActivity 中更改片段时注册监听器:

class YourPreferenceActivity : PreferenceActivity(), FragmentManager.OnBackStackChangedListener 
    override fun onBackStackChanged() 
        this@YourPreferenceActivity.removeDividerInCurrentFragment()
    

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

祝你好运!

【讨论】:

【参考方案10】:

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

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

【讨论】:

以上是关于如何管理 PreferenceFragment 中的分隔符?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Fragment中使用PreferenceFragment

如何在Fragment中使用PreferenceFragment

我们如何使用 espresso 来测试带有 PreferenceFragment 的 android 设置活动?

如何将 PreferenceFragment 与 ViewPager 一起使用?

PreferenceFragment 背景颜色

PreferenceFragment去完成设置页面