@@ -132,16 +132,6 @@ public class NumberPicker extends LinearLayout {
132132 */
133133 private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48 ;
134134
135- /**
136- * The default unscaled minimal distance for a swipe to be considered a fling.
137- */
138- private static final int UNSCALED_DEFAULT_MIN_FLING_DISTANCE = 150 ;
139-
140- /**
141- * Coefficient for adjusting touch scroll distance.
142- */
143- private static final float TOUCH_SCROLL_DECELERATION_COEFFICIENT = 2.0f ;
144-
145135 /**
146136 * The resource id for the default layout.
147137 */
@@ -232,11 +222,6 @@ public String format(int value) {
232222 */
233223 private final int mTextSize ;
234224
235- /**
236- * The minimal distance for a swipe to be considered a fling.
237- */
238- private final int mMinFlingDistance ;
239-
240225 /**
241226 * The height of the gap between text elements if the selector wheel.
242227 */
@@ -297,6 +282,11 @@ public String format(int value) {
297282 */
298283 private final Paint mSelectorWheelPaint ;
299284
285+ /**
286+ * The {@link Drawable} for pressed virtual (increment/decrement) buttons.
287+ */
288+ private final Drawable mVirtualButtonPressedDrawable ;
289+
300290 /**
301291 * The height of a selector element (text + gap).
302292 */
@@ -434,11 +424,26 @@ public String format(int value) {
434424 */
435425 private int mLastHoveredChildVirtualViewId ;
436426
427+ /**
428+ * Whether the increment virtual button is pressed.
429+ */
430+ private boolean mIncrementVirtualButtonPressed ;
431+
432+ /**
433+ * Whether the decrement virtual button is pressed.
434+ */
435+ private boolean mDecrementVirtualButtonPressed ;
436+
437437 /**
438438 * Provider to report to clients the semantic structure of this widget.
439439 */
440440 private AccessibilityNodeProviderImpl mAccessibilityNodeProvider ;
441441
442+ /**
443+ * Helper class for managing pressed state of the virtual buttons.
444+ */
445+ private final PressedStateHelper mPressedStateHelper ;
446+
442447 /**
443448 * Interface to listen for changes of the current value.
444449 */
@@ -553,12 +558,6 @@ public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
553558 mSelectionDividersDistance = attributesArray .getDimensionPixelSize (
554559 R .styleable .NumberPicker_selectionDividersDistance , defSelectionDividerDistance );
555560
556- final int defMinFlingDistance = (int ) TypedValue .applyDimension (
557- TypedValue .COMPLEX_UNIT_DIP , UNSCALED_DEFAULT_MIN_FLING_DISTANCE ,
558- getResources ().getDisplayMetrics ());
559- mMinFlingDistance = attributesArray .getDimensionPixelSize (
560- R .styleable .NumberPicker_minFlingDistance , defMinFlingDistance );
561-
562561 mMinHeight = attributesArray .getDimensionPixelSize (
563562 R .styleable .NumberPicker_internalMinHeight , SIZE_UNSPECIFIED );
564563
@@ -581,8 +580,13 @@ public NumberPicker(Context context, AttributeSet attrs, int defStyle) {
581580
582581 mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED );
583582
583+ mVirtualButtonPressedDrawable = attributesArray .getDrawable (
584+ R .styleable .NumberPicker_virtualButtonPressedDrawable );
585+
584586 attributesArray .recycle ();
585587
588+ mPressedStateHelper = new PressedStateHelper ();
589+
586590 // By default Linearlayout that we extend is not drawn. This is
587591 // its draw() method is not called but dispatchDraw() is called
588592 // directly (see ViewGroup.drawChild()). However, this class uses
@@ -776,7 +780,19 @@ public boolean onInterceptTouchEvent(MotionEvent event) {
776780 mLastDownEventTime = event .getEventTime ();
777781 mIngonreMoveEvents = false ;
778782 mShowSoftInputOnTap = false ;
779- // Make sure we wupport flinging inside scrollables.
783+ // Handle pressed state before any state change.
784+ if (mLastDownEventY < mTopSelectionDividerTop ) {
785+ if (mScrollState == OnScrollListener .SCROLL_STATE_IDLE ) {
786+ mPressedStateHelper .buttonPressDelayed (
787+ PressedStateHelper .BUTTON_DECREMENT );
788+ }
789+ } else if (mLastDownEventY > mBottomSelectionDividerBottom ) {
790+ if (mScrollState == OnScrollListener .SCROLL_STATE_IDLE ) {
791+ mPressedStateHelper .buttonPressDelayed (
792+ PressedStateHelper .BUTTON_INCREMENT );
793+ }
794+ }
795+ // Make sure we support flinging inside scrollables.
780796 getParent ().requestDisallowInterceptTouchEvent (true );
781797 if (!mFlingScroller .isFinished ()) {
782798 mFlingScroller .forceFinished (true );
@@ -826,8 +842,7 @@ public boolean onTouchEvent(MotionEvent event) {
826842 onScrollStateChange (OnScrollListener .SCROLL_STATE_TOUCH_SCROLL );
827843 }
828844 } else {
829- int deltaMoveY = (int ) ((currentMoveY - mLastDownOrMoveEventY )
830- / TOUCH_SCROLL_DECELERATION_COEFFICIENT );
845+ int deltaMoveY = (int ) ((currentMoveY - mLastDownOrMoveEventY ));
831846 scrollBy (0 , deltaMoveY );
832847 invalidate ();
833848 }
@@ -836,23 +851,12 @@ public boolean onTouchEvent(MotionEvent event) {
836851 case MotionEvent .ACTION_UP : {
837852 removeBeginSoftInputCommand ();
838853 removeChangeCurrentByOneFromLongPress ();
854+ mPressedStateHelper .cancel ();
839855 VelocityTracker velocityTracker = mVelocityTracker ;
840856 velocityTracker .computeCurrentVelocity (1000 , mMaximumFlingVelocity );
841857 int initialVelocity = (int ) velocityTracker .getYVelocity ();
842858 if (Math .abs (initialVelocity ) > mMinimumFlingVelocity ) {
843- int deltaMove = (int ) (event .getY () - mLastDownEventY );
844- int absDeltaMoveY = Math .abs (deltaMove );
845- if (absDeltaMoveY > mMinFlingDistance ) {
846- fling (initialVelocity );
847- } else {
848- final int normalizedDeltaMove =
849- (int ) (absDeltaMoveY / TOUCH_SCROLL_DECELERATION_COEFFICIENT );
850- if (normalizedDeltaMove < mSelectorElementHeight ) {
851- snapToNextValue (deltaMove < 0 );
852- } else {
853- snapToClosestValue ();
854- }
855- }
859+ fling (initialVelocity );
856860 onScrollStateChange (OnScrollListener .SCROLL_STATE_FLING );
857861 } else {
858862 int eventY = (int ) event .getY ();
@@ -867,8 +871,12 @@ public boolean onTouchEvent(MotionEvent event) {
867871 - SELECTOR_MIDDLE_ITEM_INDEX ;
868872 if (selectorIndexOffset > 0 ) {
869873 changeValueByOne (true );
874+ mPressedStateHelper .buttonTapped (
875+ PressedStateHelper .BUTTON_INCREMENT );
870876 } else if (selectorIndexOffset < 0 ) {
871877 changeValueByOne (false );
878+ mPressedStateHelper .buttonTapped (
879+ PressedStateHelper .BUTTON_DECREMENT );
872880 }
873881 }
874882 } else {
@@ -1356,6 +1364,22 @@ protected void onDraw(Canvas canvas) {
13561364 float x = (mRight - mLeft ) / 2 ;
13571365 float y = mCurrentScrollOffset ;
13581366
1367+ // draw the virtual buttons pressed state if needed
1368+ if (mVirtualButtonPressedDrawable != null
1369+ && mScrollState == OnScrollListener .SCROLL_STATE_IDLE ) {
1370+ if (mDecrementVirtualButtonPressed ) {
1371+ mVirtualButtonPressedDrawable .setState (PRESSED_STATE_SET );
1372+ mVirtualButtonPressedDrawable .setBounds (0 , 0 , mRight , mTopSelectionDividerTop );
1373+ mVirtualButtonPressedDrawable .draw (canvas );
1374+ }
1375+ if (mIncrementVirtualButtonPressed ) {
1376+ mVirtualButtonPressedDrawable .setState (PRESSED_STATE_SET );
1377+ mVirtualButtonPressedDrawable .setBounds (0 , mBottomSelectionDividerBottom , mRight ,
1378+ mBottom );
1379+ mVirtualButtonPressedDrawable .draw (canvas );
1380+ }
1381+ }
1382+
13591383 // draw the selector wheel
13601384 int [] selectorIndices = mSelectorIndices ;
13611385 for (int i = 0 ; i < selectorIndices .length ; i ++) {
@@ -1465,15 +1489,15 @@ private int resolveSizeAndStateRespectingMinSize(
14651489 */
14661490 private void initializeSelectorWheelIndices () {
14671491 mSelectorIndexToStringCache .clear ();
1468- int [] selectorIdices = mSelectorIndices ;
1492+ int [] selectorIndices = mSelectorIndices ;
14691493 int current = getValue ();
14701494 for (int i = 0 ; i < mSelectorIndices .length ; i ++) {
14711495 int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX );
14721496 if (mWrapSelectorWheel ) {
14731497 selectorIndex = getWrappedSelectorIndex (selectorIndex );
14741498 }
1475- mSelectorIndices [i ] = selectorIndex ;
1476- ensureCachedScrollSelectorValue (mSelectorIndices [i ]);
1499+ selectorIndices [i ] = selectorIndex ;
1500+ ensureCachedScrollSelectorValue (selectorIndices [i ]);
14771501 }
14781502 }
14791503
@@ -1775,6 +1799,7 @@ private void removeAllCallbacks() {
17751799 if (mBeginSoftInputOnLongPressCommand != null ) {
17761800 removeCallbacks (mBeginSoftInputOnLongPressCommand );
17771801 }
1802+ mPressedStateHelper .cancel ();
17781803 }
17791804
17801805 /**
@@ -1910,39 +1935,80 @@ private boolean ensureScrollWheelAdjusted() {
19101935 return false ;
19111936 }
19121937
1913- private void snapToNextValue (boolean increment ) {
1914- int deltaY = mCurrentScrollOffset - mInitialScrollOffset ;
1915- int amountToScroll = 0 ;
1916- if (deltaY != 0 ) {
1917- mPreviousScrollerY = 0 ;
1918- if (deltaY > 0 ) {
1919- if (increment ) {
1920- amountToScroll = - deltaY ;
1921- } else {
1922- amountToScroll = mSelectorElementHeight - deltaY ;
1923- }
1924- } else {
1925- if (increment ) {
1926- amountToScroll = - mSelectorElementHeight - deltaY ;
1927- } else {
1928- amountToScroll = - deltaY ;
1929- }
1938+ class PressedStateHelper implements Runnable {
1939+ public static final int BUTTON_INCREMENT = 1 ;
1940+ public static final int BUTTON_DECREMENT = 2 ;
1941+
1942+ private final int MODE_PRESS = 1 ;
1943+ private final int MODE_TAPPED = 2 ;
1944+
1945+ private int mManagedButton ;
1946+ private int mMode ;
1947+
1948+ public void cancel () {
1949+ mMode = 0 ;
1950+ mManagedButton = 0 ;
1951+ NumberPicker .this .removeCallbacks (this );
1952+ if (mIncrementVirtualButtonPressed ) {
1953+ mIncrementVirtualButtonPressed = false ;
1954+ invalidate (0 , mBottomSelectionDividerBottom , mRight , mBottom );
1955+ }
1956+ mDecrementVirtualButtonPressed = false ;
1957+ if (mDecrementVirtualButtonPressed ) {
1958+ invalidate (0 , 0 , mRight , mTopSelectionDividerTop );
19301959 }
1931- mFlingScroller .startScroll (0 , 0 , 0 , amountToScroll , SNAP_SCROLL_DURATION );
1932- invalidate ();
19331960 }
1934- }
19351961
1936- private void snapToClosestValue () {
1937- // adjust to the closest value
1938- int deltaY = mInitialScrollOffset - mCurrentScrollOffset ;
1939- if (deltaY != 0 ) {
1940- mPreviousScrollerY = 0 ;
1941- if (Math .abs (deltaY ) > mSelectorElementHeight / 2 ) {
1942- deltaY += (deltaY > 0 ) ? -mSelectorElementHeight : mSelectorElementHeight ;
1962+ public void buttonPressDelayed (int button ) {
1963+ cancel ();
1964+ mMode = MODE_PRESS ;
1965+ mManagedButton = button ;
1966+ NumberPicker .this .postDelayed (this , ViewConfiguration .getTapTimeout ());
1967+ }
1968+
1969+ public void buttonTapped (int button ) {
1970+ cancel ();
1971+ mMode = MODE_TAPPED ;
1972+ mManagedButton = button ;
1973+ NumberPicker .this .post (this );
1974+ }
1975+
1976+ @ Override
1977+ public void run () {
1978+ switch (mMode ) {
1979+ case MODE_PRESS : {
1980+ switch (mManagedButton ) {
1981+ case BUTTON_INCREMENT : {
1982+ mIncrementVirtualButtonPressed = true ;
1983+ invalidate (0 , mBottomSelectionDividerBottom , mRight , mBottom );
1984+ } break ;
1985+ case BUTTON_DECREMENT : {
1986+ mDecrementVirtualButtonPressed = true ;
1987+ invalidate (0 , 0 , mRight , mTopSelectionDividerTop );
1988+ }
1989+ }
1990+ } break ;
1991+ case MODE_TAPPED : {
1992+ switch (mManagedButton ) {
1993+ case BUTTON_INCREMENT : {
1994+ if (!mIncrementVirtualButtonPressed ) {
1995+ NumberPicker .this .postDelayed (this ,
1996+ ViewConfiguration .getPressedStateDuration ());
1997+ }
1998+ mIncrementVirtualButtonPressed ^= true ;
1999+ invalidate (0 , mBottomSelectionDividerBottom , mRight , mBottom );
2000+ } break ;
2001+ case BUTTON_DECREMENT : {
2002+ if (!mDecrementVirtualButtonPressed ) {
2003+ NumberPicker .this .postDelayed (this ,
2004+ ViewConfiguration .getPressedStateDuration ());
2005+ }
2006+ mDecrementVirtualButtonPressed ^= true ;
2007+ invalidate (0 , 0 , mRight , mTopSelectionDividerTop );
2008+ }
2009+ }
2010+ } break ;
19432011 }
1944- mFlingScroller .startScroll (0 , 0 , 0 , deltaY , SNAP_SCROLL_DURATION );
1945- invalidate ();
19462012 }
19472013 }
19482014
0 commit comments