27

I started dealing with preferences in a PreferenceFragment. Here's what I have:

my_preferences

I'm trying to:

  1. get rid of the dividers between items. I suppose this can be defined from styles, but I can't figure out how. I tried getting the preference ListView at runtime calling findViewById(android.R.id.list), as I read somewhere, but it returns null.

  2. set new, full width dividers right on top of the headers, as seen here. For example in this case I want a full width divider right above "Statistiche", but not above "Generali" which is on top of the list.

The only way that comes to my mind is setting dividers as fake preferences, like with:

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

<PreferenceCategory ... />

The main issue here is that my PreferenceFragment (or the ActionBarActivity it's in) has some left/right padding, that make any divider I add into preferences.xml not cover the entire width.

So my question are:

  • How can I get rid of default, item-item dividers that you can see in the image?

  • How can I set full width dividers right above headers, or how can I get rid of internal fragment/activity padding? Of course my activity layout has no (explicit) padding whatsoever.

natario
  • 24,954
  • 17
  • 88
  • 158
  • Hi, did u get the answer?. can you help with the solution if you have? – Naruto Feb 01 '15 at 15:55
  • @Naruto in short I used `list = findViewById(android.R.id.list)` and then `list.setDivider(null)`. To avoid NPE, you need to call this `onResume` rather than `onCreate`. As for the other issue, I will try to post a full answer today. – natario Feb 01 '15 at 16:07
  • Hey, thanks for reply. I'm using fragments not an activity. i.e my class looks like `public class SettingsFragment extends PreferenceFragment`. So in this case how can we achieve? any help. – Naruto Feb 01 '15 at 16:09
  • `getview()` returns me NULL, if i call `View rootView = getView();` like this – Naruto Feb 01 '15 at 16:10
  • @Naruto see my answer below. – natario Feb 03 '15 at 16:14

10 Answers10

56

AndroidX makes it simple, but I wish it was better documented.

In XML

To add/remove dividers between preferences in XML, use the following attributes:

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

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

</androidx.preference.PreferenceScreen>

Note, a divider will only be shown between two preferences if the top divider has allowDividerBelow set to true and the bottom divider has allowDividerAbove set to true.

In Code

You can also change/remove dividers programmatically using the following methods in onActivityCreated of your PreferenceFragmentCompat:

@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);
}
Maksim Ivanov
  • 3,991
  • 31
  • 25
  • Good answer: Quick questions: 1. Do you know how to make the dividers appear in the layout preview of Android Studio? 2. Is there a way to call `setDivider()` in an XML theme, rather than in code? For item 2, I've tried `"android:listDivider"` and `"android:divider"` everywhere, but it has no effect. – Mr-IDE Mar 20 '20 at 20:55
28

Add this code under the 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);

    }
SagiLow
  • 5,721
  • 9
  • 60
  • 115
  • 4
    Perfect! Quick, easy, short. Every other answer (That didn't even work) that I've come across seemed excessive. Thanks! – TJ Biddle Jul 18 '15 at 05:56
  • do we have to create a list view then set each element as layout for the same?! – Alok Rajasukumaran Jun 30 '16 at 08:58
  • After ANDROID 21 a RecyclerView is used to display the list-based content, so the R.id.list delieveres a RecyclerView reference, which can't be handled the same way we did with ListView. – Fattum Jan 09 '17 at 07:25
  • @Fattum that's not what I'm experiencing... maybe it depends on the type of preference used? normal vs support v4 etc.? – CamHart Mar 15 '17 at 06:57
  • @CamHart what exacatly, you are not experiencing? – Fattum Mar 15 '17 at 13:07
  • @Fattum I'm using android > 21 and R.id.list still works. – CamHart Mar 17 '17 at 16:03
  • For SDK 21 and below. the code works within onCreateView. For SDK 23, it only works within onActivityCreated. BTW, if you are getting RecyclerView, perhaps you are using the support library, which has it's own setDivider methods. – Desmond Lua Apr 05 '18 at 09:42
11

Although a bit late I`m having same troubles with dividers in preff screen and found this solution: Set custom style to the hosting activity and add to the style:

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

It actually does the same as setting it trough code but you save one findById and i think it looks clearer set trough styles

X3Btel
  • 1,408
  • 1
  • 13
  • 21
9

Had totally forgot about this question, will post an answer now to help others. I solved by moving my code in the onResume() method of the activity that hosts my PreferenceFragment. I think there are several other points at which you can recall a non-null ListView using findViewById(android.R.id.list).

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

You can probably get rid of the rootView check, but at the same time you might even want to check for list != null. I didn't face any NPE this way anyway.

So, setDivider(null) takes off the item-item dividers. I managed to add section dividers covering the full width of the screen by:

  • Removing padding from list;
  • Adding a custom preference in my XML:

n

 <Preference
     android:title="divider"
     android:selectable="false"
     android:layout="@layout/preference_divider"/>
natario
  • 24,954
  • 17
  • 88
  • 158
4

For PreferenceFragmentCompat, ListView doesn't work. But this works like a charm:

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

Add this to your app theme:

<item name="preferenceTheme">@style/PrefsTheme</item>
formatBCE
  • 159
  • 1
  • 12
3

I had this exact problem and wanted dividers between preference categories but not between the items themselves. I found that the accepted solution satisfied Question 1 by removing the dividers from preference items but did not fix question 2 and add the dividers between preference categories.

Fix below. Basically override the onCreateAdapter method of your PreferenceFragmentCompat and give it a custom PreferenceGroupAdapter that has an overridden onBindViewHolder method that uses the position and whatever else you need to set above and below permissions for each view holder. A divider will be drawn when both viewholders allow a divider between them.

Here is my fix

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);
        }
    }
}
doubleA
  • 2,446
  • 22
  • 45
3

(AndroidX only)

Maksim Ivanov's answer got me most of the way there. But to remove dividers only for a specific Preference created in code, I had to do:

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)
    }
}
Stephen Talley
  • 1,130
  • 15
  • 15
0

if you use a PreferenceFragment ,you can use ListView.setDivider(null); if you use a PreferenceFragmentCompat ,you can use PreferenceFragmentCompat.setDivider(Drawable divider) or setDividerHeight(int height);

sadmonster
  • 11
  • 2
0

New solution for new android API 24 and above (December 25th, 2017).

I've found after try many ways on stackoverflow, but not work, or it just work but not work in nestest PreferenceScreen.

Frist, you need to find listview from current displaying fragment, and remove the divider:

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

Second, to remove the divider when fragment commited, call method above (removeDividerInCurrentFragment) to remove listview's divider.

To sure if you have nestest PreferenceScreen. Register listener when fragment changed in your PreferenceActivity by implement FragmentManager.OnBackStackChangedListener protocol:

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

Finally, register backstack changes listener by call fragmentManager.addOnBackStackChangedListener(this@ YourPreferenceActivity) in onCreate. And remove backstack changes listener by call fragmentManager.removeOnBackStackChangedListener in onDestroyed method.

Good luck!

dphans
  • 1,543
  • 19
  • 20
0

You can re-style your divider using this theme.

<style name="PreferenceFragment.Material">
    <item name="android:divider">@drawable/preference_list_divider_material</item>
</style>
The Finest Artist
  • 3,150
  • 29
  • 34