2

我正在尝试DialogPreference使用两个NumberPicker对象来实现 a ,以NumberPicker在方向更改后恢复最后更改的值:

public class CustomTimePreference extends DialogPreference {

public NumberPicker firstPicker, secondPicker;

private int lastHour = 0;
private int lastMinute = 15;
private int firstMaxValue;
private int tempHour;
private int tempMinute;
private int rotatedHour;
private int rotatedMinute;
private int firstMinValue = 0;
private int secondMinValue=0;
private int secondMaxValue=59;
private String headerText;
private boolean usedForApprox;




public static int getHour(String time){
    String[] pieces = time.split(":");
    return (Integer.parseInt(pieces[0]));
}

public static int getMinute(String time){
    String[] pieces = time.split(":");
    return (Integer.parseInt(pieces[1]));
}

public CustomTimePreference(Context context){
    this(context, null);
}

public CustomTimePreference(Context context, AttributeSet attrs){
    super(context, attrs);
    init(attrs);
    setDialogLayoutResource(R.layout.custom_time_preference);
    setPositiveButtonText(context.getString(R.string.time_preference_set_text));
    setNegativeButtonText(context.getString(R.string.time_preference_cancel_text));
}

private void init(AttributeSet attrs){
    TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTimePreference);
    firstMaxValue = a.getInteger(R.styleable.CustomTimePreference_firstMaxValue,10);
    usedForApprox = a.getBoolean(R.styleable.CustomTimePreference_usedForApproximate, false);
    headerText = a.getString(R.styleable.CustomTimePreference_customTimeDialogTopText);
    a.recycle();
}

public void setFirstPickerValue(int value){
    firstPicker.setValue(value);
}

public void setSecondPickerValue(int value){
    secondPicker.setValue(value);
}



@Override
protected View onCreateDialogView(){
    Log.d("OnCreateDialogView","nanana");
    View root = super.onCreateDialogView();
    TextView tv = (TextView)root.findViewById(R.id.custom_time_preference_title);
    tv.setText(headerText);
    firstPicker = (NumberPicker)root.findViewById(R.id.time_preference_first_picker);
    firstPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {

        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            // TODO Auto-generated method stub
            tempHour = newVal;
        }
    });

    secondPicker = (NumberPicker)root.findViewById(R.id.time_preference_second_picker);
    secondPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {

        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            // TODO Auto-generated method stub
            tempMinute = newVal;
        }
    });
    if(usedForApprox){
        int smallestValue = MainActivity.getShortestPeriodLength(getContext());
        int second = smallestValue % 60;
        second-=1;
        firstPicker.setMaxValue(second);
        secondPicker.setMaxValue(59);

    } else {
        firstPicker.setMaxValue(firstMaxValue);
        secondPicker.setMaxValue(secondMaxValue);
    }
    firstPicker.setMinValue(firstMinValue);
    secondPicker.setMinValue(secondMinValue);
    return root;
}

@Override
protected void onBindDialogView(View v){
    super.onBindDialogView(v);
        firstPicker.setValue(lastHour);
        secondPicker.setValue(lastMinute);
}

@Override
protected void onDialogClosed(boolean positiveResult){
    super.onDialogClosed(positiveResult);
    if(positiveResult){
        lastHour = firstPicker.getValue();
        lastMinute = secondPicker.getValue();

        if (lastHour ==0 && lastMinute == 0){
            lastMinute =1;
        }

        String time = String.valueOf(lastHour) + ":" + String.valueOf(lastMinute);

        if(callChangeListener(time)){
            persistString(time);
        }
    }
}

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

@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue){
    String time = null;

    if(restoreValue){
        if (defaultValue == null){
            time = getPersistedString("00:00");
        } else {
            time = getPersistedString(defaultValue.toString());
        }
    } else {
        time = defaultValue.toString();
    }

    lastHour = tempHour =  getHour(time);
    lastMinute = tempMinute = getMinute(time);
}

private static class SavedState extends BaseSavedState {
    // Member that holds the setting's value
    // Change this data type to match the type saved by your Preference
    String value;

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

    public SavedState(Parcel source) {
        super(source);
        // Get the current preference's value
        value = source.readString();  // Change this to read the appropriate data type
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        // Write the preference's value
        dest.writeString(value);  // Change this to write the appropriate data type
    }

    // Standard creator object using an instance of this class
    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];
        }
    };
}

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    // Check whether this Preference is persistent (continually saved)
    /*
    if (isPersistent()) {
        // No need to save instance state since it's persistent, use superclass state
        return superState;
    }
    */
    // Create instance of custom BaseSavedState
    final SavedState myState = new SavedState(superState);
    // Set the state's value with the class member that holds current setting value
    myState.value = String.valueOf(tempHour) + ":" + String.valueOf(tempMinute);
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    // Check whether we saved the state in onSaveInstanceState
    if (state == null || !state.getClass().equals(SavedState.class)) {
        // Didn't save the state, so call superclass
        super.onRestoreInstanceState(state);
        return;
    }
    // Cast state to custom BaseSavedState and pass to superclass
    SavedState myState = (SavedState) state;
    super.onRestoreInstanceState(myState.getSuperState());
    // Set this Preference's widget to reflect the restored state
    rotatedHour = getHour(myState.value);
    rotatedMinute = getMinute(myState.value);
    firstPicker.setValue(rotatedHour);
    secondPicker.setValue(rotatedMinute);
}

}

