値の範囲を設定できるプリファレンス「RangeSelectPreference」を作ってみた

Androidの設定画面で使われるプリファレンスを作りました。
今回のモノは、先日の投稿の「Androidで範囲を選択する為のSeekBar的なカスタムビュー「RangeSelectBar」をつくってみた。」を使って作成しました。

ポイント
・表示する値に書式を指定できる。
・カスタムダイアログに表示するレイアウトをxmlで作成した。
・プリファレンスで保存される値が「<value>,<value>」なのでそれぞれの値を取得する為のstaticメソッドを作成した。


スクリーンショット
実際に使ってみた感じです。
時間の範囲を選択できるようにしたかったので値を「%d:00」という書式指定を使って表現しています。


というわけで、今回は前回の投稿のクラスが必要ですのでご注意ください。
ブログに貼ってあるコードよりGoogleCodeの方が新しくなっています。

このクラスは「GoogleCode relog」にも保存されてますのでよろしければどうぞ。



プリファレンスの組み込み方
<jp.xii.relog.customlibrary.preference.RangeSelectPreference
	android:key="@string/m8102_mute_time"
	android:title="@string/m1102_mute_time"
	max="24"
	min="0"
	step="1"
	default_first="22"
	default_last="8"
	unit="24hour"
	value_format="%d:00"
	>


クラス
package jp.xii.relog.customlibrary.preference;

import jp.xii.relog.customlibrary.widget.RangeSelectBar;
import jp.xii.relog.customlibrary.widget.RangeSelectBar.onRangeSelectBarChangeListener;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.DialogInterface.OnClickListener;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

public class RangeSelectPreference extends OriginalDialogPreference {

	public final static String STR_ATTR_UNIT = "unit";		//単位
	public final static String STR_ATTR_VALUE_FORMAT = "value_format";	//値のフォーマット書式
	
	private int _max = 100;		//最大値
	private int _min = 0;		//最小値
	private int _step = 10;		//可変値
	
	private int _first = 40;		//最初の値
	private int _last = 80;			//最後の値
	private boolean _isLoop = true;		//左右がループするか

	private String _unitString = "";	//単位の文字列(サマリの表示用)
	private String _valueFormatString = "";	//値のフォーマット書式指定
	
	/**
	 * 最大値
	 * @param _max the _max to set
	 */
	public void setMax(int _max) {
		if(_max <= getMin()){
			_max = getMin() + 1; 
		}
		this._max = _max;
	}
	/**
	 * 最大値
	 * @return the _max
	 */
	public int getMax() {
		return _max;
	}

	/**
	 * 最小値
	 * @param _min the _min to set
	 */
	public void setMin(int _min) {
		if(_min >= getMax()){
			_min = getMax() - 1;
		}if(_min < 0){
			_min = 0;
		}
		this._min = _min;
	}
	/**
	 * 最小値
	 * @return the _min
	 */
	public int getMin() {
		return _min;
	}
		
	/**
	 * 可変値
	 * @param _step the _step to set
	 */
	public void setStep(int _step) {
		this._step = _step;
	}
	/**
	 * 可変値
	 * @return the _step
	 */
	public int getStep() {
		return _step;
	}
	
	
	/**
	 * 最初の値
	 * @param _first the _first to set
	 */
	public void setFirst(int _first) {
		this._first = _first;
	}
	/**
	 * 最初の値
	 * @return the _first
	 */
	public int getFirst() {
		return _first;
	}
	/**
	 * 最後の値
	 * @param _last the _last to set
	 */
	public void setLast(int _last) {
		this._last = _last;
	}
	/**
	 * 最後の値
	 * @return the _last
	 */
	public int getLast() {
		return _last;
	}
	/**
	 * 左右がループするか
	 * @param _isLoop the _isLoop to set
	 */
	public void setIsLoop(boolean _isLoop) {
		this._isLoop = _isLoop;
	}
	/**
	 * 左右がループするか
	 * @return the _isLoop
	 */
	public boolean isLoop() {
		return _isLoop;
	}

	/**
	 * 単位の文字列(サマリの表示用)
	 * @param _unitString the _unitString to set
	 */
	public void setUnitString(String _unitString) {
		this._unitString = _unitString;
	}
	/**
	 * 単位の文字列(サマリの表示用)
	 * @return the _unitString
	 */
	public String getUnitString() {
		return _unitString;
	}
	
