質問

I am using two NumberPickers to display province(mProvincePicker) and city(mCityPicker) data in my app. When user changes province data, city data should be changed accordingly. I reset the mCityPicker data in NumberPicker.onValueChange(NumberPicker picker, int oldVal, int newVal). But it does not work well and an java.lang.ArrayIndexOutOfBoundsException crash the app.

Here is my code:

public class AreaPickerDialog extends Dialog implements OnValueChangeListener, OnClickListener {
    static final String TAG = "AreaPickerDialog";

    private NumberPicker mProvincePicker;
    private NumberPicker mCityPicker;
    private Button mCancelBtn;
    private Button mOKBtn;

    private AreaUtil mAreaUtil;
    private List<Area> mProvinces;
    private int mAreaId;
    private int mProvinceId;
    private Handler mHandler;

    public AreaPickerDialog(Context context, Handler handler, int areaId) {
        super(context);
        mAreaUtil = AreaUtil.getInstance();
        mProvinces = mAreaUtil.getProvinceList();
        mAreaId = areaId;
        mProvinceId = mAreaUtil.getProvinceIdByAreaId(areaId);
        mHandler = handler;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_area_picker);
        mProvincePicker = (NumberPicker) findViewById(R.id.np_province);
        mCityPicker = (NumberPicker) findViewById(R.id.np_city);
        mCancelBtn = (Button) findViewById(R.id.btn_cancel);
        mOKBtn = (Button) findViewById(R.id.btn_ok);

        initProvinceData();

        mProvincePicker.setOnValueChangedListener(this);
        mCityPicker.setOnValueChangedListener(this);
        mCancelBtn.setOnClickListener(this);
        mOKBtn.setOnClickListener(this);
    }

    private void initProvinceData() {
        if (mProvinces != null && mProvinces.size() > 0) {
            String[] provinceNames = new String[mProvinces.size()];
            for (int i = 0; i < provinceNames.length; i++) {
                provinceNames[i] = mProvinces.get(i).getProvincename();
            }
            mProvincePicker.setDisplayedValues(provinceNames );
            mProvincePicker.setMinValue(1);
            mProvincePicker.setMaxValue(mProvinces.size());
        }
        mProvincePicker.setValue(mProvinceId);
        initCityData(mProvincePicker.getValue());
    }

    private void initCityData(int provinceId) {
        List<Arealist> cities = mAreaUtil.getCityListOfProvince(provinceId);
        if (cities != null && cities.size() > 0) {
            try {
                int min = Integer.parseInt(cities.get(0).getAreaid());
                int max = Integer.parseInt(cities.get(cities.size() -1).getAreaid());
                String[] cityNames = new String[cities.size()];
                for (int i = 0; i < cityNames.length; i++) {
                    cityNames[i] = cities.get(i).getName();
                }
                mCityPicker.setValue(0);
                mCityPicker.setDisplayedValues(cityNames);
                mCityPicker.setMinValue(min);
                mCityPicker.setMaxValue(max);
            } catch (NumberFormatException e) {
                ILog.e(TAG, e.getMessage(), e);
            }
        }
    }

    @Override
    public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
        if (mProvincePicker.equals(picker)) {
            initCityData(mProvincePicker.getValue());
        } else if (mCityPicker.equals(picker)) {
            mAreaId = mCityPicker.getValue();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btn_cancel:
            dismiss();
            break;

        case R.id.btn_ok:
            Message msg = mHandler.obtainMessage(MineActivity.MSG_UPDATE_AREA);
            msg.arg1 = mAreaId;
            msg.sendToTarget();
            dismiss();
            break;

        default:
            break;
        }
    }
} 

And logcat error trace:

12-30 23:09:26.560: E/InputEventReceiver(5620): Exception dispatching input event.
12-30 23:09:26.560: W/dalvikvm(5620): threadid=1: thread exiting with uncaught exception (group=0x41ddf438)
12-30 23:09:26.580: E/AndroidRuntime(5620): FATAL EXCEPTION: main
12-30 23:09:26.580: E/AndroidRuntime(5620): java.lang.ArrayIndexOutOfBoundsException: length=14; index=15
12-30 23:09:26.580: E/AndroidRuntime(5620):     at net.simonvt.numberpicker.NumberPicker.updateInputTextView(NumberPicker.java:1840)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at net.simonvt.numberpicker.NumberPicker.setDisplayedValues(NumberPicker.java:1423)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at com.meishai.app.dialog.AreaPickerDialog.initCityData(AreaPickerDialog.java:88)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at com.meishai.app.dialog.AreaPickerDialog.onValueChange(AreaPickerDialog.java:100)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at net.simonvt.numberpicker.NumberPicker.notifyChange(NumberPicker.java:1855)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at net.simonvt.numberpicker.NumberPicker.setValueInternal(NumberPicker.java:1641)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at net.simonvt.numberpicker.NumberPicker.scrollBy(NumberPicker.java:1106)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at net.simonvt.numberpicker.NumberPicker.onTouchEvent(NumberPicker.java:886)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at android.view.View.dispatchTouchEvent(View.java:7127)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2170)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1905)
12-30 23:09:26.580: E/AndroidRuntime(5620):     at net.simonvt.numberpicker.NumberPicker.dispatchTouchEvent(NumberPicker.java:944)
    ....

