How to handle long text in preferences on Android?
Asked Answered
M

4

13

Background

I'm making an app that has some settings, and I want to use the built in PreferenceActivity or PreferenceFragment for the job

The problem

Some of the preferences have a long title which I cannot shorten, plus I think that if I ever localize the app (translate to multiple languages) I would face the same problem (for example in German, which has quite long words, sometimes).

What you get in this situation is just the beginning of the text and then "..." (or less dots, which doesn't make much sense btw) in the end of it.

Example:

enter image description here

What I've tried

I know that the PreferenceActivity extends from ListActivity, so I can change its adapter to whatever I wish, but that would remove the way it works.

I also know that I can extend from each of the types of the preferences classes, use the "onCreateView" method to have a reference to the created view and then access its children, but this is weird, no? I mean, it's almost like assuming that it will never change the way it looks.

EDIT: Here's a sample code of what I've tried:

Extend from each of the preferences classes, and in each of them , use:

...
@Override
protected View onCreateView(final ViewGroup parent)
  {
  final View view=super.onCreateView(parent);
  ViewUtil.handlePreferenceTitleTextView(view);
  return view;
  }
...

//ViewUtil.java :

private void handlePreferenceTitleTextView(final View v)
  {
  final TextView titleTextView=(TextView)v.findViewById(android.R.id.title);
  if(titleTextView!=null)
    titleTextView.setSingleLine(false);
  }

It works, but I don't think it's recommended as Google might change the way preferences views work.

The question

How to handle long text in preferences' titles on Android ?

Is it possible to make it have an ellipsize / marquee (so that it will have an animation to show everything) ? Or maybe auto fit the font size? Or set it to have word wrap ? Or a horizontal scrollView that will allow the user to scroll to read the rest of the text?

Is there maybe a convention of how to handle such cases? Maybe long clicking to show a toast/dialog for seeing the whole text?

Montserrat answered 6/4, 2013 at 17:15 Comment(12)
I am interesting in this question. with some google search I found these threads #4925694 and #3200410 am waiting for the answerUppsala
@WilliamKinaan No, I'm not talking about the sharedPreferences. I'm talking about UI. the preferenceActivity has all of its textual views to have a limit on their titles. Currently, the only solution that works for me on this case is to extend from each of the preferences classes, and find out the TextView with the id "R.id.title" and change its attributes to whatever I want, but this is a weird solution.Montserrat
Thanks for your explanation, could you please write the answer when you find it, ofc if no one else has answered it.Uppsala
Yes, I will write a tiny sample of what i've tried, which seems to work fine, but I don't think it's recommended to use it.Montserrat
Any reason to not make up a short name (users will prefer it anyway) and describe what it does in android:summary?Superannuate
@el_bhm As I've written, sometimes it's very hard as some words are very long. This is especially problematic with localizations, as some languages have very long words (like German).Montserrat
I am of thought that a work on a proper translation and usage of a language will give you less headache than reinventing the wheel to fit longer words on multiple screen sizes. I am not a native speaker of German, know little of it, but from what is known to me Germans do love short words for the sheer amount of 15+ letter words.Superannuate
@el_bhm but there are also screens with small amount of space, which cannot show even english words. Also, in case of a switchPreference, there is about 50% less space than usual (because of the switch view and the padding).Montserrat
I think the problem is very annoying and frequent for small screens. Would you mind to post the workaround you mentioned in your comments as an extended answer?Washedout
@Washedout For which case exactly? What kind of UI component do you need help with? For most of them I've made my own workaround (which I hope works well on all devices). You can check my app to see what I've done there if you wish: play.google.com/store/apps/details?id=com.lb.app_managerMontserrat
For the case shown in the question exactly (titles of switches are truncated (whereas titles of checkboxes - do not, BTW)). I'm going to play with custom preference layouts if your approach will seem too complicated. Is the "EDIT" in your question - the final workaround you use?Washedout
@Washedout If all you wish to handle is the switch/checkbox preference, you can use what I've done, but I asked this since I didn't find an official way to handle it. The sample code works for me on all of the preferences, but you need to extend each of them. Also, BTW, if you wish to use a SwitchPreference whenever possible, you can use this post (which is also a workaround) : https://mcmap.net/q/524892/-android-using-switch-preference-pre-api-level-14 . I've even made a post about this issue to Google: code.google.com/p/android-developer-preview/issues/…Montserrat
P
2

2021: When using androidx.preference (likely what everyone has)

You can simply use app:singleLineTitle="false"

<Preference
        app:key="your_key"
        app:singleLineTitle="false"
        app:title="LONG TITLEEEEEEE EEEEEEEEE EEEEEEEEEEEEEEE" />

Works for Switches etc too

Pedestal answered 30/4, 2021 at 17:3 Comment(2)
Seems to work well. Thank you. But is there a way to set it for all?Montserrat
OK I've updated my answer (which was wrong, it seems, but maybe was right in the beginning, not sure) to have a solution for all preferences.Montserrat
W
6

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.

Washedout answered 26/9, 2014 at 10:48 Comment(5)
This is a safer solution, but on the other hand, it doesn't respect the default look of the device's OS , as it forces it to look as you've chosen. I guess each solution has its own advantages and disadantages...Montserrat
You're right. And yes, Android can change in newer versions in such a way that introduce unwanted side effects (as it's mentioned in the edited answer regarding "multilineness" of preference titles), so having custom layout gives full control over the app. After all many developers use custom preferences.Washedout
Great! This is working perfectly fine. But there is this warning: android:singleLine is deprecated: False is the default, so just remove the attribute. Should I remove it?Preceptive
@HammadNasir, you may try it. This can be version specific.Washedout
Link to the original layout in AOSP: github.com/aosp-mirror/platform_frameworks_base/blob/master/…Gaynell
M
2

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

}
Montserrat answered 23/2, 2019 at 22:28 Comment(4)
Would be sweet to know "how" this solves the question (and the 6 subquestions asked)Trentontrepan
@Trentontrepan Sorry I don't remember. as for the questions, you might be able to extend from it and handle the Views yourself.Montserrat
@Trentontrepan You are correct. This doesn't help. no idea why I wrote it. Maybe for some time it handled it, but now it's not. No idea what's going on.Montserrat
@Trentontrepan Anyway updated now with a nicer way to do it.Montserrat
P
2