	/**
	 * 値のフォーマット書式指定
	 * @param _valueFormatString the _valueFormatString to set
	 */
	public void setValueFormatString(String _valueFormatString) {
		this._valueFormatString = _valueFormatString;
	}
	/**
	 * 値のフォーマット書式指定
	 * @return the _valueFormatString
	 */
	public String getValueFormatString() {
		return _valueFormatString;
	}
	
	
	/**
	 * コンストラクタ
	 * @param context
	 * @param attrs
	 */
	public RangeSelectPreference(Context context, AttributeSet attrs) {
		super(context, attrs);

		String temp = null;

		//最大値
		temp = attrs.getAttributeValue(null, RangeSelectBar.STR_ATTR_MAX);
		if(temp != null){
			setMax(Integer.parseInt(temp));
		}
		//最小値
		temp = attrs.getAttributeValue(null, RangeSelectBar.STR_ATTR_MIN);
		if(temp != null){
			setMin(Integer.parseInt(temp));
		}
		//可変値
		temp = attrs.getAttributeValue(null, RangeSelectBar.STR_ATTR_STEP);
		if(temp != null){
			setStep(Integer.parseInt(temp));
		}
		
		//最初の値の初期値
		temp = attrs.getAttributeValue(null, RangeSelectBar.STR_ATTR_DEFAULT_FIRST);
		if(temp != null){
			setFirst(Integer.parseInt(temp));
		}
		//最後の値の初期値
		temp = attrs.getAttributeValue(null, RangeSelectBar.STR_ATTR_DEFAULT_LAST);
		if(temp != null){
			setLast(Integer.parseInt(temp));
		}

		//単位
		temp = attrs.getAttributeValue(null, STR_ATTR_UNIT);
		if(temp != null){
			setUnitString(temp);
		}
		
		//書式指定文字列
		temp = attrs.getAttributeValue(null, STR_ATTR_VALUE_FORMAT);
		if(temp != null){
			setValueFormatString(temp);
		}
		

		//不正な値を治す
		if(getFirst() < getMin()){
			setFirst(getMin());
		}
		if(getLast() > getMax()){
			setLast(getMax());
		}
	}
	

	/**
	 * 表示したときに呼ばれる
	 */
	@Override
	protected void onBindView(View view) {
		
		//設定読込
		SharedPreferences pref = getSharedPreferences();
		if(pref == null){
		}else{
			String temp = "";
			String[] values = null;
			temp = pref.getString(getKey(), temp);
			values = temp.split(",");
			if(values == null){
			}else if(values.length < 2){
			}else{
				setFirst(Integer.valueOf(values[0]));
				setLast(Integer.valueOf(values[1]));
			}
		}
		
		//サマリに表示する
		if(getUnitString().length() == 0){
			setSummary("Range : " + getFirst() + " - " + getLast());
		}else{
			setSummary("Range(" + getUnitString() + ") : " + getFirst() + " - " + getLast());
		}
		
		super.onBindView(view);
	}

	/**
	 * クリックイベント
	 */
	@Override
	protected void onClick() {


	    //inflaterを使ってxmlのレイアウトをViewに反映する
	    LayoutInflater inflater = (LayoutInflater)getContext()
	                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	    View v = inflater.inflate(jp.xii.relog.customlibrary.R.layout.range_select_preference_dialog, null);
		final RangeSelectBar bar = (RangeSelectBar)v.findViewById(jp.xii.relog.customlibrary.R.id.RangeSelect_RangeSelectBar);
		final TextView text_first = (TextView)v.findViewById(jp.xii.relog.customlibrary.R.id.LavelFirst_TextView);
		final TextView text_last = (TextView)v.findViewById(jp.xii.relog.customlibrary.R.id.LavelLast_TextView);
		TextView text_min = (TextView)v.findViewById(jp.xii.relog.customlibrary.R.id.LavelMin_TextView);
		TextView text_max = (TextView)v.findViewById(jp.xii.relog.customlibrary.R.id.LavelMax_TextView);
		TextView text_center = (TextView)v.findViewById(jp.xii.relog.customlibrary.R.id.LavelCenter_TextView);
		bar.setMax(getMax());
		bar.setMin(getMin());
		bar.setFirst(getFirst());
		bar.setLast(getLast());
		bar.setStep(getStep());
		setFormatText(text_first, getFirst());
		setFormatText(text_last, getLast());
		setFormatText(text_max, getMax());
		setFormatText(text_min, getMin());
		setFormatText(text_center, (getMax() + getMin()) / 2);
		
		//値変更時のイベント
		bar.setOnRangeSelectBarChangeListener(new onRangeSelectBarChangeListener() {
			@Override
			public void onProgressChanged(RangeSelectBar rangeSelectBar, int first,
					int last) {
				//ダイアログの現在の値を設定する
				setFormatText(text_first, first);
				setFormatText(text_last, last);
			}
		});
		
		
	    //ダイアログ表示
		showCustumDialog(getContext()
						, (String)getDialogTitle(), (String)getDialogMessage()
						, v, new OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				//ダイアログのビューから結果を取得
				if(bar != null){
					setFirst(bar.getFirst());
					setLast(bar.getLast());
				}
				// 設定保存
				SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getContext());
				SharedPreferences.Editor editor = pref.edit();
				editor.putString(getKey(), getFirst() + "," + getLast());
				editor.commit();
				
				//表示を更新
				notifyChanged();
			}
		});
	}
	
	/**
	 * 書式指定の文字列をテキストビューに指定する
	 * @param v
	 * @param value
	 */
	private void setFormatText(TextView v, int value){
		if(v == null){
		}else{
			try{
				v.setText(String.format(getValueFormatString(), value));
			}catch(Exception e){
				v.setText(String.valueOf(value));
			}
		}
	}

	/**
	 * プリファレンスに保存された文字列から前の値を取得する
	 * @param value
	 * @return
	 */
	static public int getPref2First(String value){
		int ret = 0;
		String[] values = null;
		values = value.split(",");
		if(values == null){
		}else if(values.length < 2){
		}else{
			ret = Integer.valueOf(values[0]);
		}
		return ret;
	}

	/**
	 * プリファレンスに保存された文字列から後の値を取得する
	 * @param value
	 * @return
	 */
	static public int getPref2Last(String value){
		int ret = 0;
		String[] values = null;
		values = value.split(",");
		if(values == null){
		}else if(values.length < 2){
		}else{
			ret = Integer.valueOf(values[1]);
		}
		return ret;
	}

}



