4

In my settingsImage of my settings menu I have multiple preferences that don't have lines in between them, creating an ugly look. How do I fix this?

Pike D.
  • 671
  • 2
  • 11
  • 30
  • this link may be use full to you check it http://www.javatpoint.com/android-preferences-example – sunita Mar 18 '17 at 03:59
  • This is the correct look for Material Design. I agree that it looks odd, but your app will look out of place if you don't follow it. – Tenfour04 Jul 09 '17 at 22:52
  • @Tenfour04 could you link where it specifically says that? – Pike D. Jul 09 '17 at 22:53
  • @PikeD. Well, I looked it up and realized I'm wrong. The spec doesn't mention it. I was making an assumption because it is the default styling in Marshmallow+ and AppCompat. But why expend effort trying to look different than Google's own apps, when the result is that your UI is not cohesive with the rest of the system? – Tenfour04 Jul 09 '17 at 22:57

6 Answers6

13

AndroidX

If using AndroidX, to show dividers you can simply add the following attributes in your Preference XML:

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

A more detailed answer here: https://stackoverflow.com/a/55981453/2836371

Maksim Ivanov
  • 3,991
  • 31
  • 25
7

The following is for AndroidX:

In AndroidX, getListView() returns a RecyclerView.

Dividing lines can be added to RecyclerViews using .addItemDecoration()

This should be done after the RecyclerView has been inflated in onActivityCreated().

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    RecyclerView recyclerView = getListView();
    DividerItemDecoration itemDecoration = new DividerItemDecoration(context, RecyclerView.VERTICAL);
    recyclerView.addItemDecoration(itemDecoration);
}
Matthew Smith
  • 79
  • 2
  • 4
3

The most appropriate solution I found is to set up layouts for categories and preferences in XML. For example

pref_screen.xml:

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

    <Preference
        android:key="@string/pref_org_code_key"
        android:title="@string/pref_org_code_title"
        android:defaultValue="@string/pref_org_code_default"
        app:iconSpaceReserved="false"
        android:layout="@layout/single_preference" />
    <PreferenceCategory android:title="Invitation - Auto accept">
        <CheckBoxPreference
            android:defaultValue="@bool/friend_invite_accept_default"
            android:key="@string/pref_friend_invite_auto_accept_key"
            android:summaryOff="@string/pref_disabled"
            android:summaryOn="@string/pref_enabled"
            android:title="@string/pref_invites_friend_title"
            app:iconSpaceReserved="false"
            android:layout="@layout/single_preference"
            android:widgetLayout="@layout/single_pref_checkbox" />
        <CheckBoxPreference
            android:defaultValue="@bool/group_invite_accept_default"
            android:key="@string/pref_group_invite_auto_accept_key"
            android:summaryOff="@string/pref_disabled"
            android:summaryOn="@string/pref_enabled"
            android:title="@string/pref_invites_group_title"
            app:iconSpaceReserved="false"
            android:layout="@layout/single_preference"
            android:widgetLayout="@layout/single_pref_checkbox" />
    </PreferenceCategory>
</PreferenceScreen>

single_preference.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2015 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License
  -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeightSmall">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center_vertical"
    android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
    android:background="?android:attr/selectableItemBackground"
    android:clipToPadding="false"
    android:focusable="true" >

    <LinearLayout
        android:id="@+id/icon_frame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="-4dp"
        android:minWidth="60dp"
        android:gravity="start|center_vertical"
        android:orientation="horizontal"
        android:paddingRight="12dp"
        android:paddingTop="4dp"
        android:paddingBottom="4dp">
        <android.support.v7.internal.widget.PreferenceImageView
            android:id="@android:id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:maxWidth="48dp"
            app:maxHeight="48dp" />
    </LinearLayout>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingTop="16dp"
        android:paddingBottom="16dp">

        <TextView android:id="@android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textAppearance="@style/Preference_TextAppearanceMaterialSubhead"
            android:ellipsize="marquee" />

        <TextView android:id="@android:id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@android:id/title"
            android:layout_alignLeft="@android:id/title"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:textColor="?android:attr/textColorSecondary"
            android:maxLines="10" />
    </RelativeLayout>

    <!-- Preference should place its actual preference widget here. -->
    <LinearLayout android:id="@android:id/widget_frame"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="end|center_vertical"
        android:paddingLeft="16dp"
        android:orientation="vertical" >
    </LinearLayout>

</LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:background="@color/cool_grey"/>
</LinearLayout>

single_pref_checkbox.xml

<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+android:id/checkbox"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:focusable="false"
    android:clickable="false"
    android:background="@null" />

single_pref_category.xml

