【问题标题】:Custom layout for ListPreferenceListPreference 的自定义布局
【发布时间】:2015-08-25 09:19:12
【问题描述】:

我正在使用 PreferenceActivity 来设置我的应用。 我想添加一个允许用户选择图标的新首选项。 对于这个任务,我想使用 ListPreference,但我还想在列表中显示图标。

我尝试自定义 ListPreference 以使用自定义布局,但问题是一旦我这样做,列表项就不可点击(它确实显示了我的自定义布局并使用默认值当前选择)。

我在不同的模拟器版本和 Galaxy S2 上对其进行了测试。按下项目时,我可以看到按下/未按下的一些效果,但没有调用 onClick 方法。

我按照Android: Checkable Linear Layout 上的说明添加自定义布局(我也尝试了How to customize list preference radio button 中描述的选项,但结果相同)。

IconTypePreference.java(从ListPreference复制并修改):

public class IconTypePreference extends DialogPreference {
    private IconType value;
    private int      clickedDialogIndex;
    private boolean  valueSet;

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public IconTypePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

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

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public IconTypePreference(Context context) {
        super(context);
    }

    public void setValue(String value) {
        // Always persist/notify the first time.
        final boolean changed = !TextUtils.equals(getValueText(), value);
        if (changed || !valueSet) {
            if (value == null) {
                this.value = null;
            } else {
                this.value = IconType.valueOf(value);
            }
            valueSet = true;
            persistString(value);
            if (changed) {
                notifyChanged();
            }
        }
    }

    public void setValueIndex(int index) {
        setValue(IconType.values()[index].toString());
    }

    public IconType getValue() {
        return value;
    }

    public String getValueText() {
        return (value == null ? null : value.toString());
    }

    public int findIndexOfValue(String value) {
        IconType[] values = IconType.values();
        for (int i = values.length - 1; i >= 0; i--) {
            if (values[i].toString().equals(value)) {
                return i;
            }
        }
        return -1;
    }

    private int getValueIndex() {
        return findIndexOfValue(getValueText());
    }

    @Override
    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
        super.onPrepareDialogBuilder(builder);

        clickedDialogIndex = getValueIndex();
        builder.setSingleChoiceItems(new IconTypeAdapter(getContext()), clickedDialogIndex,
                                     new DialogInterface.OnClickListener() {
                                         public void onClick(DialogInterface dialog, int which) {
                                             clickedDialogIndex = which;
                                             IconTypePreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
                                             dialog.dismiss();
                                         }
                                     });
        /*
         * The typical interaction for list-based dialogs is to have
         * click-on-an-item dismiss the dialog instead of the user having to
         * press 'Ok'.
         */
        builder.setPositiveButton(null, null);
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        super.onDialogClosed(positiveResult);

        if (positiveResult && clickedDialogIndex >= 0) {
            String value = IconType.values()[clickedDialogIndex].toString();
            if (callChangeListener(value)) {
                setValue(value);
            }
        }
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) {
        return a.getString(index);
    }

    @Override
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        setValue(restoreValue ? getPersistedString(getValueText()) : (String)defaultValue);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        final Parcelable superState = super.onSaveInstanceState();
        if (isPersistent()) {
            // No need to save instance state since it's persistent
            return superState;
        }

        final SavedState myState = new SavedState(superState);
        myState.value = getValueText();
        return myState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state == null || !state.getClass().equals(SavedState.class)) {
            // Didn't save state for us in onSaveInstanceState
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState myState = (SavedState)state;
        super.onRestoreInstanceState(myState.getSuperState());
        setValue(myState.value);
    }

    private static class SavedState extends BaseSavedState {
        String value;

        public SavedState(Parcel source) {
            super(source);
            value = source.readString();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeString(value);
        }

        public SavedState(Parcelable superState) {
            super(superState);
        }

        public static final Parcelable.Creator<SavedState> CREATOR =
                new Parcelable.Creator<SavedState>() {
                    public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                    }

                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                };
    }

    private static class IconTypeAdapter extends ArrayAdapter<IconType> {
        private final String[]       iconTypeText;
        private       LayoutInflater inflater;

        public IconTypeAdapter(Context context) {
            super(context, R.layout.icon_type_item, IconType.values());
            this.inflater = LayoutInflater.from(context);
            iconTypeText = context.getResources().getStringArray(R.array.icon_type);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.icon_type_item, parent, false);
            }
            ((TextView)convertView.findViewById(R.id.text)).setText(iconTypeText[position]);
            convertView.setClickable(true);
            // todo: set view text
            return convertView;
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }

        @Override
        public long getItemId(int position) {
            return position;
        }
    }
}

CheckableLinearLayout.java

public class CheckableLinearLayout extends LinearLayout implements Checkable {
    private Checkable checkable;

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

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

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public CheckableLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
//        setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        checkable = getCheckable(this);
        if (checkable == null) {
            throw new RuntimeException("Missing Checkable component");
        }
    }

    private Checkable getCheckable(ViewGroup viewGroup) {
        View v;
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; ++i) {
            v = getChildAt(i);
            if (v instanceof Checkable) {
                return (Checkable)v;
            } else if (v instanceof ViewGroup) {
                Checkable result = getCheckable((ViewGroup)v);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

    @Override
    public void setChecked(boolean checked) {
        checkable.setChecked(checked);
    }

    @Override
    public boolean isChecked() {
        return checkable.isChecked();
    }

    @Override
    public void toggle() {
        checkable.toggle();
    }
}

icon_type_item.xml

<?xml version="1.0" encoding="utf-8"?>
<com.utils.ui.widget.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                                                          android:layout_width="match_parent"
                                                          android:layout_height="wrap_content"
                                                          android:orientation="horizontal">
    <TextView android:id="@+id/text"
              android:layout_width="0dp"
              android:layout_height="wrap_content"
              android:layout_weight="1"
              android:focusable="false"
              android:focusableInTouchMode="false"/>
    <RadioButton android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:clickable="false"
                 android:focusable="false"
                 android:focusableInTouchMode="false"/>
</com.utils.ui.widget.CheckableLinearLayout>

添加到 settings.xml

<com.utils.ui.preference.IconTypePreference
        android:key="icon_type"
        android:defaultValue="type_b"
        android:title="@string/icon_type_preference_title"/>

编辑

CheckableLinearLayout.java

中有一个错误

getCheckable 方法替换为:

private Checkable getCheckable(ViewGroup viewGroup) {
    View v;
    int childCount = viewGroup.getChildCount();
    for (int i = 0; i < childCount; ++i) {
        v = viewGroup.getChildAt(i);
        if (v instanceof Checkable) {
            return (Checkable)v;
        } else if (v instanceof ViewGroup) {
            Checkable result = getCheckable((ViewGroup)v);
            if (result != null) {
                return result;
            }
        }
    }
    return null;
}

【问题讨论】:

    标签: android android-preferences


    【解决方案1】:

    找到问题的解决方案。

    问题出在适配器的 getView 方法中: 我变了

    convertView.setClickable(true);
    

    convertView.setClickable(false);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-07
      • 1970-01-01
      • 1970-01-01
      • 2018-08-02
      相关资源
      最近更新 更多