レイアウトxml
ファイル名は「range_select_preference_dialog.xml」にしています。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
		xmlns:android="http://schemas.android.com/apk/res/android"
		android:layout_width="fill_parent"
		android:layout_height="fill_parent"
		android:orientation="vertical"
		android:paddingLeft="10dip"
		android:paddingRight="10dip"
		android:paddingBottom="10dip"
		>

	<LinearLayout
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			>
		<TextView
				android:id="@+id/LavelFirst_TextView"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content"
				android:layout_weight="1"
				android:paddingRight="10dip"
				android:gravity="right"
				android:text="0"
				android:textSize="24dip"
				/>
		<TextView
				android:layout_width="wrap_content"
				android:layout_height="wrap_content"
				android:text=" - "
				android:textSize="24dip"
				/>
		<TextView
				android:id="@+id/LavelLast_TextView"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content"
				android:layout_weight="1"
				android:paddingLeft="10dip"
				android:gravity="left"
				android:text="0"
				android:textSize="24dip"
				/>
	</LinearLayout>
	<LinearLayout
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			>
		<TextView
				android:id="@+id/LavelMin_TextView"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content"
				android:text="0"
				android:gravity="left"
				android:layout_weight="1"
				/>
		<TextView
				android:id="@+id/LavelCenter_TextView"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content"
				android:text="50"
				android:gravity="center"
				android:layout_weight="1"
				/>
		<TextView
				android:id="@+id/LavelMax_TextView"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content"
				android:text="100"
				android:gravity="right"
				android:layout_weight="1"
				/>
	</LinearLayout>

	<jp.xii.relog.customlibrary.widget.RangeSelectBar
			android:id="@+id/RangeSelect_RangeSelectBar"
			android:layout_width="fill_parent"
			android:layout_height="wrap_content"
			max="100"
			min="0"
			step="10"
			default_first="25"
			default_last="75"
			/>

</LinearLayout>



基底クラス
package jp.xii.relog.customlibrary.preference;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface.OnClickListener;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;

public class OriginalDialogPreference extends DialogPreference {


	private String _summary = "";			//サマリーの文字列

	/**
	 * サマリの初期状態
	 * @param _summary the _summary to set
	 */
	public void setDefaultSummary(String _summary) {
		this._summary = _summary;
	}

	/**
	 * サマリの初期状態
	 * @return the _summary
	 */
	public String getDefaultSummary() {
		return _summary;
	}

	/**
	 * コンストラクタ
	 * @param context
	 * @param attrs
	 */
	public OriginalDialogPreference(Context context, AttributeSet attrs) {
		super(context, attrs);

		String temp;
		
		//サマリーの初期状態を保存しておく
		temp = (String) getSummary();
		if(temp != null){
			setDefaultSummary(temp);
		}

	}
	
	/**
	 * ダイアログの表示
	 * @param context 親のコンテキスト
	 * @param title ダイアログのタイトル
	 * @param text ダイアログの本文
	 */
	protected void showCustumDialog(Context context, String title, String text ,View custumItem, OnClickListener listener){
		AlertDialog.Builder ad = new AlertDialog.Builder(context);
		ad.setTitle(title);
		if(text != null){
			ad.setMessage(text);
		}
		ad.setView(custumItem);
		ad.setPositiveButton("OK", listener);
		ad.setNegativeButton("Cancel", null);
		ad.create();
		ad.show();
	}
	
	/**
	 * 数値に変換する
	 * @param number
	 * @return
	 */
	protected int toInt(String number){
		int num = 0;
		
		try{
			num = Integer.parseInt(number);
		}catch(Exception e){
			num = 0;
		}
		
		return num;
	}

	/**
	 * 数値か確かめる
	 * @param number
	 * @return
	 */
	protected boolean isInt(String number){
		boolean ret = false;
		
		try{
			Integer.parseInt(number);
			ret = true;
		}catch(Exception e){
			ret = false;
		}
		
		return ret;
	}


}