<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2015 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License
  -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_marginBottom="0dp"
    android:layout_marginTop="0dp"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <View
        android:layout_width="match_parent"
        android:layout_height="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:background="@color/grey300"/>

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="?android:attr/listPreferredItemPaddingLeft">

    <LinearLayout
        android:id="@+id/icon_frame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="start|center_vertical"
        android:orientation="horizontal">
        <android.support.v7.internal.widget.PreferenceImageView
            android:id="@android:id/icon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:maxHeight="18dp"
            app:maxWidth="18dp"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingLeft="@dimen/preference_category_padding_start">

        <TextView
            android:id="@android:id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:paddingRight="?android:attr/listPreferredItemPaddingRight"
            android:textAlignment="viewStart"
            android:textColor="@color/preference_fallback_accent_color"
            android:textStyle="bold" />
        <TextView
            android:id="@android:id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:singleLine="true"
            android:textColor="?android:attr/textColorSecondary"/>
    </LinearLayout>

</FrameLayout>
</LinearLayout>

Maybe additionally it will be required to modify styles and use this style instead of default:

<style name="SpecialPreferenceTheme">
        <item name="android:scrollbars">vertical</item>
        <item name="checkBoxPreferenceStyle">@style/Preference.CheckBoxPreference.Material</item>
        <item name="dialogPreferenceStyle">@style/Preference.DialogPreference.Material</item>
        <item name="dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
        <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference.Material</item>
        <item name="preferenceCategoryStyle">@style/CategoryPreference</item>
        <item name="preferenceFragmentCompatStyle">@style/PreferenceFragment.Material</item>
        <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList.Material</item>
        <item name="preferenceFragmentStyle">@style/PreferenceFragment.Material</item>
        <item name="preferenceScreenStyle">@style/Preference.PreferenceScreen.Material</item>
        <item name="preferenceStyle">@style/SinglePreference</item>
        <item name="seekBarPreferenceStyle">@style/Preference.SeekBarPreference.Material</item>
        <item name="switchPreferenceCompatStyle">@style/Preference.SwitchPreferenceCompat.Material</item>
        <item name="switchPreferenceStyle">@style/Preference.SwitchPreference.Material</item>
    </style>
    <style name="SinglePreference">
        <item name="android:layout">@layout/single_preference</item>
        <item name="allowDividerAbove">false</item>
        <item name="allowDividerBelow">true</item>
        <item name="singleLineTitle">false</item>
        <item name="iconSpaceReserved">false</item>
    </style>
    <style name="CategoryPreference">
        <item name="android:layout">@layout/single_pref_category</item>
        <item name="allowDividerAbove">false</item>
        <item name="allowDividerBelow">false</item>
        <item name="iconSpaceReserved">false</item>
    </style>
    <style name="CheckboxPreferece">
        <item name="android:layout">@layout/single_preference</item>
        <item name="allowDividerAbove">false</item>
        <item name="allowDividerBelow">true</item>
        <item name="iconSpaceReserved">false</item>
    </style>
Valentina Konyukhova
  • 4,464
  • 2
  • 24
  • 33
  • 1
    I have been looking for app:iconSpaceReserved="false" for waaaaaayyyyy too long. I've learned more from the code snippets in your post than I have in 100 articles and the official docs about preference items. Thanks! <3 – jungledev Jun 27 '19 at 22:44
  • the problem comes that the app:iconSpaceReserved is for API 26 and above :( – Pulkit Mar 12 '21 at 19:11
2

A good way to create dividers in the whole settings screen in androidx (based on this post) is to create a Subclass of Preference and override onBindViewHolder then use it in xml. It works in

implementation 'androidx.preference:preference:1.1.1'

but unfortunately it is not a good solution for Screens with more than one Preference type (one may create subclass for EditTextPreference etc.)

public class CustomPreference extends Preference {
  public CustomPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  @Override
  public void onBindViewHolder(PreferenceViewHolder holder) {
    super.onBindViewHolder(holder);
    holder.setDividerAllowedAbove(true);
  }

}

Vít Kapitola
  • 499
  • 1
  • 5
  • 9
1

I think you're trying to add the dividers in custom preference.xml.

It should be ease If you're using both PreferenceActivity or Preference Fragment.

Just go to the onCreate method and call this

ListView list = getListView();
list.setDivider(); // pass null for no dividers or a valid drawable for dividers.
albeee
  • 1,452
  • 1
  • 12
  • 20
1

Thanks to Matthew Smith's answer.

A proper way is to overwrite thePreferenceFragmentCompat's onCreateRecyclerView method of class

    @Override
    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
        RecyclerView recyclerView = super.onCreateRecyclerView(inflater, parent, savedInstanceState);
        DividerItemDecoration itemDecoration = new DividerItemDecoration(getContext(), RecyclerView.VERTICAL);
        recyclerView.addItemDecoration(itemDecoration);
        return recyclerView;
    }

This works for com.android.support:preference-v7:28.0.0 library

Julien Kieffer
  • 1,116
  • 6
  • 16
crazygit
  • 449
  • 4
  • 11