30

I'm making an app that extends the PreferenceActivity and I want to add an icon to each Preference.

I read a similar question, and this is the answer with more reputation:

CommonsWare Say:

The Settings application uses a private custom PreferenceScreen subclass to have the icon -- IconPreferenceScreen. It is 51 lines of code, including the comments, though it also requires some custom attributes. The simplest option is to clone all of that into your project, even though you do not like that.

But I can't make it work. For now I cloned the class IconPreferenceScreen to my project. And I don't know what I have to do after this. I'm trying to make a new IconPreferenceScreen I can't make it work..

    IconPreferenceScreen test = new IconPreferenceScreen();
    test.setIcon(icon);
Toni Alvarez
  • 1,012
  • 1
  • 10
  • 21
  • You can find int also here on **GrepCode**: http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.4.2_r1/com/android/settings/IconPreferenceScreen.java?av=f –  Jul 26 '14 at 14:12

9 Answers9

46

Today you can just use android:icon attr available since API 11 :)

Mathias
  • 163
  • 2
  • 7
Vadim
  • 1,582
  • 2
  • 15
  • 16
  • This is the best solution, but is not compatible with Android 2.3 which today still has a significant share (More than 10%) – Toni Alvarez Oct 19 '14 at 12:31
  • 3
    2.3 is absolutely NOT significant today (specially, when you just have no icons on that devices). – Vadim Oct 19 '14 at 18:43
  • 10% share (100 million+ potential customers) is significant for me. – Toni Alvarez Oct 19 '14 at 23:30
  • 6
    It is significant when you lose this market. And absolutely not significant when users just don't see additional icons. – Vadim Oct 20 '14 at 11:03
  • Anyone know what happens if you run it in 2.3? Does it cause an issue or just the icon doesn't show? If it silently just doesn't show the icon w/o distorting the layout then I feel its acceptable to not have that that feature for 5-10%, icons & bitmaps take up memory on lower end devices running 2.3 anyways.. – AlexVPerl Dec 22 '14 at 06:08
19

After many tests and many mistakes I could get it!

I had to do this:

1 - Clone the class IconPreferenceScreen from the Android native Settings app (thanks CommonWare)

2 - Clone the layout file preference_icon.xml from the Android Settings app.

3 - Declare the IconPreferenceScreen styleable in the file attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="IconPreferenceScreen">
         <attr name="icon" format="reference" />
    </declare-styleable>
</resources>

4 - Declare the IconPreferenceScreen in the preference.xml file:

<com.app.example.IconPreferenceScreen 
                android:title="IconPreferenceScreen Title"
                android:summary="IconPreferenceScreen Summary"
                android:key="key1" />

5 - Finally set the icon for the preference, in the preference class:

addPreferencesFromResource(R.xml.example);
IconPreferenceScreen test = (IconPreferenceScreen) findPreference("key1");
Resources res = getResources();
Drawable icon = res.getDrawable(R.drawable.icon1);
test.setIcon(icono1);

Thanks again to CommonsWare for tell me where to start, and for his explanation.

This is the cloned IconPreferenceScreen class:

package com.app.example;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

public class IconPreferenceScreen extends Preference {

    private Drawable mIcon;

    public IconPreferenceScreen(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public IconPreferenceScreen(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setLayoutResource(R.layout.preference_icon);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.IconPreferenceScreen, defStyle, 0);
        mIcon = a.getDrawable(R.styleable.IconPreferenceScreen_icon);
    }

    @Override
    public void onBindView(View view) {
        super.onBindView(view);
        ImageView imageView = (ImageView) view.findViewById(R.id.icon);
        if (imageView != null && mIcon != null) {
            imageView.setImageDrawable(mIcon);
        }
    }

    public void setIcon(Drawable icon) {
        if ((icon == null && mIcon != null) || (icon != null && !icon.equals(mIcon))) {
            mIcon = icon;
            notifyChanged();
        }
    }

    public Drawable getIcon() {
        return mIcon;
    }
}

And this is the cloned preference_icon.xml layout:

<LinearLayout android:id="@+android:id/iconpref"
    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">
    <ImageView android:id="@+id/icon" 
    android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_marginLeft="6dip"
        android:layout_marginRight="6dip" 
        android:layout_gravity="center" />
    <RelativeLayout android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_marginLeft="2dip"
        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="true" 
            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:maxLines="2" />
    </RelativeLayout>
