Merge "Fix hotspot coordinate propagation in ViewGroup and AbsListView" into lmp-mr1-dev

This commit is contained in:
Alan Viverette
2014-12-08 18:39:55 +00:00
committed by Android (Google) Code Review
5 changed files with 134 additions and 55 deletions

View File

@@ -33331,6 +33331,7 @@ package android.view {
method public void dispatchDisplayHint(int); method public void dispatchDisplayHint(int);
method public boolean dispatchDragEvent(android.view.DragEvent); method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas); method protected void dispatchDraw(android.graphics.Canvas);
method public void dispatchDrawableHotspotChanged(float, float);
method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent); method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
method public boolean dispatchGenericMotionEvent(android.view.MotionEvent); method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent); method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);

View File

@@ -16059,7 +16059,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/** /**
* This function is called whenever the view hotspot changes and needs to * 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> * <p>
* Be sure to call through to the superclass when overriding this function. * Be sure to call through to the superclass when overriding this function.
* *
@@ -16070,6 +16073,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackground != null) { if (mBackground != null) {
mBackground.setHotspot(x, y); 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) {
} }
/** /**

View File

@@ -161,6 +161,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// Used during drag dispatch // Used during drag dispatch
private PointF mLocalPoint; private PointF mLocalPoint;
// Lazily-created holder for point computations.
private float[] mTempPoint;
// Layout animation // Layout animation
private LayoutAnimationController mLayoutAnimationController; private LayoutAnimationController mLayoutAnimationController;
private Animation.AnimationListener mAnimationListener; private Animation.AnimationListener mAnimationListener;
@@ -2442,6 +2445,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|| child.getAnimation() != null; || 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 * Returns true if a child view contains the specified point when transformed
* into its coordinate space. * into its coordinate space.
@@ -2450,23 +2460,29 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/ */
protected boolean isTransformedTouchPointInView(float x, float y, View child, protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) { PointF outLocalPoint) {
float localX = x + mScrollX - child.mLeft; final float[] point = getTempPoint();
float localY = y + mScrollY - child.mTop; point[0] = x;
if (! child.hasIdentityMatrix() && mAttachInfo != null) { point[1] = y;
final float[] localXY = mAttachInfo.mTmpTransformLocation; transformPointToViewLocal(point, child);
localXY[0] = localX; final boolean isInView = child.pointInView(point[0], point[1]);
localXY[1] = localY;
child.getInverseMatrix().mapPoints(localXY);
localX = localXY[0];
localY = localXY[1];
}
final boolean isInView = child.pointInView(localX, localY);
if (isInView && outLocalPoint != null) { if (isInView && outLocalPoint != null) {
outLocalPoint.set(localX, localY); outLocalPoint.set(point[0], point[1]);
} }
return isInView; 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, * Transforms a motion event into the coordinate space of a particular child view,
* filters out irrelevant pointer ids, and overrides its action if necessary. * filters out irrelevant pointer ids, and overrides its action if necessary.
@@ -3606,6 +3622,44 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
} }
} }
/**
* 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 @Override
void dispatchCancelPendingInputEvents() { void dispatchCancelPendingInputEvents() {
super.dispatchCancelPendingInputEvents(); super.dispatchCancelPendingInputEvents();
@@ -5960,28 +6014,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
} }
} }
@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 @Override
protected int[] onCreateDrawableState(int extraSpace) { protected int[] onCreateDrawableState(int extraSpace) {
if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) { if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {

View File

@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable; import android.graphics.drawable.TransitionDrawable;
@@ -611,6 +612,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private final int[] mScrollOffset = new int[2]; private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = 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. // 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 // 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 // directly, or alternatively put a VT into absolute-positioning mode that only
@@ -2509,38 +2512,29 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* Positions the selector in a way that mimics touch. * Positions the selector in a way that mimics touch.
*/ */
void positionSelectorLikeTouch(int position, View sel, float x, float y) { void positionSelectorLikeTouch(int position, View sel, float x, float y) {
positionSelectorLikeFocus(position, sel); positionSelector(position, sel, true, x, y);
if (mSelector != null && position != INVALID_POSITION) {
mSelector.setHotspot(x, y);
}
} }
/** /**
* Positions the selector in a way that mimics keyboard focus. * Positions the selector in a way that mimics keyboard focus.
*/ */
void positionSelectorLikeFocus(int position, View sel) { void positionSelectorLikeFocus(int position, View sel) {
// If we're changing position, update the visibility since the selector if (mSelector != null && mSelectorPosition != position && position != INVALID_POSITION) {
// 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) {
final Rect bounds = mSelectorRect; final Rect bounds = mSelectorRect;
final float x = bounds.exactCenterX(); final float x = bounds.exactCenterX();
final float y = bounds.exactCenterY(); final float y = bounds.exactCenterY();
selector.setVisible(getVisibility() == VISIBLE, false); positionSelector(position, sel, true, x, y);
selector.setHotspot(x, y); } else {
positionSelector(position, sel);
} }
} }
void positionSelector(int position, View 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) { if (position != INVALID_POSITION) {
mSelectorPosition = position; mSelectorPosition = position;
} }
@@ -2560,7 +2554,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// Update the selector drawable. // Update the selector drawable.
final Drawable selector = mSelector; final Drawable selector = mSelector;
if (selector != null) { 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); 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; final boolean isChildViewEnabled = mIsChildViewEnabled;
@@ -3198,6 +3207,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// get the selector in the right state, but we don't want to press each child. // 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. * Maps a point to a position in the list.
* *
@@ -3256,6 +3271,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mLayoutMode = LAYOUT_NORMAL; mLayoutMode = LAYOUT_NORMAL;
if (!mDataChanged) { if (!mDataChanged) {
final float[] point = mTmpPoint;
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
child.drawableHotspotChanged(point[0], point[1]);
child.setPressed(true); child.setPressed(true);
setPressed(true); setPressed(true);
layoutChildren(); layoutChildren();
@@ -3756,10 +3776,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
} }
// Otherwise, check containment within list bounds. If we're // Otherwise, check containment within list bounds. If we're
// outside bounds, cancel any active presses. // outside bounds, cancel any active presses.
final View motionView = getChildAt(mMotionPosition - mFirstPosition);
final float x = ev.getX(pointerIndex); final float x = ev.getX(pointerIndex);
if (!pointInView(x, y, mTouchSlop)) { if (!pointInView(x, y, mTouchSlop)) {
setPressed(false); setPressed(false);
final View motionView = getChildAt(mMotionPosition - mFirstPosition);
if (motionView != null) { if (motionView != null) {
motionView.setPressed(false); motionView.setPressed(false);
} }
@@ -3767,6 +3787,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mPendingCheckForTap : mPendingCheckForLongPress); mPendingCheckForTap : mPendingCheckForLongPress);
mTouchMode = TOUCH_MODE_DONE_WAITING; mTouchMode = TOUCH_MODE_DONE_WAITING;
updateSelectorState(); 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; break;
case TOUCH_MODE_SCROLL: case TOUCH_MODE_SCROLL:
@@ -6416,6 +6443,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views. // Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
// However, we will NOT place them into scrap views. // However, we will NOT place them into scrap views.
activeViews[i] = child; activeViews[i] = child;
// Remember the position so that setupChild() doesn't reset state.
lp.scrappedFromPosition = firstActivePosition + i;
} }
} }
} }

View File

@@ -556,11 +556,13 @@ public class RippleDrawable extends LayerDrawable {
if (mRipple != null) { if (mRipple != null) {
mRipple.cancel(); mRipple.cancel();
mRipple = null; mRipple = null;
mRippleActive = false;
} }
if (mBackground != null) { if (mBackground != null) {
mBackground.cancel(); mBackground.cancel();
mBackground = null; mBackground = null;
mBackgroundActive = false;
} }
cancelExitingRipples(); cancelExitingRipples();