How to handle long text in preferences on Android?

This is my solution of the problem for specific case of the SwitchPreference and for a preference in general.

First of all, I should note that for API levels before 14 it looks like preference titles were multiline by default - at least I did not see any problems with long titles in Android 2.3.3. In newer versions of Android this behaviour has changed to forced single line title.

The workaround is to tweek the SwitchPreference or any other type of preference's layout a bit.

In the preference screen file add the android:layout attribute for required preference, for example:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
  <PreferenceCategory android:title="@string/prefs_category">
    <SwitchPreference
        android:key="keyOfThePreference"
        android:title="@string/pref_title"
        android:switchTextOn="@string/pref_on"
        android:switchTextOff="@string/pref_off"
        android:summaryOn="@string/pref_enabled"
        android:summaryOff="@string/pref_disabled"
        android:layout="@layout/preference_multiline"
        />
        ...

Next, provide the preference_multiline.xml with alternative layout. I used a modified version of the standard preference.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:paddingRight="?android:attr/scrollbarSize"
    android:background="?android:attr/selectableItemBackground" >

    <ImageView
        android:id="@+android:id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dip"
        android:layout_marginRight="6dip"
        android:layout_marginTop="6dip"
        android:layout_marginBottom="6dip"
        android:layout_weight="1">

        <TextView android:id="@+android:id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:singleLine="false"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:ellipsize="marquee"
            android:fadingEdge="horizontal" />

        <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="4" />

    </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="center_vertical"
        android:orientation="vertical" />

</LinearLayout>

Here I deliberately changed android:singleLine value in the title's TextView from true to false. This does the job.


Based on solution from here and here , this is a way to set it for all, if anyone wishes:

abstract class BasePreferenceFragment : PreferenceFragmentCompat() {

    private fun applyOperationsForAllPreferences(preference: Preference) {
        // preference.isIconSpaceReserved = false //you can add this too, if you don't want icons space
        preference.isSingleLineTitle = false
        if (preference is PreferenceGroup)
            for (i in 0 until preference.preferenceCount)
                applyOperationsForAllPreferences(preference.getPreference(i))
    }

    override fun setPreferenceScreen(preferenceScreen: PreferenceScreen?) {
        preferenceScreen?.let { applyOperationsForAllPreferences(it) }
        super.setPreferenceScreen(preferenceScreen)
    }

}