Skip to content

Instantly share code, notes, and snippets.

@lukehorvat
Last active June 17, 2021 14:06
Show Gist options
  • Save lukehorvat/4398028 to your computer and use it in GitHub Desktop.
Save lukehorvat/4398028 to your computer and use it in GitHub Desktop.
An Android preference class that provides a user with the means to select an integer from a NumberPicker and persist it. (License: MIT License)
<resources>
<!-- note: to re-use an existing Android attribute not already used by the superclass, name should have prefix "android:" and do not define a format -->
<declare-styleable name="NumberPickerDialogPreference">
<attr name="android:max" />
<attr name="min" format="integer" />
</declare-styleable>
</resources>
package com.lukehorvat;
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.TextView;
/**
* A {@link DialogPreference} that provides a user with the means to select an integer from a {@link NumberPicker}, and persist it.
*
* @author lukehorvat
*
*/
public class NumberPickerDialogPreference extends DialogPreference
{
private static final int DEFAULT_MIN_VALUE = 0;
private static final int DEFAULT_MAX_VALUE = 100;
private static final int DEFAULT_VALUE = 0;
private int mMinValue;
private int mMaxValue;
private int mValue;
private NumberPicker mNumberPicker;
public NumberPickerDialogPreference(Context context)
{
this(context, null);
}
public NumberPickerDialogPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
// get attributes specified in XML
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NumberPickerDialogPreference, 0, 0);
try
{
setMinValue(a.getInteger(R.styleable.NumberPickerDialogPreference_min, DEFAULT_MIN_VALUE));
setMaxValue(a.getInteger(R.styleable.NumberPickerDialogPreference_android_max, DEFAULT_MAX_VALUE));
}
finally
{
a.recycle();
}
// set layout
setDialogLayoutResource(R.layout.preference_number_picker_dialog);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
setDialogIcon(null);
}
@Override
protected void onSetInitialValue(boolean restore, Object defaultValue)
{
setValue(restore ? getPersistedInt(DEFAULT_VALUE) : (Integer) defaultValue);
}
@Override
protected Object onGetDefaultValue(TypedArray a, int index)
{
return a.getInt(index, DEFAULT_VALUE);
}
@Override
protected void onBindDialogView(View view)
{
super.onBindDialogView(view);
TextView dialogMessageText = (TextView) view.findViewById(R.id.text_dialog_message);
dialogMessageText.setText(getDialogMessage());
mNumberPicker = (NumberPicker) view.findViewById(R.id.number_picker);
mNumberPicker.setMinValue(mMinValue);
mNumberPicker.setMaxValue(mMaxValue);
mNumberPicker.setValue(mValue);
}
public int getMinValue()
{
return mMinValue;
}
public void setMinValue(int minValue)
{
mMinValue = minValue;
setValue(Math.max(mValue, mMinValue));
}
public int getMaxValue()
{
return mMaxValue;
}
public void setMaxValue(int maxValue)
{
mMaxValue = maxValue;
setValue(Math.min(mValue, mMaxValue));
}
public int getValue()
{
return mValue;
}
public void setValue(int value)
{
value = Math.max(Math.min(value, mMaxValue), mMinValue);
if (value != mValue)
{
mValue = value;
persistInt(value);
notifyChanged();
}
}
@Override
protected void onDialogClosed(boolean positiveResult)
{
super.onDialogClosed(positiveResult);
// when the user selects "OK", persist the new value
if (positiveResult)
{
int numberPickerValue = mNumberPicker.getValue();
if (callChangeListener(numberPickerValue))
{
setValue(numberPickerValue);
}
}
}
@Override
protected Parcelable onSaveInstanceState()
{
// save the instance state so that it will survive screen orientation changes and other events that may temporarily destroy it
final Parcelable superState = super.onSaveInstanceState();
// set the state's value with the class member that holds current setting value
final SavedState myState = new SavedState(superState);
myState.minValue = getMinValue();
myState.maxValue = getMaxValue();
myState.value = getValue();
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;
}
// restore the state
SavedState myState = (SavedState) state;
setMinValue(myState.minValue);
setMaxValue(myState.maxValue);
setValue(myState.value);
super.onRestoreInstanceState(myState.getSuperState());
}
private static class SavedState extends BaseSavedState
{
int minValue;
int maxValue;
int value;
public SavedState(Parcelable superState)
{
super(superState);
}
public SavedState(Parcel source)
{
super(source);
minValue = source.readInt();
maxValue = source.readInt();
value = source.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
super.writeToParcel(dest, flags);
dest.writeInt(minValue);
dest.writeInt(maxValue);
dest.writeInt(value);
}
@SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>()
{
@Override
public SavedState createFromParcel(Parcel in)
{
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size)
{
return new SavedState[size];
}
};
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/text_dialog_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dip"
android:paddingLeft="12dip"
android:paddingRight="12dip" >
</TextView>
<NumberPicker
android:id="@+id/number_picker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dip"
android:layout_marginTop="6dip" />
</LinearLayout>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto" >
<com.lukehorvat.NumberPickerDialogPreference
android:defaultValue="200"
android:dialogMessage="Please select the number of targets:"
android:max="250"
android:title="Total number of targets"
custom:min="100" />
</PreferenceScreen>
@rolinh
Copy link

rolinh commented Apr 3, 2014

I added a one liner to prevent the keyboard from showing up (because it is annoying). See change here: https://gist.github.com/Rolinh/9960021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment