androidandroid-preferencesandroid-stylesandroid-settingspreferencescreen

Adjust margin/padding in the 'PreferenceScreen' in Android


I would like to adjust the default margin/padding values for the 'PreferenceScreen' in Android, I know it's not possible to simply set it in the XML code (layout file) but Android definitely gets these values from somewhere and PROBABLY (I hope) I can set some 'style attribute(s)' and achieve it.

The values I want to adjust are:

enter image description here

I found this answer online: Android: How to maximize PreferenceFragment width (or get rid of margin)?

but as per me it really uses a really bad workaround and I would like to try to manipulate some of the official parameters.

Does anyone knows how can I achieve it by adjusting the style(s) or some other fields?


Solution

  • Unfortunately, I don't think that you are going to find a better answer than you have already discovered in Android: How to maximize PreferenceFragment width (or get rid of margin)?. I will explain why and give you an alternative which you may consider to be worse than the one presented in the answer you reference.

    Underneath the preference screen is a layout for each item which we will identify. These are the preference library dependencies that we will use:

    implementation 'com.android.support:preference-v7:27.1.1'
    implementation 'com.android.support:preference-v14:27.1.1'
    

    The preferenceTheme attribute of the theme defines how the preferences will look.

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        ...
        <!-- Theme for the preferences -->
        <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
    </style>
    

    Going up the parent chain (ctrl-B) from PreferenceThemeOverlay.v14.Material we see

    <style name="PreferenceThemeOverlay.v14.Material">
    ...
    <item name="preferenceStyle">@style/Preference.Material</item>
    ...
    </style>    
    

    Preference.Material is defined as:

    <style name="Preference.Material">
        <item name="android:layout">@layout/preference_material</item>
    </style>    
    

    The layout for the preference is preference_material.xml. Here is the source.

    The following section of this layout interests us:

    ...
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:paddingTop="16dp"
        android:paddingBottom="16dp">
        <TextView android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:textAppearance="?attr/textAppearanceListItem"
            android:ellipsize="marquee" />
        <TextView android:id="@+id/summary"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/title"
            android:layout_alignStart="@id/title"
            android:textAppearance="?attr/textAppearanceListItemSecondary"
            android:textColor="?attr/textColorSecondary"
            android:maxLines="10"
            android:ellipsize="end" />
    </RelativeLayout>
    ...
    

    As you can see, the top and bottom padding for the RelativeLayout is hard-coded. Since a style attribute is not used, there is no way to override the padding. Because of this hard-coding, you have two choices:

    1. Use the "really bad workaround" outlined in the answer you found which involves Java code to modify the padding, or,

    2. Use your own layout by defining android:layout="@layout/custom_preference" in the XML for your preferences. You can copy the Android layout and make your modifications.

    Each method has drawbacks, so pick the one you think you can best deal with.


    The following are key components of a small app that demonstrates substitution of the preference layout.

    MainActivity.java
    This activity rolls in the preference fragment for convenience.

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            if (savedInstanceState == null) {
                Fragment preferenceFragment = new PrefsFragment();
                FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                ft.add(R.id.prefContainer, preferenceFragment);
                ft.commit();
            }
        }
    
        public static class PrefsFragment extends PreferenceFragmentCompat {
    
            @Override
            public void onCreatePreferences(Bundle bundle, String s) {
                addPreferencesFromResource(R.xml.app_preferences);
            }
        }
    }
    

    app_preference.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v7.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
        <android.support.v7.preference.Preference
            android:key="preference"
            android:layout="@layout/custom_pref_layout"
            android:summary="Doesn't really do anything."
            android:title="Preference Title" />
    
        <android.support.v7.preference.EditTextPreference
            android:defaultValue="Default EditTextPreference value"
            android:dialogMessage="EditTextPreference Dialog Message"
            android:inputType="number"
            android:key="editTextPreference"
            android:layout="@layout/custom_pref_layout"
            android:summary="EditTextPreference Summary"
            android:title="EditTextPreference Title" />
    
        <android.support.v7.preference.SwitchPreferenceCompat
            android:defaultValue="true"
            android:key="switchPreference"
            android:layout="@layout/custom_pref_layout"
            android:summary="SwitchPreference Summary"
            android:title="SwitchPreference Title" />
    
        <android.support.v7.preference.CheckBoxPreference
            android:defaultValue="true"
            android:key="checkBoxPreference"
            android:layout="@layout/custom_pref_layout"
            android:summary="CheckBoxPreference Summary"
            android:title="CheckBoxPreference Title" />
    
        <android.support.v7.preference.ListPreference
            android:defaultValue="180"
            android:entries="@array/pref_sync_frequency_titles"
            android:entryValues="@array/pref_sync_frequency_values"
            android:key="list_preference"
            android:layout="@layout/custom_pref_layout"
            android:negativeButtonText="@null"
            android:positiveButtonText="@null"
            android:title="List Preference Title" />
    
    </android.support.v7.preference.PreferenceScreen>
    

    activity_main.xml
    Simply defines a home for the preference fragment.

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/prefContainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.preferencecustomlayout.MainActivity" />
    

    custom_pref_layout.xml
    Some modification have been made to accommodate usage of this file mainly updated ?attr/somthing to ?android:attr/something.

    <?xml version="1.0" encoding="utf-8"?>
    <!-- Copyright (C) 2014 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.
    -->
    <!-- Layout for a Preference in a PreferenceActivity. The
         Preference is able to place a specific widget for its particular
         type in the "widget_frame" layout. -->
    
    <!-- Modified from the original to accommodate usage as a local layout file. -->
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?android:attr/activatedBackgroundIndicator"
        android:clipToPadding="false"
        android:gravity="center_vertical"
        android:minHeight="?attr/listPreferredItemHeightSmall"
        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
        android:paddingStart="?android:attr/listPreferredItemPaddingStart">
    
        <LinearLayout
            android:id="@+id/icon_frame"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="-4dp"
            android:gravity="start|center_vertical"
            android:minWidth="60dp"
            android:orientation="horizontal"
            android:paddingBottom="4dp"
            android:paddingEnd="12dp"
            android:paddingTop="4dp">
    
            <com.android.internal.widget.PreferenceImageView
                android:id="@+id/icon"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:maxHeight="48dp"
                android:maxWidth="48dp" />
        </LinearLayout>
    
        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:paddingBottom="16dp"
            android:paddingTop="16dp">
    
            <TextView
                android:id="@android:id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="marquee"
                android:singleLine="true"
                android:textAppearance="?attr/textAppearanceListItem" />
    
            <TextView
                android:id="@android:id/summary"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignStart="@android:id/title"
                android:layout_below="@android:id/title"
                android:ellipsize="end"
                android:maxLines="10"
                android:textAppearance="?attr/textAppearanceListItemSecondary"
                android:textColor="?android:attr/textColorSecondary" />
        </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:orientation="vertical"
            android:paddingStart="16dp" />
    </LinearLayout>
    

    Gradle file

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 27
        buildToolsVersion '27.0.3'
    
        defaultConfig {
            applicationId "com.example.preferencecustomlayout"
            minSdkVersion 18
            targetSdkVersion 27
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        testImplementation 'junit:junit:4.12'
        implementation 'com.android.support:appcompat-v7:27.1.1'
        implementation 'com.android.support:preference-v7:27.1.1'
        implementation 'com.android.support:preference-v14:27.1.1'
    }