Improved handle touch interaction.
- the handle size is now entirely independent of asset size - the handle doesn't have to have a handle graphic - the handle top/bottom sizing can each change based on whether the challenge is visible - now, the handle becomes very small when the challenge is visible, and does not intrude into the challenge rect itself (so you really need to drag downward through the top edge of the challenge to activate the handle and close the challenge) - when the challenge is closed, the handle may be clicked to open Bug: 7428215 Change-Id: I4ca7b4d5d475337295a6a7492a831abbba288ac5 Proto-Id: Ibc8997ece27ca1e0fa4deb651bb11860f95d7b23
This commit is contained in:
@@ -32,7 +32,6 @@
|
||||
android:id="@+id/sliding_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
androidprv:dragHandle="@drawable/kg_security_grip"
|
||||
androidprv:dragIcon="@drawable/kg_security_lock">
|
||||
|
||||
<FrameLayout
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.FloatProperty;
|
||||
@@ -44,6 +45,18 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
private static final String TAG = "SlidingChallengeLayout";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
// The drag handle is measured in dp above & below the top edge of the
|
||||
// challenge view; these parameters change based on whether the challenge
|
||||
// is open or closed.
|
||||
private static final int DRAG_HANDLE_CLOSED_ABOVE = 32; // dp
|
||||
private static final int DRAG_HANDLE_CLOSED_BELOW = 32; // dp
|
||||
private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp
|
||||
private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp
|
||||
|
||||
private static final boolean OPEN_ON_CLICK = true;
|
||||
|
||||
private static final int HANDLE_ANIMATE_DURATION = 200; // ms
|
||||
|
||||
// Drawn to show the drag handle in closed state; crossfades to the challenge view
|
||||
// when challenge is fully visible
|
||||
private Drawable mHandleDrawable;
|
||||
@@ -82,13 +95,20 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private int mMinVelocity;
|
||||
private int mMaxVelocity;
|
||||
private float mGestureStartY; // where did you touch the screen to start this gesture?
|
||||
private float mGestureStartX, mGestureStartY; // where did you first touch the screen?
|
||||
private int mGestureStartChallengeBottom; // where was the challenge at that time?
|
||||
private int mDragHandleSize; // handle hitrect extension into the challenge view
|
||||
private int mDragHandleHeadroom; // extend the handle's hitrect this far above the line
|
||||
|
||||
private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view
|
||||
private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line
|
||||
private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view
|
||||
private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line
|
||||
|
||||
private int mDragHandleEdgeSlop;
|
||||
private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
|
||||
// that should remain on-screen
|
||||
|
||||
private int mTouchSlop;
|
||||
|
||||
float mHandleAlpha;
|
||||
float mFrameAlpha;
|
||||
private ObjectAnimator mHandleAnimation;
|
||||
@@ -125,9 +145,6 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
}
|
||||
};
|
||||
|
||||
private static final int DRAG_HANDLE_DEFAULT_SIZE = 32; // dp
|
||||
private static final int HANDLE_ANIMATE_DURATION = 200; // ms
|
||||
|
||||
// True if at least one layout pass has happened since the view was attached.
|
||||
private boolean mHasLayout;
|
||||
|
||||
@@ -224,20 +241,25 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
mDragHandleEdgeSlop = getResources().getDimensionPixelSize(
|
||||
R.dimen.kg_edge_swipe_region_size);
|
||||
|
||||
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public void setDragDrawables(Drawable handle, Drawable icon) {
|
||||
final float density = getResources().getDisplayMetrics().density;
|
||||
final int defaultSize = (int) (DRAG_HANDLE_DEFAULT_SIZE * density + 0.5f);
|
||||
final int handleHeight = handle != null ? handle.getIntrinsicHeight() : 0;
|
||||
final int iconHeight = icon != null ? icon.getIntrinsicHeight() : 0;
|
||||
mDragHandleSize = Math.max(handleHeight > 0 ? handleHeight : defaultSize,
|
||||
iconHeight > 0 ? iconHeight : defaultSize);
|
||||
|
||||
// top half of the lock icon, plus another 25% to be sure
|
||||
mDragHandleHeadroom = (int) (iconHeight * 0.75f);
|
||||
mChallengeBottomBound = (mDragHandleSize + mDragHandleHeadroom + handleHeight) / 2;
|
||||
mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f);
|
||||
mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f);
|
||||
mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f);
|
||||
mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f);
|
||||
|
||||
// how much space to account for in the handle when closed
|
||||
mChallengeBottomBound =
|
||||
(mDragHandleClosedBelow + mDragHandleClosedAbove + handleHeight) / 2;
|
||||
|
||||
mHandleDrawable = handle;
|
||||
mDragIconDrawable = icon;
|
||||
@@ -477,9 +499,12 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
}
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
//Log.v(TAG, "onIntercept: " + ev);
|
||||
|
||||
final int action = ev.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mGestureStartX = ev.getX();
|
||||
mGestureStartY = ev.getY();
|
||||
mBlockDrag = false;
|
||||
break;
|
||||
@@ -500,7 +525,8 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
(isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
|
||||
mActivePointerId == INVALID_POINTER) {
|
||||
mActivePointerId = ev.getPointerId(i);
|
||||
mGestureStartY = ev.getY();
|
||||
mGestureStartX = x;
|
||||
mGestureStartY = y;
|
||||
mGestureStartChallengeBottom = getChallengeBottom();
|
||||
mDragging = true;
|
||||
} else if (isInChallengeView(x, y)) {
|
||||
@@ -532,10 +558,13 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
}
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
//Log.v(TAG, "onTouch: " + ev);
|
||||
|
||||
final int action = ev.getActionMasked();
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mBlockDrag = false;
|
||||
mGestureStartX = ev.getX();
|
||||
mGestureStartY = ev.getY();
|
||||
break;
|
||||
|
||||
@@ -551,7 +580,12 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mDragging) {
|
||||
if (OPEN_ON_CLICK
|
||||
&& isInDragHandle(mGestureStartX, mGestureStartY)
|
||||
&& Math.abs(ev.getX() - mGestureStartX) <= mTouchSlop
|
||||
&& Math.abs(ev.getY() - mGestureStartY) <= mTouchSlop) {
|
||||
showChallenge(true);
|
||||
} else if (mDragging) {
|
||||
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
|
||||
showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
|
||||
}
|
||||
@@ -568,6 +602,7 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
|
||||
(isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
|
||||
&& mActivePointerId == INVALID_POINTER) {
|
||||
mGestureStartX = x;
|
||||
mGestureStartY = y;
|
||||
mActivePointerId = ev.getPointerId(i);
|
||||
mGestureStartChallengeBottom = getChallengeBottom();
|
||||
@@ -605,8 +640,11 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
* We only want to add additional vertical space to the drag handle when the panel is fully
|
||||
* closed.
|
||||
*/
|
||||
private int getDragHandleHeadroom() {
|
||||
return isChallengeShowing() ? 0 : mDragHandleHeadroom;
|
||||
private int getDragHandleSizeAbove() {
|
||||
return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove;
|
||||
}
|
||||
private int getDragHandleSizeBelow() {
|
||||
return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow;
|
||||
}
|
||||
|
||||
private boolean isInChallengeView(float x, float y) {
|
||||
@@ -620,17 +658,17 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
if (mChallengeView == null) return false;
|
||||
|
||||
return x >= mDragHandleEdgeSlop &&
|
||||
y >= mChallengeView.getTop() - getDragHandleHeadroom() &&
|
||||
y >= mChallengeView.getTop() - getDragHandleSizeAbove() &&
|
||||
x < getWidth() - mDragHandleEdgeSlop &&
|
||||
y < mChallengeView.getTop() + mDragHandleSize;
|
||||
y < mChallengeView.getTop() + getDragHandleSizeBelow();
|
||||
}
|
||||
|
||||
private boolean crossedDragHandle(float x, float y, float initialY) {
|
||||
final int challengeTop = mChallengeView.getTop();
|
||||
return x >= 0 &&
|
||||
x < getWidth() &&
|
||||
initialY < (challengeTop - getDragHandleHeadroom()) &&
|
||||
y > challengeTop + mDragHandleSize;
|
||||
initialY < (challengeTop - getDragHandleSizeAbove()) &&
|
||||
y > challengeTop + getDragHandleSizeBelow();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -760,20 +798,25 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
|
||||
debugPaint.setColor(0x40FF00CC);
|
||||
// show the isInDragHandle() rect
|
||||
c.drawRect(mDragHandleEdgeSlop,
|
||||
mChallengeView.getTop() - getDragHandleHeadroom(),
|
||||
mChallengeView.getTop() - getDragHandleSizeAbove(),
|
||||
getWidth() - mDragHandleEdgeSlop,
|
||||
mChallengeView.getTop() + mDragHandleSize,
|
||||
mChallengeView.getTop() + getDragHandleSizeBelow(),
|
||||
debugPaint);
|
||||
}
|
||||
|
||||
if (mChallengeView != null && mHandleAlpha > 0 && mHandleDrawable != null) {
|
||||
if (mChallengeView != null && mHandleAlpha > 0) {
|
||||
final int top = mChallengeView.getTop();
|
||||
final int handleHeight = mHandleDrawable.getIntrinsicHeight();
|
||||
final int handleHeight;
|
||||
final int challengeLeft = mChallengeView.getLeft();
|
||||
final int challengeRight = mChallengeView.getRight();
|
||||
mHandleDrawable.setBounds(challengeLeft, top, challengeRight, top + handleHeight);
|
||||
mHandleDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
|
||||
mHandleDrawable.draw(c);
|
||||
if (mHandleDrawable != null) {
|
||||
handleHeight = mHandleDrawable.getIntrinsicHeight();
|
||||
mHandleDrawable.setBounds(challengeLeft, top, challengeRight, top + handleHeight);
|
||||
mHandleDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
|
||||
mHandleDrawable.draw(c);
|
||||
} else {
|
||||
handleHeight = 0;
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
// now show the actual drag handle
|
||||
|
||||
Reference in New Issue
Block a user