I am reseting the minValue,maxValue and DisplayedValues to reset the NumberPicker data and not working, so what is the right way?

役に立ちましたか?

解決 4

Well, I look into the source code of NumberPicker.java, find that it will recompute the data and refresh UI in setMinValue(int minValue) and setMaxValue(int minValue). When set the new minValue, it will compute using new minValue and old maxValue, then error occured.

So, I modify a little of the NumberPicker: set value only in setMinValue(int minValue) and setMaxValue(int maxValue), and update UI in setDisplayedValues(String[] displayedValues). When data of NumberPicker changed, just reset the minValue、maxVaule and displayedValues. Note that setDisplayedValues(String[] displayedValues) MUST be called at last.

Here is what I modified:

NumberPicker.java:

public void setMinValue(int minValue) {
    if (mMinValue == minValue) {
        return;
    }
    if (minValue < 0) {
        throw new IllegalArgumentException("minValue must be >= 0");
    }
    mMinValue = minValue;
    if (mMinValue > mValue) {
        mValue = mMinValue;
    }
    //boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
    //setWrapSelectorWheel(wrapSelectorWheel);
    //initializeSelectorWheelIndices();
    //updateInputTextView();
    //tryComputeMaxWidth();
    invalidate();
}

public void setMaxValue(int maxValue) {
    if (mMaxValue == maxValue) {
        return;
    }
    if (maxValue < 0) {
        throw new IllegalArgumentException("maxValue must be >= 0");
    }
    mMaxValue = maxValue;
    if (mMaxValue < mValue) {
        mValue = mMaxValue;
    }
    //boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
    //setWrapSelectorWheel(wrapSelectorWheel);
    //initializeSelectorWheelIndices();
    //updateInputTextView();
    //tryComputeMaxWidth();
    invalidate();
}

public void setDisplayedValues(String[] displayedValues) {
    if (mDisplayedValues == displayedValues) {
        return;
    }
    mDisplayedValues = displayedValues;
    if (mDisplayedValues != null) {
        // Allow text entry rather than strictly numeric entry.
        mInputText.setRawInputType(InputType.TYPE_CLASS_TEXT
                | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
    } else {
        mInputText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
    }
    boolean wrapSelectorWheel = mMaxValue - mMinValue > mSelectorIndices.length;
    setWrapSelectorWheel(wrapSelectorWheel);
    updateInputTextView();
    initializeSelectorWheelIndices();
    tryComputeMaxWidth();
}

AreaPickerDialog.java:

private void initCityData(int provinceId) {
    List<Arealist> cities = mAreaUtil.getCityListOfProvince(provinceId);
    if (cities != null && cities.size() > 0) {
        try {
            int min = Integer.parseInt(cities.get(0).getAreaid());
            int max = Integer.parseInt(cities.get(cities.size() -1).getAreaid());
            String[] cityNames = new String[cities.size()];
            for (int i = 0; i < cityNames.length; i++) {
                cityNames[i] = cities.get(i).getName();
            }
            mCityPicker.setMinValue(min);
            mCityPicker.setMaxValue(max);
            mCityPicker.setDisplayedValues(cityNames);
        } catch (NumberFormatException e) {
            ILog.e(TAG, e.getMessage(), e);
        }
    }
}

This is an ugly but working solution.

他のヒント

check for Call setDisplayedValues(null) before setMaxValue in onClick.

Reference: https://stackoverflow.com/a/24322319/2624806

I have the same problem and I fixed it without modifing NumberPicker.java. Here is the code for your reference.

private void initCityData(int provinceId) {
        List<Arealist> cities = mAreaUtil.getCityListOfProvince(provinceId);
        if (cities != null && cities.size() > 0) {
            try {
                int min = Integer.parseInt(cities.get(0).getAreaid());
                int max = Integer.parseInt(cities.get(cities.size() -1).getAreaid());
                String[] cityNames = new String[cities.size()];
                for (int i = 0; i < cityNames.length; i++) {
                    cityNames[i] = cities.get(i).getName();
                }
                int maxV = mCityPicker.getMaxValue();
                if (max> maxV){ 
                    mCityPicker.setMinValue(min);
                    mCityPicker.setValue(0);
                    mCityPicker.setDisplayedValues(cityNames);
                    mCityPicker.setMaxValue(max);
                }else{
                    mCityPicker.setMinValue(min);
                    mCityPicker.setValue(0);
                    mCityPicker.setMaxValue(max);
                    mCityPicker.setDisplayedValues(cityNames);
                }

            } catch (NumberFormatException e) {
                ILog.e(TAG, e.getMessage(), e);
            }
        }
    }

Just set displayedValues to null before resetting number picker like below:

 pickerWard.displayedValues = null

 pickerWard.maxValue = values.size - 1
 pickerWard.displayedValues = values

looking the code of NumberPicker you're using:

private boolean updateInputTextView() {
/*
* If we don't have displayed values then use the current number else
* find the correct value in the displayed values for the current
* number.
*/
        String text = (mDisplayedValues == null) ? formatNumber(mValue)
                : mDisplayedValues[mValue - mMinValue];

you can see that's using mMinValue before you call setMinValue.

Try setting minValue and maxValue, before set the displayed values.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top