0

I've searched for this all over place and have found a lot of answers that don't compile, or use deprecated APIs. It's a simple thing really that I thought would have been incorporated in the core APIs a while ago:

I want be able able to click on a Preference and replace the radio button with an ImageView and a TextView per row. Simple as that. I would think I should be able to add "entries", "entryValues", and "entryImages" into the preferences.xml and I'd have thought that would be supported as I've seen that UX in lots of other applications. Can't believe it's custom work every time.

Can someone point me in the right direction?

This article is getting there, but is incomplete: Custom list preference dialog layout in Preference Screen, with custom list items

Please help!

I tried the link above and I can easily bring up a custom dialog, I just have to create an adapter to get the data, which I mostly have. I just can't believe it's this much coding for something so simple.

0

1 Answer 1

0

For posterity here is the java file that does what I want it to do. It references the pre-androidx code but works for androidx behavior. It also supports automatically setting the icon for the preference to the selected icon and updates the summary.

    import android.app.AlertDialog;
    import android.content.Context;
    import android.content.SharedPreferences;
    import android.content.res.TypedArray;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.ListAdapter;
    import android.widget.RadioButton;
    import android.widget.TextView;
    
    import androidx.annotation.NonNull;
    import androidx.preference.ListPreference;
    import androidx.preference.PreferenceManager;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Objects;
    
    @SuppressWarnings("unused")
    public class IconListPreference extends ListPreference {
        private static final int DEFAULT_TINT = 0xFF;
        private static final int DEFAULT_BACKGROUND_TINT = 0xFF;
    
        private Context mContext;
        private int mErrorResource;
        private final List<Integer> mImages = new ArrayList<>();
        private int mTintColor;
        private int mBackgroundColor;
        private boolean mUseCard;
        private int mCustomItemLayout;
        private String mInitialSummary;
    
        private class iconListPreferenceAdapter extends ArrayAdapter<ImageListItem> {
            private final List<ImageListItem> mItems;
            private final int mLayoutResource;
    
            iconListPreferenceAdapter(Context context, int layoutResource, List<ImageListItem> items) {
                super(context, layoutResource, items);
                mLayoutResource = layoutResource;
                mItems = items;
            }
    
            @NonNull
            @Override
            public View getView(int position, View convertView, @NonNull ViewGroup parent) {
                ViewHolder holder;
    
                if (convertView == null) {
                    LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                    try {
                        assert inflater != null;
                        convertView = inflater.inflate(mLayoutResource, parent, false);
    
                        holder = new ViewHolder();
                        holder.iconName = convertView.findViewById(R.id.iconlistpreference_text);
                        holder.iconImage = convertView.findViewById(R.id.iconlistpreference_image);
                        holder.radioButton = convertView.findViewById(R.id.iconlistpreference_radio);
                        convertView.setTag(holder);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    holder = (ViewHolder)convertView.getTag();
                }
    
                if (holder == null) {
                    return super.getView(position, convertView, parent);
                }
    
                ImageListItem item = mItems.get(position);
    
                holder.iconName.setText(item.name);
                if (item.resource != 0) {
                    holder.iconImage.setImageResource(item.resource);
                } else {
                    holder.iconImage.setImageResource(mErrorResource);
                }
                if (mTintColor != 0) {
                    holder.iconImage.setColorFilter(mTintColor);
                }
                if (mBackgroundColor != 0) {
                    holder.iconImage.setBackgroundColor(mBackgroundColor);
                }
                holder.radioButton.setChecked(item.isChecked);
                return convertView;
            }
        }
    
        private static class ImageListItem {
            private final int resource;
            private final boolean isChecked;
            private final String name;
    
            ImageListItem(CharSequence name, int resource, boolean isChecked) {
                this(name.toString(), resource, isChecked);
            }
    
            ImageListItem(String name, int resource, boolean isChecked) {
                this.name = name;
                this.resource = resource;
                this.isChecked = isChecked;
            }
        }
    
        private static class ViewHolder {
            ImageView iconImage;
            TextView iconName;
            RadioButton radioButton;
        }
    
        public IconListPreference(Context context) {
            super(context);
        }
    
        public IconListPreference(Context context, AttributeSet attrs) {
            this(context, attrs, 0, 0);
        }
    
        public IconListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
            this(context, attrs, defStyleAttr, 0);
        }
    
        public IconListPreference(Context context, AttributeSet attrs, int defStyleAttr,  int defStyleRes) {
            super(context, attrs);
    
            mContext = context;
            mInitialSummary = Objects.requireNonNull(getSummary()).toString();
    
            final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
            final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.iconlistpreference, defStyleAttr, defStyleRes);
    
            try {
                int entryImagesArrayResource = array.getResourceId(R.styleable.iconlistpreference_ilp_entryImages, 0);
                String tintKey = array.getNonResourceString(R.styleable.iconlistpreference_ilp_tintKey);
                String backgroundKey = array.getNonResourceString(R.styleable.iconlistpreference_ilp_backgroundTint);
    
                mTintColor = array.getColor(R.styleable.iconlistpreference_ilp_tint, DEFAULT_TINT);
                mBackgroundColor = array.getColor(R.styleable.iconlistpreference_ilp_backgroundTint, DEFAULT_BACKGROUND_TINT);
                mErrorResource = array.getResourceId(R.styleable.iconlistpreference_ilp_errorImage, 0);
                mUseCard = array.getBoolean(R.styleable.iconlistpreference_ilp_useCard, false);
                mCustomItemLayout = array.getResourceId(R.styleable.iconlistpreference_ilp_itemLayout, 0);
                if (tintKey != null) {
                    mTintColor = sharedPreferences.getInt(tintKey, mTintColor);
                }
                if (backgroundKey != null) {
                    mBackgroundColor = sharedPreferences.getInt(backgroundKey, mBackgroundColor);
                }
                if (entryImagesArrayResource != 0) {
                    TypedArray images = context.getResources().obtainTypedArray(entryImagesArrayResource);
    
                    for (int i = 0; i < images.length(); i++) {
                        mImages.add(images.getResourceId(i, 0));
                    }
                    images.recycle();
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                array.recycle();
            }
        }
    
        @Override
        protected void onClick() {
            //
            // If an images array has been specified display the custom dialog otherwise
            // display the default.
            //
            if (!mImages.isEmpty()) {
                AlertDialog dialog = onPrepareDialog();
    
                dialog.show();
            } else {
                super.onClick();
            }
        }
    
        @Override
        public void setValue(final String value) {
            super.setValue(value);
    
            int index = findIndexOfValue(value);
    
            if (index != -1) {
                //
                // Update the icon for the list preference from the list of entryImages, if specified.
                //
                if (!mImages.isEmpty()) {
                    setIcon(mImages.get(index));
                }
                //
                // Update the summary.
                //
                if (!mInitialSummary.isEmpty()) {
                    setSummary(getEntries()[index] + "\n" + mInitialSummary);
                }
            }
            notifyChanged();
        }
    
        protected AlertDialog onPrepareDialog() {
            AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
            List<ImageListItem> items = new ArrayList<>();
            int length = getEntries().length;
    
            for (int i = 0; i < length; i++) {
                int resource = 0;
    
                if (mImages.size() > i) {
                    resource = mImages.get(i);
                }
                String currentValue = getValue();
    
                items.add(new ImageListItem(getEntries()[i], resource, (getEntryValues()[i]).equals(currentValue)));
            }
    
            int layout = R.layout.iconlistpreference_item;
    
            if (mUseCard) {
                layout = R.layout.iconlistpreference_item_card;
            }
            if (mCustomItemLayout != 0) {
                layout = mCustomItemLayout;
            }
    
            ListAdapter adapter = new iconListPreferenceAdapter(getContext(), layout, items);
    
            //
            // Setup the adapter to populate the alertDialog and Set the value that was selected.
            //
            builder.setAdapter(adapter, (dialog, which) -> setValue(getEntryValues()[which].toString()));
    
            //
            // Cancel the dialog if cancel is pressed.
            //
            builder.setNegativeButton(android.R.string.cancel, null);
            builder.setTitle(getTitle());
    
            return builder.create();
        }
}

Only problem is that this code does not render in Android Studio when I try to view the preferences layout. Haven't figured out why yet.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.