</LinearLayout>
Toni Alvarez
  • 1,012
  • 1
  • 10
  • 21
  • Why not just use a custom layout? http://developer.android.com/reference/android/preference/Preference.html#setWidgetLayoutResource%28int%29 – Nathan Fig Apr 26 '11 at 19:22
  • This doesn't work for me. No icon appears and when I next preferences within, the app crashes. Do you know of a way to have and Icon on the label of a PreferenceScreen? – Camille Sévigny Aug 29 '11 at 18:40
  • This solution worked for me. I don't understand why you have to set the icon in the preference activity when the IconPreferenceScreen takes the time to pull it out of attrs, but you do. – Chiatar Feb 17 '12 at 19:20
  • 5
    The example you've posted _is_ _not_ a PreferenceScreen in any way shape or form. You've basically just added an image to the layout of a generic preference that won't actually _do_ anything when selected. – Justin Buser Mar 27 '12 at 10:55
  • somehow this is not working for me on a ICS+. Please check http://stackoverflow.com/questions/19786999/preference-with-icon-not-showing-in-ics – M Rajoy Nov 05 '13 at 11:14
11

The best and easiest way to achieve what you want is to make the icon a 9-patch icon with the right border as the stretchable area.

Let's say you have an EditTextPreference that you want to add an icon before the title and summary.

You create a MyEditTextPreference class that extends EditTextPreference and override the getView method to add your 9-patch icon as the background resource.

Here is a sample code that works:

public class MyEditTextPreference extends EditTextPreference {

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

    @Override
    public View getView(View convertView, ViewGroup parent) {
        View view = super.getView(convertView, parent);
        view.setBackgroundResource(R.drawable.my_icon);
        return view;
    }
}

Since the icon is a 9-patch, the icon will stretch the transparent part until the right end of the cell, placing the icon on the left side.

This is a very clean solution for your problem.

Henrique Rocha
  • 1,737
  • 1
  • 19
  • 29
7

well too late but maybe it will helps somebody the problem is this you can add icon only child of a category such as this is my code samples. only edit text icon can be seen on the screen.

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
        android:title="@string/pref_profil"
        android:icon="@drawable/profile" // it wont show
        android:key="pref_key_storage_settings">

        <EditTextPreference
            android:key="edit_text_preference_1"
            android:selectAllOnFocus="true"
            android:singleLine="true"
            android:icon="@drawable/profile"
            android:title="Edit text preference" />

        <Preference
            android:key="pref_key_sms_delete_limit"
            android:summary="HELLO2"
            android:title="HELLO" />
    </PreferenceCategory>

</PreferenceScreen>

result:

enter image description here

mehmet
  • 1,558
  • 5
  • 30
  • 41
1

I'm making an app in that the PreferenceActivity is the main activity and I want to add an icon to each Custom Preference. Like this image:

That's not a PreferenceActivity.

How I can add an icon to the PreferenceScreen?

It will be simpler for you to just create your own activity that happens to save preferences.

Personally, I would just skip the icon and use the regular PreferenceScreen and PreferenceActivity system.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I know that this image isn't a PreferenceActivity, but I know it's posible to add an icon to Preference using the IconPreferenceScreen class like in the Settings android App.. I cloned the IconPrefScreen class and the preference_icon XML, and I added the IconPrefScreen attribute to attrs.xml, but at this point I don't know how to create a new IconPreference in my activity.. Please help me if you know how to do it! Thanks – Toni Alvarez Apr 23 '11 at 19:49
  • @Tony_GPR: Do you even know what `IconPreferenceScreen` does? – CommonsWare Apr 23 '11 at 20:13
  • extends the Preference class, with the option to add an icon. Am I wrong? – Toni Alvarez Apr 24 '11 at 01:19
  • @Tony_GPR: You are wrong. `PreferenceScreen`, as you may recall, serves two roles: it is the root XML element, and it also serves as the starting point for a nested screen of preferences. In that second role, the `PreferenceScreen`'s title and description serve as what the user taps on from the original screen of preferences to get to the nested screen of preferences. `IconPreferenceScreen` is simply a `PreferenceScreen` with an icon on the side. It has no impact whatsoever on any other preferences. – CommonsWare Apr 24 '11 at 11:32
  • 3
    @Tony_GPR: This is why I said that "it will be simpler for you to just create your own activity that happens to save preferences." You will have to subclass each and every type of `Preference` you want to use and rewire it to have an icon next to it. Again, I REALLY ENCOURAGE YOU TO NOT DO THIS. Android developers have a reputation of being morons when it comes to UI standardization, because Android developers do things like add icons where they don't belong. Users and media members complain that no two Android apps look and work the same, unlike with iOS. Please leave the preferences alone. – CommonsWare Apr 24 '11 at 11:35
  • Thanks for the explanation, but I'm really interested in add icons to each preference. I will continue trying to do this from your explanation. Thanks – Toni Alvarez Apr 24 '11 at 13:25
  • @CommonsWare: _"Android developers have a reputation of being morons when it comes to UI standardization"_ - That's rather harsh, mate. But in your defence, adding an icon to a Preference screen does absolutely nothing except for decoration - the part that get executed is the "text" part of a preference option. – ChuongPham Oct 28 '13 at 06:33
  • Somehow this is not working for me on ICS+. Please check http://stackoverflow.com/questions/19786999/preference-with-icon-not-showing-in-ics – M Rajoy Nov 05 '13 at 11:15