2021: When using androidx.preference (likely what everyone has)

You can simply use app:singleLineTitle="false"

<Preference
        app:key="your_key"
        app:singleLineTitle="false"
        app:title="LONG TITLEEEEEEE EEEEEEEEE EEEEEEEEEEEEEEE" />

Works for Switches etc too

Pedestal answered 30/4, 2021 at 17:3 Comment(2)
Seems to work well. Thank you. But is there a way to set it for all?Montserrat
OK I've updated my answer (which was wrong, it seems, but maybe was right in the beginning, not sure) to have a solution for all preferences.Montserrat
G
0

I didn't want to copy-paste a layout, so I've ended up subclassing the preference:

import android.content.Context;
import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.widget.TextView;

public class CustomCheckboxPreference extends CheckBoxPreference {

  public CustomCheckboxPreference(Context context) {
    super(context);
  }

  public CustomCheckboxPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public CustomCheckboxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }

  public CustomCheckboxPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
  }

  @Override
  public void onBindViewHolder(PreferenceViewHolder holder) {
    super.onBindViewHolder(holder);
    TextView title = (TextView) holder.findViewById(android.R.id.title);
    if (title != null) {
      title.setSingleLine(false);
    }
  }

}

Then just use it in your prefs.xml:

<android.support.v7.preference.PreferenceScreen ...>
  <android.support.v7.preference.PreferenceCategory ...>
    <com.example.CustomCheckboxPreference
      ...
      />
Gaynell answered 12/2, 2018 at 15:1 Comment(2)
That's the same exact code of I wrote in the question. Same also exists in the library I've made, here: github.com/AndroidDeveloperLB/MaterialPreferenceLibraryMontserrat
Yes it's the same (except that I use the support preferences)... I didn't even read the question, haha.Gaynell

© 2022 - 2024 — McMap. All rights reserved.