/[aagtl_public1]/src/com/example/widget/NumberPicker.java
aagtl

Contents of /src/com/example/widget/NumberPicker.java

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (show annotations) (download)
Sun Aug 5 13:48:36 2012 UTC (11 years, 7 months ago) by zoffadmin
File size: 14081 byte(s)
initial import of aagtl source code
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
17 package com.example.widget;
18
19 import android.content.Context;
20 import android.os.Handler;
21 import android.text.InputFilter;
22 import android.text.InputType;
23 import android.text.Spanned;
24 import android.text.method.NumberKeyListener;
25 import android.util.AttributeSet;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.View.OnClickListener;
29 import android.view.View.OnFocusChangeListener;
30 import android.view.View.OnLongClickListener;
31 import android.widget.EditText;
32 import android.widget.LinearLayout;
33 import android.widget.TextView;
34
35 import com.zoffcc.applications.aagtl.R;
36
37 /**
38 * This class has been pulled from the Android platform source code, its an internal widget that hasn't been
39 * made public so its included in the project in this fashion for use with the preferences screen; I have made
40 * a few slight modifications to the code here, I simply put a MAX and MIN default in the code but these values
41 * can still be set publically by calling code.
42 *
43 * @author Google
44 */
45 public class NumberPicker extends LinearLayout
46 implements
47 OnClickListener,
48 OnFocusChangeListener,
49 OnLongClickListener
50 {
51
52 private static final String TAG = "NumberPicker";
53 private static final int DEFAULT_MAX = 200;
54 private static final int DEFAULT_MIN = 0;
55
56 public interface OnChangedListener
57 {
58 void onChanged(NumberPicker picker, int oldVal, int newVal);
59 }
60
61 public interface Formatter
62 {
63 String toString(int value);
64 }
65
66 /*
67 * Use a custom NumberPicker formatting callback to use two-digit
68 * minutes strings like "01". Keeping a static formatter etc. is the
69 * most efficient way to do this; it avoids creating temporary objects
70 * on every call to format().
71 */
72 public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER = new NumberPicker.Formatter()
73 {
74 final StringBuilder mBuilder = new StringBuilder();
75 final java.util.Formatter mFmt = new java.util.Formatter(
76 mBuilder);
77 final Object[] mArgs = new Object[1];
78 public String toString(
79 int value)
80 {
81 mArgs[0] = value;
82 mBuilder
83 .delete(
84 0,
85 mBuilder
86 .length());
87 mFmt.format("%02d",
88 mArgs);
89 return mFmt
90 .toString();
91 }
92 };
93 public static final NumberPicker.Formatter THREE_DIGIT_FORMATTER = new NumberPicker.Formatter()
94 {
95 final StringBuilder mBuilder = new StringBuilder();
96 final java.util.Formatter mFmt = new java.util.Formatter(
97 mBuilder);
98 final Object[] mArgs = new Object[1];
99 public String toString(
100 int value)
101 {
102 mArgs[0] = value;
103 mBuilder
104 .delete(
105 0,
106 mBuilder
107 .length());
108 mFmt.format("%03d",
109 mArgs);
110 return mFmt
111 .toString();
112 }
113 };
114
115 private final Handler mHandler;
116 private final Runnable mRunnable = new Runnable()
117 {
118 public void run()
119 {
120 if (mIncrement)
121 {
122 changeCurrent(mCurrent + 1);
123 mHandler
124 .postDelayed(
125 this,
126 mSpeed);
127 }
128 else if (mDecrement)
129 {
130 changeCurrent(mCurrent - 1);
131 mHandler
132 .postDelayed(
133 this,
134 mSpeed);
135 }
136 }
137 };
138
139 private final EditText mText;
140 private final InputFilter mNumberInputFilter;
141
142 private String[] mDisplayedValues;
143 protected int mStart;
144 protected int mEnd;
145 protected int mCurrent;
146 protected int mPrevious;
147 private OnChangedListener mListener;
148 private Formatter mFormatter;
149 private long mSpeed = 300;
150
151 private boolean mIncrement;
152 private boolean mDecrement;
153
154 public NumberPicker(Context context)
155 {
156 this(context, null);
157 }
158
159 public NumberPicker(Context context, AttributeSet attrs)
160 {
161 this(context, attrs, 0);
162 }
163
164 @SuppressWarnings({"UnusedDeclaration"})
165 public NumberPicker(Context context, AttributeSet attrs, int defStyle)
166 {
167 super(context, attrs);
168 setOrientation(VERTICAL);
169 LayoutInflater inflater = (LayoutInflater) context
170 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
171 inflater.inflate(R.layout.number_picker, this, true);
172 mHandler = new Handler();
173 InputFilter inputFilter = new NumberPickerInputFilter();
174 mNumberInputFilter = new NumberRangeKeyListener();
175
176 mIncrementButton = (NumberPickerButton) findViewById(R.id.increment);
177 mIncrementButton.setOnClickListener(this);
178 mIncrementButton.setOnLongClickListener(this);
179 mIncrementButton.setNumberPicker(this);
180
181 mDecrementButton = (NumberPickerButton) findViewById(R.id.decrement);
182 mDecrementButton.setOnClickListener(this);
183 mDecrementButton.setOnLongClickListener(this);
184 mDecrementButton.setNumberPicker(this);
185
186 mText = (EditText) findViewById(R.id.timepicker_input);
187 mText.setOnFocusChangeListener(this);
188 mText.setFilters(new InputFilter[]{inputFilter});
189 mText.setRawInputType(InputType.TYPE_CLASS_NUMBER);
190
191 if (!isEnabled())
192 {
193 setEnabled(false);
194 }
195
196 mStart = DEFAULT_MIN;
197 mEnd = DEFAULT_MAX;
198 }
199
200 @Override
201 public void setEnabled(boolean enabled)
202 {
203 super.setEnabled(enabled);
204 mIncrementButton.setEnabled(enabled);
205 mDecrementButton.setEnabled(enabled);
206 mText.setEnabled(enabled);
207 }
208
209 public void setOnChangeListener(OnChangedListener listener)
210 {
211 mListener = listener;
212 }
213
214 public void setFormatter(Formatter formatter)
215 {
216 mFormatter = formatter;
217 }
218
219 /**
220 * Set the range of numbers allowed for the number picker. The current
221 * value will be automatically set to the start.
222 *
223 * @param start
224 * the start of the range (inclusive)
225 * @param end
226 * the end of the range (inclusive)
227 */
228 public void setRange(int start, int end)
229 {
230 mStart = start;
231 mEnd = end;
232 mCurrent = start;
233 updateView();
234 }
235
236 /**
237 * Set the range of numbers allowed for the number picker. The current
238 * value will be automatically set to the start. Also provide a mapping
239 * for values used to display to the user.
240 *
241 * @param start
242 * the start of the range (inclusive)
243 * @param end
244 * the end of the range (inclusive)
245 * @param displayedValues
246 * the values displayed to the user.
247 */
248 public void setRange(int start, int end, String[] displayedValues)
249 {
250 mDisplayedValues = displayedValues;
251 mStart = start;
252 mEnd = end;
253 mCurrent = start;
254 updateView();
255 }
256
257 public void setCurrent(int current)
258 {
259 mCurrent = current;
260 updateView();
261 }
262
263 /**
264 * The speed (in milliseconds) at which the numbers will scroll
265 * when the the +/- buttons are longpressed. Default is 300ms.
266 */
267 public void setSpeed(long speed)
268 {
269 mSpeed = speed;
270 }
271
272 public void onClick(View v)
273 {
274 validateInput(mText);
275 if (!mText.hasFocus()) mText.requestFocus();
276
277 // now perform the increment/decrement
278 if (R.id.increment == v.getId())
279 {
280 changeCurrent(mCurrent + 1);
281 }
282 else if (R.id.decrement == v.getId())
283 {
284 changeCurrent(mCurrent - 1);
285 }
286 }
287
288 private String formatNumber(int value)
289 {
290 return (mFormatter != null) ? mFormatter.toString(value) : String.valueOf(value);
291 }
292
293 protected void changeCurrent(int current)
294 {
295
296 // Wrap around the values if we go past the start or end
297 if (current > mEnd)
298 {
299 current = mStart;
300 }
301 else if (current < mStart)
302 {
303 current = mEnd;
304 }
305 mPrevious = mCurrent;
306 mCurrent = current;
307
308 notifyChange();
309 updateView();
310 }
311
312 protected void notifyChange()
313 {
314 if (mListener != null)
315 {
316 mListener.onChanged(this, mPrevious, mCurrent);
317 }
318 }
319
320 protected void updateView()
321 {
322
323 /*
324 * If we don't have displayed values then use the
325 * current number else find the correct value in the
326 * displayed values for the current number.
327 */
328 if (mDisplayedValues == null)
329 {
330 mText.setText(formatNumber(mCurrent));
331 }
332 else
333 {
334 mText.setText(mDisplayedValues[mCurrent - mStart]);
335 }
336 mText.setSelection(mText.getText().length());
337 }
338
339 private void validateCurrentView(CharSequence str)
340 {
341 int val = getSelectedPos(str.toString());
342 if ((val >= mStart) && (val <= mEnd))
343 {
344 if (mCurrent != val)
345 {
346 mPrevious = mCurrent;
347 mCurrent = val;
348 notifyChange();
349 }
350 }
351 updateView();
352 }
353
354 public void onFocusChange(View v, boolean hasFocus)
355 {
356
357 /*
358 * When focus is lost check that the text field
359 * has valid values.
360 */
361 if (!hasFocus)
362 {
363 validateInput(v);
364 }
365 }
366
367 private void validateInput(View v)
368 {
369 String str = String.valueOf(((TextView) v).getText());
370 if ("".equals(str))
371 {
372
373 // Restore to the old value as we don't allow empty values
374 updateView();
375 }
376 else
377 {
378
379 // Check the new value and ensure it's in range
380 validateCurrentView(str);
381 }
382 }
383
384 /**
385 * We start the long click here but rely on the {@link NumberPickerButton} to inform us when the long click has ended.
386 */
387 public boolean onLongClick(View v)
388 {
389
390 /*
391 * The text view may still have focus so clear it's focus which will
392 * trigger the on focus changed and any typed values to be pulled.
393 */
394 mText.clearFocus();
395
396 if (R.id.increment == v.getId())
397 {
398 mIncrement = true;
399 mHandler.post(mRunnable);
400 }
401 else if (R.id.decrement == v.getId())
402 {
403 mDecrement = true;
404 mHandler.post(mRunnable);
405 }
406 return true;
407 }
408
409 public void cancelIncrement()
410 {
411 mIncrement = false;
412 }
413
414 public void cancelDecrement()
415 {
416 mDecrement = false;
417 }
418
419 private static final char[] DIGIT_CHARACTERS = new char[]{'0', '1', '2', '3', '4', '5', '6',
420 '7', '8', '9' };
421
422 private NumberPickerButton mIncrementButton;
423 private NumberPickerButton mDecrementButton;
424
425 private class NumberPickerInputFilter implements InputFilter
426 {
427 public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
428 int dend)
429 {
430 if (mDisplayedValues == null) { return mNumberInputFilter.filter(source, start, end, dest,
431 dstart, dend); }
432 CharSequence filtered = String.valueOf(source.subSequence(start, end));
433 String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
434 + dest.subSequence(dend, dest.length());
435 String str = String.valueOf(result).toLowerCase();
436 for (String val : mDisplayedValues)
437 {
438 val = val.toLowerCase();
439 if (val.startsWith(str)) { return filtered; }
440 }
441 return "";
442 }
443 }
444
445 private class NumberRangeKeyListener extends NumberKeyListener
446 {
447
448 // XXX This doesn't allow for range limits when controlled by a
449 // soft input method!
450 public int getInputType()
451 {
452 return InputType.TYPE_CLASS_NUMBER;
453 }
454
455 @Override
456 protected char[] getAcceptedChars()
457 {
458 return DIGIT_CHARACTERS;
459 }
460
461 @Override
462 public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
463 int dend)
464 {
465
466 CharSequence filtered = super.filter(source, start, end, dest, dstart, dend);
467 if (filtered == null)
468 {
469 filtered = source.subSequence(start, end);
470 }
471
472 String result = String.valueOf(dest.subSequence(0, dstart)) + filtered
473 + dest.subSequence(dend, dest.length());
474
475 if ("".equals(result)) { return result; }
476 int val = getSelectedPos(result);
477
478 /*
479 * Ensure the user can't type in a value greater
480 * than the max allowed. We have to allow less than min
481 * as the user might want to delete some numbers
482 * and then type a new number.
483 */
484 if (val > mEnd)
485 {
486 return "";
487 }
488 else
489 {
490 return filtered;
491 }
492 }
493 }
494
495 private int getSelectedPos(String str)
496 {
497 if (mDisplayedValues == null)
498 {
499 return Integer.parseInt(str);
500 }
501 else
502 {
503 for (int i = 0; i < mDisplayedValues.length; i++)
504 {
505
506 /* Don't force the user to type in jan when ja will do */
507 str = str.toLowerCase();
508 if (mDisplayedValues[i].toLowerCase().startsWith(str)) { return mStart + i; }
509 }
510
511 /*
512 * The user might have typed in a number into the month field i.e.
513 * 10 instead of OCT so support that too.
514 */
515 try
516 {
517 return Integer.parseInt(str);
518 }
519 catch (NumberFormatException e)
520 {
521
522 /* Ignore as if it's not a number we don't care */
523 }
524 }
525 return mStart;
526 }
527
528 /**
529 * @return the current value.
530 */
531 public int getCurrent()
532 {
533 return mCurrent;
534 }
535 }

   
Visit the aagtl Website