PageRenderTime 44ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Android/StreamlindineTabletop/src/com/streamlinedine/custom_component/NumberPicker.java

https://bitbucket.org/vincentjames501/cs394
Java | 433 lines | 290 code | 64 blank | 79 comment | 45 complexity | 281b55d183f30d509c82c784c010df6e MD5 | raw file
  1. /*
  2. * Copyright (C) 2008 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.streamlinedine.custom_component;
  17. import android.content.Context;
  18. import android.os.Handler;
  19. import android.text.InputFilter;
  20. import android.text.InputType;
  21. import android.text.Spanned;
  22. import android.text.method.NumberKeyListener;
  23. import android.util.AttributeSet;
  24. import android.view.LayoutInflater;
  25. import android.view.View;
  26. import android.view.View.OnClickListener;
  27. import android.view.View.OnFocusChangeListener;
  28. import android.view.View.OnLongClickListener;
  29. import android.widget.TextView;
  30. import android.widget.LinearLayout;
  31. import android.widget.EditText;
  32. import com.streamlinedine.R;
  33. /**
  34. * This class has been pulled from the Android platform source code.
  35. *
  36. * @author Google
  37. */
  38. public class NumberPicker extends LinearLayout implements OnClickListener,
  39. OnFocusChangeListener, OnLongClickListener {
  40. private static final String TAG = "NumberPicker";
  41. private static final int DEFAULT_MAX = 20;
  42. private static final int DEFAULT_MIN = 1;
  43. public interface OnChangedListener {
  44. void onChanged(NumberPicker picker, int oldVal, int newVal);
  45. }
  46. public interface Formatter {
  47. String toString(int value);
  48. }
  49. /*
  50. * Use a custom NumberPicker formatting callback to use two-digit
  51. * minutes strings like "01". Keeping a static formatter etc. is the
  52. * most efficient way to do this; it avoids creating temporary objects
  53. * on every call to format().
  54. */
  55. public static final Formatter TWO_DIGIT_FORMATTER =
  56. new Formatter() {
  57. final StringBuilder mBuilder = new StringBuilder();
  58. final java.util.Formatter mFmt = new java.util.Formatter(mBuilder);
  59. final Object[] mArgs = new Object[1];
  60. public String toString(int value) {
  61. mArgs[0] = value;
  62. mBuilder.delete(0, mBuilder.length());
  63. mFmt.format("%02d", mArgs);
  64. return mFmt.toString();
  65. }
  66. };
  67. private final Handler mHandler;
  68. private final Runnable mRunnable = new Runnable() {
  69. public void run() {
  70. if (mIncrement) {
  71. changeCurrent(mCurrent + 1);
  72. mHandler.postDelayed(this, mSpeed);
  73. } else if (mDecrement) {
  74. changeCurrent(mCurrent - 1);
  75. mHandler.postDelayed(this, mSpeed);
  76. }
  77. }
  78. };
  79. private final EditText mText;
  80. private final InputFilter mNumberInputFilter;
  81. private String[] mDisplayedValues;
  82. protected int mStart;
  83. protected int mEnd;
  84. protected int mCurrent;
  85. protected int mPrevious;
  86. private OnChangedListener mListener;
  87. private Formatter mFormatter;
  88. private long mSpeed = 300;
  89. private boolean mIncrement;
  90. private boolean mDecrement;
  91. public NumberPicker(Context context) {
  92. this(context, null);
  93. }
  94. public NumberPicker(Context context, AttributeSet attrs) {
  95. this(context, attrs, 0);
  96. }
  97. @SuppressWarnings({"UnusedDeclaration"})
  98. public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
  99. super(context, attrs);
  100. setOrientation(VERTICAL);
  101. LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  102. inflater.inflate(R.layout.number_picker, this, true);
  103. mHandler = new Handler();
  104. InputFilter inputFilter = new NumberPickerInputFilter();
  105. mNumberInputFilter = new NumberRangeKeyListener();
  106. mIncrementButton = (NumberPickerButton) findViewById(R.id.increment);
  107. mIncrementButton.setOnClickListener(this);
  108. mIncrementButton.setOnLongClickListener(this);
  109. mIncrementButton.setNumberPicker(this);
  110. mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement);
  111. mDecrementButton.setOnClickListener(this);
  112. mDecrementButton.setOnLongClickListener(this);
  113. mDecrementButton.setNumberPicker(this);
  114. mText = (EditText) findViewById(R.id.timepicker_input);
  115. mText.setOnFocusChangeListener(this);
  116. mText.setFilters(new InputFilter[]{inputFilter});
  117. mText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
  118. if (!isEnabled()) {
  119. setEnabled(false);
  120. }
  121. mStart = DEFAULT_MIN;
  122. mEnd = DEFAULT_MAX;
  123. }
  124. public void setMText(String value) {
  125. mText.setText(value);
  126. }
  127. @Override
  128. public void setEnabled(boolean enabled) {
  129. super.setEnabled(enabled);
  130. mIncrementButton.setEnabled(enabled);
  131. mDecrementButton.setEnabled(enabled);
  132. mText.setEnabled(enabled);
  133. }
  134. public void setOnChangeListener(OnChangedListener listener) {
  135. mListener = listener;
  136. }
  137. public void setFormatter(Formatter formatter) {
  138. mFormatter = formatter;
  139. }
  140. /**
  141. * Set the range of numbers allowed for the number picker. The current
  142. * value will be automatically set to the start.
  143. *
  144. * @param start the start of the range (inclusive)
  145. * @param end the end of the range (inclusive)
  146. */
  147. public void setRange(int start, int end) {
  148. mStart = start;
  149. mEnd = end;
  150. mCurrent = start;
  151. updateView();
  152. }
  153. /**
  154. * Set the range of numbers allowed for the number picker. The current
  155. * value will be automatically set to the start. Also provide a mapping
  156. * for values used to display to the user.
  157. *
  158. * @param start the start of the range (inclusive)
  159. * @param end the end of the range (inclusive)
  160. * @param displayedValues the values displayed to the user.
  161. */
  162. public void setRange(int start, int end, String[] displayedValues) {
  163. mDisplayedValues = displayedValues;
  164. mStart = start;
  165. mEnd = end;
  166. mCurrent = start;
  167. updateView();
  168. }
  169. public void setCurrent(int current) {
  170. mCurrent = current;
  171. updateView();
  172. }
  173. /**
  174. * The speed (in milliseconds) at which the numbers will scroll
  175. * when the the +/- buttons are longpressed. Default is 300ms.
  176. */
  177. public void setSpeed(long speed) {
  178. mSpeed = speed;
  179. }
  180. public void onClick(View v) {
  181. validateInput(mText);
  182. if (!mText.hasFocus()) mText.requestFocus();
  183. // now perform the increment/decrement
  184. if (R.id.increment == v.getId()) {
  185. changeCurrent(mCurrent + 1);
  186. } else if (R.id.decrement == v.getId()) {
  187. changeCurrent(mCurrent - 1);
  188. }
  189. }
  190. private String formatNumber(int value) {
  191. return (mFormatter != null)
  192. ? mFormatter.toString(value)
  193. : String.valueOf(value);
  194. }
  195. protected void changeCurrent(int current) {
  196. // Wrap around the values if we go past the start or end
  197. if (current > mEnd) {
  198. current = mStart;
  199. } else if (current < mStart) {
  200. current = mEnd;
  201. }
  202. mPrevious = mCurrent;
  203. mCurrent = current;
  204. notifyChange();
  205. updateView();
  206. }
  207. protected void notifyChange() {
  208. if (mListener != null) {
  209. mListener.onChanged(this, mPrevious, mCurrent);
  210. }
  211. }
  212. protected void updateView() {
  213. /* If we don't have displayed values then use the
  214. * current number else find the correct value in the
  215. * displayed values for the current number.
  216. */
  217. if (mDisplayedValues == null) {
  218. mText.setText(formatNumber(mCurrent));
  219. } else {
  220. mText.setText(mDisplayedValues[mCurrent - mStart]);
  221. }
  222. mText.setSelection(mText.getText().length());
  223. }
  224. private void validateCurrentView(CharSequence str) {
  225. int val = getSelectedPos(str.toString());
  226. if ((val >= mStart) && (val <= mEnd)) {
  227. if (mCurrent != val) {
  228. mPrevious = mCurrent;
  229. mCurrent = val;
  230. notifyChange();
  231. }
  232. }
  233. updateView();
  234. }
  235. public void onFocusChange(View v, boolean hasFocus) {
  236. /* When focus is lost check that the text field
  237. * has valid values.
  238. */
  239. if (!hasFocus) {
  240. validateInput(v);
  241. }
  242. }
  243. private void validateInput(View v) {
  244. String str = String.valueOf(((TextView) v).getText());
  245. if ("".equals(str)) {
  246. // Restore to the old value as we don't allow empty values
  247. updateView();
  248. } else {
  249. // Check the new value and ensure it's in range
  250. validateCurrentView(str);
  251. }
  252. }
  253. /**
  254. * We start the long click here but rely on the {@link NumberPickerButton}
  255. * to inform us when the long click has ended.
  256. */
  257. public boolean onLongClick(View v) {
  258. /* The text view may still have focus so clear it's focus which will
  259. * trigger the on focus changed and any typed values to be pulled.
  260. */
  261. mText.clearFocus();
  262. if (R.id.increment == v.getId()) {
  263. mIncrement = true;
  264. mHandler.post(mRunnable);
  265. } else if (R.id.decrement == v.getId()) {
  266. mDecrement = true;
  267. mHandler.post(mRunnable);
  268. }
  269. return true;
  270. }
  271. public void cancelIncrement() {
  272. mIncrement = false;
  273. }
  274. public void cancelDecrement() {
  275. mDecrement = false;
  276. }
  277. private static final char[] DIGIT_CHARACTERS = new char[]{
  278. '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
  279. };
  280. private NumberPickerButton mIncrementButton;
  281. private NumberPickerButton mDecrementButton;
  282. private class NumberPickerInputFilter implements InputFilter {
  283. public CharSequence filter(CharSequence source, int start, int end,
  284. Spanned dest, int dstart, int dend) {
  285. if (mDisplayedValues == null) {
  286. return mNumberInputFilter.filter(source, start, end, dest, dstart, dend);
  287. }
  288. CharSequence filtered = String.valueOf(source.subSequence(start, end));
  289. String result = String.valueOf(dest.subSequence(0, dstart))
  290. + filtered
  291. + dest.subSequence(dend, dest.length());
  292. String str = String.valueOf(result).toLowerCase();
  293. for (String val : mDisplayedValues) {
  294. val = val.toLowerCase();
  295. if (val.startsWith(str)) {
  296. return filtered;
  297. }
  298. }
  299. return "";
  300. }
  301. }
  302. private class NumberRangeKeyListener extends NumberKeyListener {
  303. // XXX This doesn't allow for range limits when controlled by a
  304. // soft input method!
  305. public int getInputType() {
  306. return InputType.TYPE_CLASS_NUMBER;
  307. }
  308. @Override
  309. protected char[] getAcceptedChars() {
  310. return DIGIT_CHARACTERS;
  311. }
  312. @Override
  313. public CharSequence filter(CharSequence source, int start, int end,
  314. Spanned dest, int dstart, int dend) {
  315. CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
  316. if (filtered == null) {
  317. filtered = source.subSequence(start, end);
  318. }
  319. String result = String.valueOf(dest.subSequence(0, dstart))
  320. + filtered
  321. + dest.subSequence(dend, dest.length());
  322. if ("".equals(result)) {
  323. return result;
  324. }
  325. int val = getSelectedPos(result);
  326. /* Ensure the user can't type in a value greater
  327. * than the max allowed. We have to allow less than min
  328. * as the user might want to delete some numbers
  329. * and then type a new number.
  330. */
  331. if (val > mEnd) {
  332. return "";
  333. } else {
  334. return filtered;
  335. }
  336. }
  337. }
  338. private int getSelectedPos(String str) {
  339. if (mDisplayedValues == null) {
  340. return Integer.parseInt(str);
  341. } else {
  342. for (int i = 0; i < mDisplayedValues.length; i++) {
  343. /* Don't force the user to type in jan when ja will do */
  344. str = str.toLowerCase();
  345. if (mDisplayedValues[i].toLowerCase().startsWith(str)) {
  346. return mStart + i;
  347. }
  348. }
  349. /* The user might have typed in a number into the month field i.e.
  350. * 10 instead of OCT so support that too.
  351. */
  352. try {
  353. return Integer.parseInt(str);
  354. } catch (NumberFormatException e) {
  355. /* Ignore as if it's not a number we don't care */
  356. }
  357. }
  358. return mStart;
  359. }
  360. /**
  361. * @return the current value.
  362. */
  363. public int getCurrent() {
  364. return mCurrent;
  365. }
  366. public EditText getmText() {
  367. return mText;
  368. }
  369. }