android - 自定义首选项以 ClassCastException 结束

标签 android preference

以下代码 fragment 显示了一个 fragment ,用于显示一些首选项,包括自定义首选项 (ColorPickerPreference)。

public static class PlayerEditFragment extends PreferenceFragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.profile_player_edit);
        ...

定义首选项的 xml 文件如下所示:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory
        android:key="prof_key_player"
        android:title="Player" >
        <EditTextPreference
            android:key="prof_key_player_name"
            android:title="Name" />

        <de.jhouse.agt.view.dialog.ColorPickerPreference
            android:defaultValue="2130837679"
            android:key="prof_key_player_color"
            android:title="Color" />

        <de.jhouse.agt.view.dialog.NumberPickerPreference
            android:defaultValue="0"
            android:key="prof_key_player_pos"
            android:title="Position" />
    </PreferenceCategory>
</PreferenceScreen>

以及自定义首选项ColorPickerPreference,它使用户能够从对话框中的预定义颜色列表中选择颜色:

public class ColorPickerPreference extends DialogPreference {
    // the images to display
    public static final Integer[] imageIDs = { R.drawable.player_white,
            R.drawable.player_black, R.drawable.player_orange,
            R.drawable.player_green, R.drawable.player_grey,
            R.drawable.player_red, R.drawable.player_blue,
            R.drawable.player_yellow, R.drawable.player_brown, };

    private Gallery gallery;
    private int value;

    public ColorPickerPreference(Context context, AttributeSet attrs) {

        super(context, attrs);
    }

    public ColorPickerPreference(Context context, AttributeSet attrs,
            int defStyleAttr) {

        super(context, attrs, defStyleAttr);
    }

    @Override
    protected View onCreateDialogView() {

        final FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        layoutParams.gravity = Gravity.CENTER;

        gallery = new Gallery(getContext());
        gallery.setLayoutParams(layoutParams);
        gallery.setAdapter(new ImageAdapter(getContext()));
        gallery.setOnItemClickListener(new OnItemClickListener() {

            public void onItemClick(AdapterView<?> parent, View v,
                    int position, long id) {

                setValue(imageIDs[position]);
            }
        });

        final FrameLayout dialogView = new FrameLayout(getContext());
        dialogView.addView(gallery);

        return dialogView;
    }

    @Override
    protected void onBindDialogView(View view) {

        super.onBindDialogView(view);
        gallery.setSelection(getValue(), true);
    }

    @Override
    protected void onDialogClosed(boolean positiveResult) {
        if (positiveResult) {
            setValue(gallery.getSelectedItemPosition());
        }
    }

    @Override
    protected Object onGetDefaultValue(TypedArray a, int index) {

        Integer value =  a.getInt(index, imageIDs[4]);
        return value;
    }

    @Override
    protected void onSetInitialValue(boolean restorePersistedValue,
            Object defaultValue) {

        setValue(restorePersistedValue ? getPersistedInt(imageIDs[4])
                : (Integer) defaultValue);
    }

    public void setValue(int value) {

        this.value = value;
        persistInt(this.value);
    }

    public int getValue() {
        return this.value;
    }

    /**
     * 
     * @author Andy
     *
     */
    public static class ImageAdapter extends BaseAdapter {

        private Context context;
        private int itemBackground;

        public ImageAdapter(Context c) {
            context = c;
             // sets a grey background; wraps around the images
             TypedArray a = context.obtainStyledAttributes(R.styleable.MyGallery);
             itemBackground = a.getResourceId(R.styleable.MyGallery_android_galleryItemBackground, 0);
             a.recycle();
        }

        // returns the number of images
        public int getCount() {
            return imageIDs.length;
        }

        // returns the ID of an item
        public Object getItem(int position) {
            return position;
        }

        // returns the ID of an item
        public long getItemId(int position) {
            return position;
        }

        // returns an ImageView view
        public View getView(int position, View convertView, ViewGroup parent) {

            ImageView imageView = new ImageView(context);
            imageView.setImageResource(imageIDs[position]);
            imageView.setLayoutParams(new Gallery.LayoutParams(100, 100));
            imageView.setBackgroundResource(itemBackground);
            return imageView;
        }
    }
}

现在的问题是,当执行语句addPreferencesFromResource(R.xml.profile_player_edit)时,它会抛出ClassCastException:无法将String强制转换为int。 我无法弄清楚我做错了什么,但如果我排除自定义首选项 ColorPickerPreference 一切正常吗?

这是完整的堆栈跟踪:

