am 0891a897: Merge "Polish the NumberPicker, TimePicker, and DatePicker based on UX request." into jb-dev

* commit '0891a89790777e2f88f413351fafe49dda36714f':
  Polish the NumberPicker, TimePicker, and DatePicker based on UX request.
This commit is contained in:
Svetoslav Ganov
2012-05-10 13:02:22 -07:00
committed by Android Git Automerger
5 changed files with 162 additions and 96 deletions

View File

@@ -132,16 +132,6 @@ public class NumberPicker extends LinearLayout {
*/
private static final int UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE = 48;
/**
* The default unscaled minimal distance for a swipe to be considered a fling.
*/
private static final int UNSCALED_DEFAULT_MIN_FLING_DISTANCE = 150;
/**
* Coefficient for adjusting touch scroll distance.
*/
private static final float TOUCH_SCROLL_DECELERATION_COEFFICIENT = 2.0f;
/**
* The resource id for the default layout.
*/
@@ -232,11 +222,6 @@ public class NumberPicker extends LinearLayout {
*/
private final int mTextSize;
/**
* The minimal distance for a swipe to be considered a fling.
*/
private final int mMinFlingDistance;
/**
* The height of the gap between text elements if the selector wheel.
*/
@@ -297,6 +282,11 @@ public class NumberPicker extends LinearLayout {
*/
private final Paint mSelectorWheelPaint;
/**
* The {@link Drawable} for pressed virtual (increment/decrement) buttons.
*/
private final Drawable mVirtualButtonPressedDrawable;
/**
* The height of a selector element (text + gap).
*/
@@ -434,11 +424,26 @@ public class NumberPicker extends LinearLayout {
*/
private int mLastHoveredChildVirtualViewId;
/**
* Whether the increment virtual button is pressed.
*/
private boolean mIncrementVirtualButtonPressed;
/**
* Whether the decrement virtual button is pressed.
*/
private boolean mDecrementVirtualButtonPressed;
/**
* Provider to report to clients the semantic structure of this widget.
*/
private AccessibilityNodeProviderImpl mAccessibilityNodeProvider;
/**
* Helper class for managing pressed state of the virtual buttons.
*/
private final PressedStateHelper mPressedStateHelper;
/**
* Interface to listen for changes of the current value.
*/
@@ -553,12 +558,6 @@ public class NumberPicker extends LinearLayout {
mSelectionDividersDistance = attributesArray.getDimensionPixelSize(
R.styleable.NumberPicker_selectionDividersDistance, defSelectionDividerDistance);
final int defMinFlingDistance = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_MIN_FLING_DISTANCE,
getResources().getDisplayMetrics());
mMinFlingDistance = attributesArray.getDimensionPixelSize(
R.styleable.NumberPicker_minFlingDistance, defMinFlingDistance);
mMinHeight = attributesArray.getDimensionPixelSize(
R.styleable.NumberPicker_internalMinHeight, SIZE_UNSPECIFIED);
@@ -581,8 +580,13 @@ public class NumberPicker extends LinearLayout {
mComputeMaxWidth = (mMaxWidth == SIZE_UNSPECIFIED);
mVirtualButtonPressedDrawable = attributesArray.getDrawable(
R.styleable.NumberPicker_virtualButtonPressedDrawable);
attributesArray.recycle();
mPressedStateHelper = new PressedStateHelper();
// By default Linearlayout that we extend is not drawn. This is
// its draw() method is not called but dispatchDraw() is called
// directly (see ViewGroup.drawChild()). However, this class uses
@@ -776,7 +780,19 @@ public class NumberPicker extends LinearLayout {
mLastDownEventTime = event.getEventTime();
mIngonreMoveEvents = false;
mShowSoftInputOnTap = false;
// Make sure we wupport flinging inside scrollables.
// Handle pressed state before any state change.
if (mLastDownEventY < mTopSelectionDividerTop) {
if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
mPressedStateHelper.buttonPressDelayed(
PressedStateHelper.BUTTON_DECREMENT);
}
} else if (mLastDownEventY > mBottomSelectionDividerBottom) {
if (mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
mPressedStateHelper.buttonPressDelayed(
PressedStateHelper.BUTTON_INCREMENT);
}
}
// Make sure we support flinging inside scrollables.
getParent().requestDisallowInterceptTouchEvent(true);
if (!mFlingScroller.isFinished()) {
mFlingScroller.forceFinished(true);
@@ -826,8 +842,7 @@ public class NumberPicker extends LinearLayout {
onScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
}
} else {
int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY)
/ TOUCH_SCROLL_DECELERATION_COEFFICIENT);
int deltaMoveY = (int) ((currentMoveY - mLastDownOrMoveEventY));
scrollBy(0, deltaMoveY);
invalidate();
}
@@ -836,23 +851,12 @@ public class NumberPicker extends LinearLayout {
case MotionEvent.ACTION_UP: {
removeBeginSoftInputCommand();
removeChangeCurrentByOneFromLongPress();
mPressedStateHelper.cancel();
VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity();
if (Math.abs(initialVelocity) > mMinimumFlingVelocity) {
int deltaMove = (int) (event.getY() - mLastDownEventY);
int absDeltaMoveY = Math.abs(deltaMove);
if (absDeltaMoveY > mMinFlingDistance) {
fling(initialVelocity);
} else {
final int normalizedDeltaMove =
(int) (absDeltaMoveY / TOUCH_SCROLL_DECELERATION_COEFFICIENT);
if (normalizedDeltaMove < mSelectorElementHeight) {
snapToNextValue(deltaMove < 0);
} else {
snapToClosestValue();
}
}
fling(initialVelocity);
onScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
} else {
int eventY = (int) event.getY();
@@ -867,8 +871,12 @@ public class NumberPicker extends LinearLayout {
- SELECTOR_MIDDLE_ITEM_INDEX;
if (selectorIndexOffset > 0) {
changeValueByOne(true);
mPressedStateHelper.buttonTapped(
PressedStateHelper.BUTTON_INCREMENT);
} else if (selectorIndexOffset < 0) {
changeValueByOne(false);
mPressedStateHelper.buttonTapped(
PressedStateHelper.BUTTON_DECREMENT);
}
}
} else {
@@ -1356,6 +1364,22 @@ public class NumberPicker extends LinearLayout {
float x = (mRight - mLeft) / 2;
float y = mCurrentScrollOffset;
// draw the virtual buttons pressed state if needed
if (mVirtualButtonPressedDrawable != null
&& mScrollState == OnScrollListener.SCROLL_STATE_IDLE) {
if (mDecrementVirtualButtonPressed) {
mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
mVirtualButtonPressedDrawable.setBounds(0, 0, mRight, mTopSelectionDividerTop);
mVirtualButtonPressedDrawable.draw(canvas);
}
if (mIncrementVirtualButtonPressed) {
mVirtualButtonPressedDrawable.setState(PRESSED_STATE_SET);
mVirtualButtonPressedDrawable.setBounds(0, mBottomSelectionDividerBottom, mRight,
mBottom);
mVirtualButtonPressedDrawable.draw(canvas);
}
}
// draw the selector wheel
int[] selectorIndices = mSelectorIndices;
for (int i = 0; i < selectorIndices.length; i++) {
@@ -1465,15 +1489,15 @@ public class NumberPicker extends LinearLayout {
*/
private void initializeSelectorWheelIndices() {
mSelectorIndexToStringCache.clear();
int[] selectorIdices = mSelectorIndices;
int[] selectorIndices = mSelectorIndices;
int current = getValue();
for (int i = 0; i < mSelectorIndices.length; i++) {
int selectorIndex = current + (i - SELECTOR_MIDDLE_ITEM_INDEX);
if (mWrapSelectorWheel) {
selectorIndex = getWrappedSelectorIndex(selectorIndex);
}
mSelectorIndices[i] = selectorIndex;
ensureCachedScrollSelectorValue(mSelectorIndices[i]);
selectorIndices[i] = selectorIndex;
ensureCachedScrollSelectorValue(selectorIndices[i]);
}
}
@@ -1775,6 +1799,7 @@ public class NumberPicker extends LinearLayout {
if (mBeginSoftInputOnLongPressCommand != null) {
removeCallbacks(mBeginSoftInputOnLongPressCommand);
}
mPressedStateHelper.cancel();
}
/**
@@ -1910,39 +1935,80 @@ public class NumberPicker extends LinearLayout {
return false;
}
private void snapToNextValue(boolean increment) {
int deltaY = mCurrentScrollOffset - mInitialScrollOffset;
int amountToScroll = 0;
if (deltaY != 0) {
mPreviousScrollerY = 0;
if (deltaY > 0) {
if (increment) {
amountToScroll = - deltaY;
} else {
amountToScroll = mSelectorElementHeight - deltaY;
}
} else {
if (increment) {
amountToScroll = - mSelectorElementHeight - deltaY;
} else {
amountToScroll = - deltaY;
}
}
mFlingScroller.startScroll(0, 0, 0, amountToScroll, SNAP_SCROLL_DURATION);
invalidate();
}
}
class PressedStateHelper implements Runnable {
public static final int BUTTON_INCREMENT = 1;
public static final int BUTTON_DECREMENT = 2;
private void snapToClosestValue() {
// adjust to the closest value
int deltaY = mInitialScrollOffset - mCurrentScrollOffset;
if (deltaY != 0) {
mPreviousScrollerY = 0;
if (Math.abs(deltaY) > mSelectorElementHeight / 2) {
deltaY += (deltaY > 0) ? -mSelectorElementHeight : mSelectorElementHeight;
private final int MODE_PRESS = 1;
private final int MODE_TAPPED = 2;
private int mManagedButton;
private int mMode;
public void cancel() {
mMode = 0;
mManagedButton = 0;
NumberPicker.this.removeCallbacks(this);
if (mIncrementVirtualButtonPressed) {
mIncrementVirtualButtonPressed = false;
invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
}
mDecrementVirtualButtonPressed = false;
if (mDecrementVirtualButtonPressed) {
invalidate(0, 0, mRight, mTopSelectionDividerTop);
}
}
public void buttonPressDelayed(int button) {
cancel();
mMode = MODE_PRESS;
mManagedButton = button;
NumberPicker.this.postDelayed(this, ViewConfiguration.getTapTimeout());
}
public void buttonTapped(int button) {
cancel();
mMode = MODE_TAPPED;
mManagedButton = button;
NumberPicker.this.post(this);
}
@Override
public void run() {
switch (mMode) {
case MODE_PRESS: {
switch (mManagedButton) {
case BUTTON_INCREMENT: {
mIncrementVirtualButtonPressed = true;
invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
} break;
case BUTTON_DECREMENT: {
mDecrementVirtualButtonPressed = true;
invalidate(0, 0, mRight, mTopSelectionDividerTop);
}
}
} break;
case MODE_TAPPED: {
switch (mManagedButton) {
case BUTTON_INCREMENT: {
if (!mIncrementVirtualButtonPressed) {
NumberPicker.this.postDelayed(this,
ViewConfiguration.getPressedStateDuration());
}
mIncrementVirtualButtonPressed ^= true;
invalidate(0, mBottomSelectionDividerBottom, mRight, mBottom);
} break;
case BUTTON_DECREMENT: {
if (!mDecrementVirtualButtonPressed) {
NumberPicker.this.postDelayed(this,
ViewConfiguration.getPressedStateDuration());
}
mDecrementVirtualButtonPressed ^= true;
invalidate(0, 0, mRight, mTopSelectionDividerTop);
}
}
} break;
}
mFlingScroller.startScroll(0, 0, 0, deltaY, SNAP_SCROLL_DURATION);
invalidate();
}
}

View File

@@ -41,10 +41,10 @@
android:id="@+id/month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:layout_marginLeft="8dip"
android:layout_marginRight="8dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -54,10 +54,10 @@
android:id="@+id/day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:layout_marginLeft="8dip"
android:layout_marginRight="8dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -67,9 +67,9 @@
android:id="@+id/year"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:layout_marginLeft="8dip"
android:layout_marginRight="16dip"
android:focusable="true"
android:focusableInTouchMode="true"

View File

@@ -30,10 +30,10 @@
android:id="@+id/hour"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:layout_marginLeft="16dip"
android:layout_marginRight="14dip"
android:layout_marginRight="6dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -51,10 +51,10 @@
android:id="@+id/minute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_marginLeft="14dip"
android:layout_marginRight="16dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:layout_marginLeft="6dip"
android:layout_marginRight="8dip"
android:focusable="true"
android:focusableInTouchMode="true"
/>
@@ -64,9 +64,9 @@
android:id="@+id/amPm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_marginLeft="16dip"
android:layout_marginTop="16dip"
android:layout_marginBottom="16dip"
android:layout_marginLeft="8dip"
android:layout_marginRight="16dip"
android:focusable="true"
android:focusableInTouchMode="true"

View File

@@ -3685,8 +3685,8 @@
<attr name="internalMaxWidth" format="dimension" />
<!-- @hide The layout of the number picker. -->
<attr name="internalLayout" />
<!-- @hide The minimal move distance of a swipe to be considered a fling. -->
<attr name="minFlingDistance" format="dimension" />
<!-- @hide The drawable for pressed virtual (increment/decrement) buttons. -->
<attr name="virtualButtonPressedDrawable" format="reference"/>
</declare-styleable>
<declare-styleable name="TimePicker">

View File

@@ -1649,9 +1649,9 @@ please see styles_device_defaults.xml.
<item name="android:selectionDivider">@android:drawable/numberpicker_selection_divider</item>
<item name="android:selectionDividerHeight">2dip</item>
<item name="android:selectionDividersDistance">48dip</item>
<item name="android:internalMinWidth">48dip</item>
<item name="android:internalMinWidth">64dip</item>
<item name="android:internalMaxHeight">180dip</item>
<item name="android:minFlingDistance">150dip</item>
<item name="virtualButtonPressedDrawable">?android:attr/selectableItemBackground</item>
</style>
<style name="Widget.Holo.TimePicker" parent="Widget.TimePicker">