am 5a50a8bc: am e615da7e: am f1792c42: Merge "Fix hotspot coordinate propagation in ViewGroup and AbsListView" into lmp-mr1-dev
* commit '5a50a8bcabdb2593b48b06854a7455318399b6d1':
Fix hotspot coordinate propagation in ViewGroup and AbsListView
diff --git a/api/current.txt b/api/current.txt
index b0517689..4796ccd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -33573,6 +33573,7 @@
method public void dispatchDisplayHint(int);
method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas);
+ method public void dispatchDrawableHotspotChanged(float, float);
method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d74ab13..066865c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16059,7 +16059,10 @@
/**
* This function is called whenever the view hotspot changes and needs to
- * be propagated to drawables managed by the view.
+ * be propagated to drawables or child views managed by the view.
+ * <p>
+ * Dispatching to child views is handled by
+ * {@link #dispatchDrawableHotspotChanged(float, float)}.
* <p>
* Be sure to call through to the superclass when overriding this function.
*
@@ -16070,6 +16073,18 @@
if (mBackground != null) {
mBackground.setHotspot(x, y);
}
+
+ dispatchDrawableHotspotChanged(x, y);
+ }
+
+ /**
+ * Dispatches drawableHotspotChanged to all of this View's children.
+ *
+ * @param x hotspot x coordinate
+ * @param y hotspot y coordinate
+ * @see #drawableHotspotChanged(float, float)
+ */
+ public void dispatchDrawableHotspotChanged(float x, float y) {
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 512ea99..c53af25 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -161,6 +161,9 @@
// Used during drag dispatch
private PointF mLocalPoint;
+ // Lazily-created holder for point computations.
+ private float[] mTempPoint;
+
// Layout animation
private LayoutAnimationController mLayoutAnimationController;
private Animation.AnimationListener mAnimationListener;
@@ -2442,6 +2445,13 @@
|| child.getAnimation() != null;
}
+ private float[] getTempPoint() {
+ if (mTempPoint == null) {
+ mTempPoint = new float[2];
+ }
+ return mTempPoint;
+ }
+
/**
* Returns true if a child view contains the specified point when transformed
* into its coordinate space.
@@ -2450,24 +2460,30 @@
*/
protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) {
- float localX = x + mScrollX - child.mLeft;
- float localY = y + mScrollY - child.mTop;
- if (! child.hasIdentityMatrix() && mAttachInfo != null) {
- final float[] localXY = mAttachInfo.mTmpTransformLocation;
- localXY[0] = localX;
- localXY[1] = localY;
- child.getInverseMatrix().mapPoints(localXY);
- localX = localXY[0];
- localY = localXY[1];
- }
- final boolean isInView = child.pointInView(localX, localY);
+ final float[] point = getTempPoint();
+ point[0] = x;
+ point[1] = y;
+ transformPointToViewLocal(point, child);
+ final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
- outLocalPoint.set(localX, localY);
+ outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
/**
+ * @hide
+ */
+ public void transformPointToViewLocal(float[] point, View child) {
+ point[0] += mScrollX - child.mLeft;
+ point[1] += mScrollY - child.mTop;
+
+ if (!child.hasIdentityMatrix()) {
+ child.getInverseMatrix().mapPoints(point);
+ }
+ }
+
+ /**
* Transforms a motion event into the coordinate space of a particular child view,
* filters out irrelevant pointer ids, and overrides its action if necessary.
* If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
@@ -3606,6 +3622,44 @@
}
}
+ /**
+ * Dispatches drawable hotspot changes to child views that meet at least
+ * one of the following criteria:
+ * <ul>
+ * <li>Returns {@code false} from both {@link View#isClickable()} and
+ * {@link View#isLongClickable()}</li>
+ * <li>Requests duplication of parent state via
+ * {@link View#setDuplicateParentStateEnabled(boolean)}</li>
+ * </ul>
+ *
+ * @param x hotspot x coordinate
+ * @param y hotspot y coordinate
+ * @see #drawableHotspotChanged(float, float)
+ */
+ @Override
+ public void dispatchDrawableHotspotChanged(float x, float y) {
+ final int count = mChildrenCount;
+ if (count == 0) {
+ return;
+ }
+
+ final View[] children = mChildren;
+ for (int i = 0; i < count; i++) {
+ final View child = children[i];
+ // Children that are clickable on their own should not
+ // receive hotspots when their parent view does.
+ final boolean nonActionable = !child.isClickable() && !child.isLongClickable();
+ final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0;
+ if (nonActionable || duplicatesState) {
+ final float[] point = getTempPoint();
+ point[0] = x;
+ point[1] = y;
+ transformPointToViewLocal(point, child);
+ child.drawableHotspotChanged(point[0], point[1]);
+ }
+ }
+ }
+
@Override
void dispatchCancelPendingInputEvents() {
super.dispatchCancelPendingInputEvents();
@@ -5961,28 +6015,6 @@
}
@Override
- public void drawableHotspotChanged(float x, float y) {
- super.drawableHotspotChanged(x, y);
-
- if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
- if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
- throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
- + " child has duplicateParentState set to true");
- }
-
- final View[] children = mChildren;
- final int count = mChildrenCount;
-
- for (int i = 0; i < count; i++) {
- final View child = children[i];
- if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
- child.drawableHotspotChanged(x, y);
- }
- }
- }
- }
-
- @Override
protected int[] onCreateDrawableState(int extraSpace) {
if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
return super.onCreateDrawableState(extraSpace);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index ae10a9a..f6dd69fa 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
@@ -611,6 +612,8 @@
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
+ private final float[] mTmpPoint = new float[2];
+
// Used for offsetting MotionEvents that we feed to the VelocityTracker.
// In the future it would be nice to be able to give this to the VelocityTracker
// directly, or alternatively put a VT into absolute-positioning mode that only
@@ -2511,38 +2514,29 @@
* Positions the selector in a way that mimics touch.
*/
void positionSelectorLikeTouch(int position, View sel, float x, float y) {
- positionSelectorLikeFocus(position, sel);
-
- if (mSelector != null && position != INVALID_POSITION) {
- mSelector.setHotspot(x, y);
- }
+ positionSelector(position, sel, true, x, y);
}
/**
* Positions the selector in a way that mimics keyboard focus.
*/
void positionSelectorLikeFocus(int position, View sel) {
- // If we're changing position, update the visibility since the selector
- // is technically being detached from the previous selection.
- final Drawable selector = mSelector;
- final boolean manageState = selector != null && mSelectorPosition != position
- && position != INVALID_POSITION;
- if (manageState) {
- selector.setVisible(false, false);
- }
-
- positionSelector(position, sel);
-
- if (manageState) {
+ if (mSelector != null && mSelectorPosition != position && position != INVALID_POSITION) {
final Rect bounds = mSelectorRect;
final float x = bounds.exactCenterX();
final float y = bounds.exactCenterY();
- selector.setVisible(getVisibility() == VISIBLE, false);
- selector.setHotspot(x, y);
+ positionSelector(position, sel, true, x, y);
+ } else {
+ positionSelector(position, sel);
}
}
void positionSelector(int position, View sel) {
+ positionSelector(position, sel, false, -1, -1);
+ }
+
+ private void positionSelector(int position, View sel, boolean manageHotspot, float x, float y) {
+ final boolean positionChanged = position != mSelectorPosition;
if (position != INVALID_POSITION) {
mSelectorPosition = position;
}
@@ -2562,7 +2556,22 @@
// Update the selector drawable.
final Drawable selector = mSelector;
if (selector != null) {
+ if (positionChanged) {
+ // Wipe out the current selector state so that we can start
+ // over in the new position with a fresh state.
+ selector.setVisible(false, false);
+ selector.setState(StateSet.NOTHING);
+ }
selector.setBounds(selectorRect);
+ if (positionChanged) {
+ if (getVisibility() == VISIBLE) {
+ selector.setVisible(true, false);
+ }
+ selector.setState(getDrawableState());
+ }
+ if (manageHotspot) {
+ selector.setHotspot(x, y);
+ }
}
final boolean isChildViewEnabled = mIsChildViewEnabled;
@@ -3200,6 +3209,12 @@
// get the selector in the right state, but we don't want to press each child.
}
+ @Override
+ public void dispatchDrawableHotspotChanged(float x, float y) {
+ // Don't dispatch hotspot changes to children. We'll manually handle
+ // calling drawableHotspotChanged on the correct child.
+ }
+
/**
* Maps a point to a position in the list.
*
@@ -3258,6 +3273,11 @@
mLayoutMode = LAYOUT_NORMAL;
if (!mDataChanged) {
+ final float[] point = mTmpPoint;
+ point[0] = x;
+ point[1] = y;
+ transformPointToViewLocal(point, child);
+ child.drawableHotspotChanged(point[0], point[1]);
child.setPressed(true);
setPressed(true);
layoutChildren();
@@ -3758,10 +3778,10 @@
}
// Otherwise, check containment within list bounds. If we're
// outside bounds, cancel any active presses.
+ final View motionView = getChildAt(mMotionPosition - mFirstPosition);
final float x = ev.getX(pointerIndex);
if (!pointInView(x, y, mTouchSlop)) {
setPressed(false);
- final View motionView = getChildAt(mMotionPosition - mFirstPosition);
if (motionView != null) {
motionView.setPressed(false);
}
@@ -3769,6 +3789,13 @@
mPendingCheckForTap : mPendingCheckForLongPress);
mTouchMode = TOUCH_MODE_DONE_WAITING;
updateSelectorState();
+ } else if (motionView != null) {
+ // Still within bounds, update the hotspot.
+ final float[] point = mTmpPoint;
+ point[0] = x;
+ point[1] = y;
+ transformPointToViewLocal(point, motionView);
+ motionView.drawableHotspotChanged(point[0], point[1]);
}
break;
case TOUCH_MODE_SCROLL:
@@ -6418,6 +6445,8 @@
// Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
// However, we will NOT place them into scrap views.
activeViews[i] = child;
+ // Remember the position so that setupChild() doesn't reset state.
+ lp.scrappedFromPosition = firstActivePosition + i;
}
}
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 13e3d54..9809606 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -556,11 +556,13 @@
if (mRipple != null) {
mRipple.cancel();
mRipple = null;
+ mRippleActive = false;
}
if (mBackground != null) {
mBackground.cancel();
mBackground = null;
+ mBackgroundActive = false;
}
cancelExitingRipples();