02-10 15:35:45.778: D/AndroidRuntime(19737): Shutting down VM
02-10 15:35:45.798: E/AndroidRuntime(19737): FATAL EXCEPTION: main
02-10 15:35:45.798: E/AndroidRuntime(19737): Process: de.jhouse.agt, PID: 19737
02-10 15:35:45.798: E/AndroidRuntime(19737): java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.SharedPreferencesImpl.getInt(SharedPreferencesImpl.java:239)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.Preference.getPersistedInt(Preference.java:1697)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at de.jhouse.agt.view.dialog.ColorPickerPreference.onSetInitialValue(ColorPickerPreference.java:103)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.Preference.dispatchSetInitialValue(Preference.java:1514)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.Preference.onAttachedToHierarchy(Preference.java:1309)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.PreferenceGroup.addPreference(PreferenceGroup.java:167)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.PreferenceGroup.addItemFromInflater(PreferenceGroup.java:108)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.PreferenceGroup.addItemFromInflater(PreferenceGroup.java:45)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.GenericInflater.rInflate(GenericInflater.java:488)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.GenericInflater.rInflate(GenericInflater.java:493)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.GenericInflater.inflate(GenericInflater.java:326)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.GenericInflater.inflate(GenericInflater.java:263)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.PreferenceManager.inflateFromResource(PreferenceManager.java:272)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.preference.PreferenceFragment.addPreferencesFromResource(PreferenceFragment.java:300)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at de.jhouse.agt.activity.ProfileEditActivity$PlayerEditFragment.onCreate(ProfileEditActivity.java:620)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.Fragment.performCreate(Fragment.java:2075)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:868)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1082)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.BackStackRecord.run(BackStackRecord.java:833)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.FragmentManagerImpl$1.run(FragmentManager.java:452)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.os.Handler.handleCallback(Handler.java:739)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.os.Handler.dispatchMessage(Handler.java:95)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.os.Looper.loop(Looper.java:145)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at android.app.ActivityThread.main(ActivityThread.java:5834)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at java.lang.reflect.Method.invoke(Native Method)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at java.lang.reflect.Method.invoke(Method.java:372)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1388)
02-10 15:35:45.798: E/AndroidRuntime(19737):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1183)

最佳答案

对于那些在没有重复键的情况下遇到此错误的人,以下解决方案可能会有所帮助:

尝试重新安装应用程序。您可能在开发过程中更改了首选项值的类,导致类型不兼容的旧值仍然与该键一起存储,从而阻止了设置被比较并更改为新值。

通过卸载连接(或模拟)设备上的应用程序,首选项也应该被删除。然后该 key 应该可以自由地被新类型的值使用。

如果情况并非如此,请检查您的自定义首选项类别。当您将值保存为另一种类型而不是尝试加载它的类型时,您也可能会遇到类转换异常。

所以:检查您的自定义 Preference 类的方法以确保其 persistInt() 和 getPersistedInt() 调用的一致性:

@Override
protected void onDialogClosed(boolean positiveResult) {
    super.onDialogClosed(positiveResult);
    if (positiveResult) {
        persistInt(getYourInteger());
    }
}

@Override
protected void onSetInitialValue(
               boolean restorePersistedValue,
               Object defaultValue) {
    int worstCaseDefault = 0;
    int value;
    if(restorePersistedValue) {
        if (defaultValue == null) {
            value = worstCaseDefault;
        } else { //The following line is critical.
            try { value = getPersistedInt(Integer.parseInt(defaultValue.toString())); }
            catch (Exception e) {
                Log.e("YourPreference", "OnSetInitialValue: ", e);
                value = worstCaseDefault;
            }
        }
    } else {
        try { value = Integer.parseInt(defaultValue.toString()); }
        catch (Exception e) {
            Log.e("YourPreference", "OnSetInitialValue: ", e);
            value = worstCaseDefault;
        }
    }
}

如果这也不能解决问题,我猜想该首选项在重新安装后仍然存在。在这种情况下,您可能需要查看 Stephen Kaiser 在 Andy 自己的答案中评论的问题。

注意,我在这里仅使用 Integer 类型作为示例,但它应该与其他持久方法以相同的方式工作。

关于android - 自定义首选项以 ClassCastException 结束,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28434170/

相关文章:

android - 动画问题 Android BottomNavigationView with Lottie

android - SharedPreferences 如何以编程方式添加消息或如何以编程方式在显示上添加文本

Android - 使用 PreferenceScreen 显示和保存设置到/从 ContentProvider

android - 如何在自定义 ArrayList 中保存 FCM 通知?

Android 复选框首选项

android - 如何从 Locu API 获取餐厅菜单

android - iOS 中 Localizable.strings 的基本样式

android - 如果街景在特定的纬度内不可用,如何获取最近的可用街景?

android - PreferenceActivity 在 Android 2.1 上工作正常,但在 4.1 上不工作(填充)

android - 设置首选项布局并更改其中的属性