/*
* Copyright (C) 2013 jonas.oreland@gmail.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.runnerup.widget;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Handler;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import org.runnerup.R;
@TargetApi(Build.VERSION_CODES.FROYO)
public class NumberPicker extends LinearLayout {
public interface OnChangedListener {
void onChanged(NumberPicker picker, int oldVal, int newVal);
}
public interface Formatter {
String toString(int value);
}
int prevValue;
int currValue;
int minValue = MIN_VAL;
int maxValue = MAX_VAL;
boolean wrapValue = true;
final static int DIGITS = 2;
final static int MIN_VAL = 0;
final static int MAX_VAL = 59;
EditText valueText;
OnChangedListener listener;
Button decButton;
Button incButton;
boolean longInc = false;
boolean longDec = false;
final Handler longHandler = new Handler();
final long longSpeed = 300;
final int textSize = 25;
int digits = DIGITS;
String fmtString = "%0" + digits + "d";
public NumberPicker(Context context, AttributeSet attrs) {
super(context, attrs);
createValueText(context);
createButton(context, '+');
createButton(context, '-');
setPadding(5, 5, 5, 5);
setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
addViews();
updateView();
if (attrs != null) {
TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.TitleSpinner);
processAttributes(arr);
arr.recycle();
}
}
private void processAttributes(TypedArray arr) {
if (arr == null)
return;
if (arr.hasValue(R.styleable.TitleSpinner_digits)) {
setDigits(arr.getInt(R.styleable.TitleSpinner_digits, digits));
}
if (arr.hasValue(R.styleable.TitleSpinner_min_val)) {
minValue = arr.getInt(R.styleable.TitleSpinner_min_val, minValue);
}
if (arr.hasValue(R.styleable.TitleSpinner_max_val)) {
maxValue = arr.getInt(R.styleable.TitleSpinner_max_val, maxValue);
}
}
private void addViews() {
LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
if (this.getOrientation() == VERTICAL) {
addView(incButton, lp);
addView(valueText, lp);
addView(decButton, lp);
} else {
addView(decButton, lp);
addView(valueText, lp);
addView(incButton, lp);
}
}
private void createButton(Context context, char c) {
Button b = new Button(context);
b.setText("" + c);
b.setTextSize(textSize);
b.setOnClickListener(buttonClick);
b.setOnLongClickListener(buttonLongClick);
b.setOnTouchListener(buttonLongTouchListener);
b.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
if (c == '+')
incButton = b;
else
decButton = b;
}
private void createValueText(Context context) {
valueText = new EditText(context);
valueText.setTextSize(textSize);
valueText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
valueText.selectAll();
} else {
validateInput(valueText);
}
}
});
valueText.setInputType(InputType.TYPE_CLASS_NUMBER);
valueText.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
}
final Runnable longPressUpdater = new Runnable() {
public void run() {
if (longInc) {
setValueImpl(currValue + 1);
} else if (longDec) {
setValueImpl(currValue - 1);
} else {
return;
}
longHandler.postDelayed(this, longSpeed);
}
};
private void setValueImpl(int newValue) {
if (newValue < minValue) {
if (wrapValue)
newValue = maxValue;
else
newValue = minValue;
} else if (newValue > maxValue) {
if (wrapValue)
newValue = minValue;
else
newValue = maxValue;
}
int save = prevValue;
prevValue = currValue;
currValue = newValue;
if (listener != null)
listener.onChanged(this, save, newValue);
updateView();
}
private void updateView() {
valueText.setText(formatter.toString(currValue));
valueText.selectAll();
}
final OnClickListener buttonClick = new OnClickListener() {
@Override
public void onClick(View v) {
validateInput(valueText);
if (!valueText.hasFocus())
{
valueText.requestFocus();
}
int diff = v == incButton ? 1 : -1;
setValueImpl(currValue + diff);
}
};
void buttonLongClick(int i) {
valueText.clearFocus();
if (i < 0) {
longDec = true;
} else if (i > 0) {
longInc = true;
} else {
longInc = false;
longDec = false;
return;
}
longHandler.post(longPressUpdater);
}
final OnLongClickListener buttonLongClick = new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (v == incButton)
buttonLongClick(+1);
else
buttonLongClick(-1);
return true;
}
};
final OnTouchListener buttonLongTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP &&
((longInc && v == incButton) ||
(longDec && v == decButton))) {
buttonLongClick(0);
}
return false;
}
};
protected void validateInput(EditText tv) {
String str = String.valueOf(tv.getText());
if ("".equals(str)) {
updateView();
} else {
try {
int l = Integer.valueOf(str);
setValueImpl(l);
} catch (NumberFormatException ex) {
}
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
incButton.setEnabled(enabled);
decButton.setEnabled(enabled);
valueText.setEnabled(enabled);
if (!enabled) {
longInc = false;
longDec = false;
}
}
@Override
public void setOrientation(int orientation) {
if (getOrientation() != orientation) {
super.setOrientation(orientation);
readd();
}
}
final Formatter formatter = new Formatter() {
final StringBuilder builder = new StringBuilder();
final java.util.Formatter fmt = new java.util.Formatter(builder);
final Object[] args = new Object[1];
public String toString(int value) {
args[0] = value;
builder.delete(0, builder.length());
fmt.format(fmtString, args);
return fmt.toString();
}
};
public void setRange(int min, int max, boolean wrap) {
this.minValue = min;
this.maxValue = max;
this.wrapValue = wrap;
}
public void setDigits(int digits) {
this.digits = digits;
fmtString = "%0" + digits + "d";
updateView();
readd();
}
public void setValue(int newValue) {
setValueImpl(newValue);
}
public int getValue() {
validateInput(valueText);
return currValue;
}
private void readd() {
removeView(incButton);
removeView(decButton);
removeView(valueText);
addViews();
}
}