/* The following code was written by Matthew Wiggins * and is released under the APACHE 2.0 license * * http://www.apache.org/licenses/LICENSE-2.0 * * see http://android.hlidskialf.com/post/android-seekbar-preference */ package com.hlidskialf.android.preference; import android.content.Context; import android.content.DialogInterface; import android.preference.DialogPreference; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.View.OnFocusChangeListener; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; public class SeekBarPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener, View.OnTouchListener, OnFocusChangeListener, OnEditorActionListener { private static final String androidns = "http://schemas.android.com/apk/res/android"; private SeekBar mSeekBar; private TextView mSplashText; private EditText mValueText; private Context mContext; private String mDialogMessage, mSuffix; private int mDefault, mMax, mValue = 0; public SeekBarPreference(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; String dialogMessage = attrs.getAttributeValue(androidns, "dialogMessage"); if (dialogMessage != null && dialogMessage.startsWith("@")) mDialogMessage = context.getString(Integer.valueOf(dialogMessage .substring(1))); else mDialogMessage = dialogMessage; String text = attrs.getAttributeValue(androidns, "text"); if (text != null && text.startsWith("@")) mSuffix = context.getString(Integer.valueOf(text.substring(1))); else mSuffix = text; mDefault = attrs.getAttributeIntValue(androidns, "defaultValue", 0); mMax = attrs.getAttributeIntValue(androidns, "max", 100); setPositiveButtonText("Close"); setNegativeButtonText(null); } @Override protected View onCreateDialogView() { LinearLayout.LayoutParams params; LinearLayout layout = new LinearLayout(mContext); layout.setOrientation(LinearLayout.VERTICAL); layout.setPadding(6, 6, 6, 6); mSplashText = new TextView(mContext); if (mDialogMessage != null) mSplashText.setText(mDialogMessage); layout.addView(mSplashText); mValueText = new EditText(mContext); mValueText.setGravity(Gravity.CENTER_HORIZONTAL); mValueText.setTextSize(32); params = new LinearLayout.LayoutParams( LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); layout.addView(mValueText, params); mSeekBar = new SeekBar(mContext); mSeekBar.setOnSeekBarChangeListener(this); layout.addView(mSeekBar, params); if (shouldPersist()) mValue = getPersistedInt(mDefault); mSeekBar.setMax(modFunc(mMax)); mValueText.setOnTouchListener(this); mValueText.setOnFocusChangeListener(this); mValueText.setOnEditorActionListener(this); mValueText.setInputType(InputType.TYPE_CLASS_NUMBER); mValueText.setFocusableInTouchMode(false); dispatchValue(mValue); return layout; } @Override protected void onBindDialogView(View v) { super.onBindDialogView(v); mSeekBar.setMax(modFunc(mMax)); setProgress(mValue); } @Override protected void onSetInitialValue(boolean restore, Object defaultValue) { super.onSetInitialValue(restore, defaultValue); if (restore) mValue = shouldPersist() ? getPersistedInt(mDefault) : 0; else mValue = (Integer) defaultValue; } // called if the user touches the text edit box @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { mValueText.setFocusableInTouchMode(true); CharSequence text = ((TextView) v).getText(); if (TextUtils.isEmpty(text) || !TextUtils.isDigitsOnly(text)) { // on Touch, remove the suffix if there is one. // If the user is already editing, then the suffix will already be gone // and in that case, don't change the value on the user. ((TextView) v).setText(Integer.toString(mValue)); } } return false; } // called when the user hits enter on the text edit box // sets the progress bar value, which flows on to other // values @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { setProgress(v.getText()); return true; } @Override public void onClick(DialogInterface dialog, int which) { setProgress(mValueText.getText()); } private void setProgress(CharSequence text) { if ((!TextUtils.isEmpty(text))&&(TextUtils.isDigitsOnly(text))) setProgress(Integer.parseInt(text.toString())); } // set the Progress bar to a given value. // will in turn cause onProgressChanged() to be called public void setProgress(int progress) { int modifiedValue = modFunc(progress); if (mSeekBar != null) mSeekBar.setProgress(modifiedValue); dispatchValue(progress); } public int getProgress() { return mValue; } public void onProgressChanged(SeekBar seek, int modifiedValue, boolean fromTouch) { int value = invModFunc(modifiedValue); String t = String.valueOf(value); if (mValueText != null) mValueText.setText(mSuffix == null ? t : t.concat(mSuffix)); mValue = value; } public void onStartTrackingTouch(SeekBar seek) { } public void onStopTrackingTouch(SeekBar seek) { dispatchValue(mValue); } private void dispatchValue(int value) { String t = String.valueOf(value); if (mValueText != null) mValueText.setText(mSuffix == null ? t : t.concat(mSuffix)); mValue = value; if (shouldPersist()) persistInt(value); } @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) ((TextView) v).setText(Integer.toString(mValue)); else setProgress(((TextView) v).getText()); } public void setMax(int max) { mMax = max; if (mSeekBar != null) mSeekBar.setMax(modFunc(mMax)); } public int getMax() { return mMax; } protected int modFunc(int value) { return (int)Math.sqrt(value); } protected int invModFunc(int value) { return value*value; } }