有两个问题:

  1. 当我打开我的自定义首选项,更改其中一个选择器的值,然后将手机从纵向旋转到横向时,应用程序崩溃。错误是NuLLpointerException,它指向我尝试将恢复的值分配给我的一个NumberPicker对象的行。
  2. 这更像是一个问题而不是一个问题。我从 Android 开发者主页复制了 BaseSavedState 内部类和 onSaveInstanceState()、onRestoreInstanceState() 函数,但是当我尝试应用程序在方向更改时恢复值时,手机显示的是持久值,而不是方向更改前的最新值。当我尝试使用日志消息检查代码时,我发现我的手机在 SaveInstanceState isPersisted 检查退出了该功能并且根本不使用 BaseSavedState 对象。我注释掉了 isPersisted 检查,所以现在我可以从 BaseSavedState 对象中保存和检索值,但在那之后问题就出现了。1 出现。所以我的问题是,如果偏好是持久的,那么跳过 BaseSavedState 对象创建的原因是什么。
4

2 回答 2

1

NumberPicker用 3 个对象看一下这个实现:

package com.bom.dom;

import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.NumberPicker.OnValueChangeListener;

public class TimePreference extends DialogPreference {

NumberPicker hoursNumberPicker;
NumberPicker minutesNumberPicker;
NumberPicker secondsNumberPicker;
int time;
int currentTime;

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

@Override
protected void onBindDialogView(View view) {
    hoursNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_hours);
    hoursNumberPicker.setMaxValue(24);
    hoursNumberPicker.setMinValue(0);
    hoursNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            updateCurrentTimeFromUI();
        }
    });
    minutesNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_minutes);
    minutesNumberPicker.setMaxValue(59);
    minutesNumberPicker.setMinValue(0);
    minutesNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            updateCurrentTimeFromUI();
        }
    });
    secondsNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_seconds);
    secondsNumberPicker.setMaxValue(59);
    secondsNumberPicker.setMinValue(0);
    secondsNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            updateCurrentTimeFromUI();
        }
    });
    updateUI();
    super.onBindDialogView(view);
}

@Override
protected void onDialogClosed(boolean positiveResult) {
    if (positiveResult) {
            time = currentTime;
            persistInt(time);
            return;
    }
    currentTime = time;
}

private void updateCurrentTimeFromUI() {
    int hours = hoursNumberPicker.getValue();
    int minutes = minutesNumberPicker.getValue();
    int seconds = secondsNumberPicker.getValue();
    currentTime = hours * 3600 + minutes * 60 + seconds;
}

@Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
    if (restorePersistedValue) {
        time = getPersistedInt(1);
    } else {
        time = (Integer) defaultValue;
        persistInt(time);
    }
    currentTime = time;
}

@Override
protected Object onGetDefaultValue(TypedArray a, int index) {
    Integer defaultValue = a.getInteger(index, 1);
    return defaultValue;
}

private void updateUI() {
    int hours = (int) (currentTime / 3600);
    int minutes = ((int) (currentTime / 60)) % 60;
    int seconds = currentTime % 60;
    hoursNumberPicker.setValue(hours);
    minutesNumberPicker.setValue(minutes);
    secondsNumberPicker.setValue(seconds);
}

@Override
protected Parcelable onSaveInstanceState() {
    final Parcelable superState = super.onSaveInstanceState();
    final SavedState myState = new SavedState(superState);
    myState.value = currentTime;
    return myState;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (state == null || !state.getClass().equals(SavedState.class)) {
        super.onRestoreInstanceState(state);
        return;
    }
    SavedState myState = (SavedState) state;
    currentTime = myState.value;
    super.onRestoreInstanceState(myState.getSuperState());
}

private static class SavedState extends BaseSavedState {
    int value;

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

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

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

    @SuppressWarnings("unused")
    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];
        }
    };
}
}

布局文件是:

<?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" >

<NumberPicker
    android:id="@+id/numberpicker_hours"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<NumberPicker
    android:id="@+id/numberpicker_minutes"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

<NumberPicker
    android:id="@+id/numberpicker_seconds"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

</LinearLayout>
于 2013-08-29T05:29:30.947 回答
0

我在第一个问题上找到了解决方案:为了避免 NullPointerException,我必须附上访问 NumberPicker 对象的函数,并通过检查确定这些选择器是否已启动。我遇到了这个问题,因为在我的应用程序上我有多个自定义首选项实例,当我尝试按照数据路径保存/恢复功能时,我发现在 logcat 中我的消息量是我自己的两倍(不仅对于我的屏幕偏好,但也适用于使用我的自定义 DialogPreference 类的其他偏好)。因为,我只打开了一个首选项,其他首选项中的 NumberPicker 对象的初始化没有发生,所以访问这些选择器会导致(如果我理解正确的话)NullPointerException。但我还是想听听更有经验的人,

于 2013-06-03T20:31:19.950 回答