1

Thanks to:

The following worked for me:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="YOUR.PACKAGE.NAME">
    <application
        android:label="@string/app_name"
        android:icon="@drawable/icon">
        <activity
            android:name="AndroidMain"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <string name="app_name">YOUR_APP_NAME</string>
    <string name="about">About</string>
</resources>

res/values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="IconPreference">
        <attr name="icon" format="reference" />
    </declare-styleable>
</resources>

res/layout/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:settings="http://schemas.android.com/apk/res/YOUR.PACKAGE.NAME"
    >
    <PreferenceCategory
        android:title="Main"
        android:key="mainPrefCat">
        <YOUR.PACKAGE.NAME.IconPreference
                android:title="Exit"
                android:summary="Quit the app."
                settings:icon="@drawable/YOUR_ICON"
                android:key="quitPref">
        </YOUR.PACKAGE.NAME.IconPreference>
    </PreferenceCategory>
</PreferenceScreen>

res/layout/preference_icon.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+android:id/widget_frame"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:gravity="center_vertical"
    android:paddingRight="?android:attr/scrollbarSize">
    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="6dip"
        android:layout_marginRight="6dip"
        android:layout_gravity="center" />
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="2dip"
        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="true"
            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:maxLines="2" />
    </RelativeLayout>
</LinearLayout>

Appropriately sized icons under:

res/drawable-hdpi/YOUR_ICON.png
res/drawable-mdpi/YOUR_ICON.png
res/drawable-ldpi/YOUR_ICON.png

AndroidMain.scala:

class AndroidMain extends PreferenceActivity {
  override def onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    addPreferencesFromResource(R.layout.main)
  }
}

IconPreference.scala:

class IconPreference(context: Context, attrs: AttributeSet, defStyle: Int) extends Preference(context, attrs, defStyle) {
    def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)

    setLayoutResource(R.layout.preference_icon);
    val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.IconPreference, defStyle, 0);
    val _icon: Drawable = a.getDrawable(R.styleable.IconPreference_icon);

    override def onBindView(view: View) {
        super.onBindView(view)
        val imageView: ImageView = view.findViewById(R.id.icon).asInstanceOf[ImageView]
        if (imageView != null && _icon != null) {
            imageView.setImageDrawable(_icon);
        }
    }
}
W1M0R
  • 3,367
  • 3
  • 30
  • 32
  • I used Android 2.3.3, Scala 2.9.0.1 and standard icons from the Android SDK (ANDROID_HOME\platforms\android-10\data\res\drawable-hdpi\ic_dialog_XXX.png). – W1M0R Sep 08 '11 at 23:47
0

For a "sane" Answer, you can either use the 9-patch method as described by Henrique Rocha or you can use the following:

Create a new Preference class that extends EditTextPreference and let it Override the onCreateView.

@Override
protected View onCreateView(ViewGroup parent)
{
    View v = LayoutInflater.from(getContext()).inflate(R.layout.iconpreference, null);

    return v;
}

Create a new Layout XML called iconpreference and in this XML you can let it include a standard preference layout. You can basicly tweak it every way you want. Easy Example:

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ImageView android:layout_width="48dp"
        android:layout_height="48dp"
        android:src="@drawable/checkmark_black"/>

    <include layout="@layout/preference_holo" />

</LinearLayout>

Thats all!

JanCor
  • 605
  • 5
  • 3
0

When you need a listpreference with {text and icon} you can have a look at this listpreference with icons

Community
  • 1
  • 1
tm1701
  • 7,307
  • 17
  • 79
  • 168
-1

Try this:

PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);
PreferenceScreen ps= getPreferenceManager().createPreferenceScreen(this);
ps.setKey("ps");
ps.setLayoutResource(R.layout.your_layout);
root.addPreference(ps);
Jav_Rock
  • 22,059
  • 20
  • 123
  • 164