Draw a default focus highlight if needed.

am: 8062949ece

Change-Id: Ia06126f29168df05fe851746d86e15846ceca1b8
This commit is contained in:
Jiaquan He
2017-04-12 22:54:17 +00:00
committed by android-build-merger

View File

@@ -3920,6 +3920,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private int mBackgroundResource;
private boolean mBackgroundSizeChanged;
/** The default focus highlight.
* @see #mDefaultFocusHighlightEnabled
* @see Drawable#hasFocusStateSpecified()
*/
private Drawable mDefaultFocusHighlight;
private Drawable mDefaultFocusHighlightCache;
private boolean mDefaultFocusHighlightSizeChanged;
private String mTransitionName;
static class TintInfo {
@@ -6811,6 +6819,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
// Here we check whether we still need the default focus highlight, and switch it on/off.
switchDefaultFocusHighlight();
InputMethodManager imm = InputMethodManager.peekInstance();
if (!gainFocus) {
if (isPressed()) {
@@ -11790,6 +11801,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (dr != null && isVisible != dr.isVisible()) {
dr.setVisible(isVisible, false);
}
final Drawable hl = mDefaultFocusHighlight;
if (hl != null && isVisible != hl.isVisible()) {
hl.setVisible(isVisible, false);
}
final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
if (fg != null && isVisible != fg.isVisible()) {
fg.setVisible(isVisible, false);
@@ -13003,6 +13018,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if ((changed & DRAW_MASK) != 0) {
if ((mViewFlags & WILL_NOT_DRAW) != 0) {
if (mBackground != null
|| mDefaultFocusHighlight != null
|| (mForegroundInfo != null && mForegroundInfo.mDrawable != null)) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
} else {
@@ -13074,6 +13090,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}
@@ -13965,6 +13982,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}
@@ -14033,6 +14051,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}
@@ -14095,6 +14114,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}
@@ -14154,6 +14174,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(true);
}
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}
@@ -18758,6 +18779,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
@@ -19316,6 +19340,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags |= drawn;
mBackgroundSizeChanged = true;
mDefaultFocusHighlightSizeChanged = true;
if (mForegroundInfo != null) {
mForegroundInfo.mBoundsChanged = true;
}
@@ -19467,6 +19492,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
mForegroundInfo.mDrawable.setLayoutDirection(layoutDirection);
}
if (mDefaultFocusHighlight != null) {
mDefaultFocusHighlight.setLayoutDirection(layoutDirection);
}
mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
onResolveDrawables(layoutDirection);
}
@@ -19525,7 +19553,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Avoid verifying the scroll bar drawable so that we don't end up in
// an invalidation loop. This effectively prevents the scroll bar
// drawable from triggering invalidations and scheduling runnables.
return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
return who == mBackground || (mForegroundInfo != null && mForegroundInfo.mDrawable == who)
|| (mDefaultFocusHighlight == who);
}
/**
@@ -19549,6 +19578,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
changed |= bg.setState(state);
}
final Drawable hl = mDefaultFocusHighlight;
if (hl != null && hl.isStateful()) {
changed |= hl.setState(state);
}
final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
if (fg != null && fg.isStateful()) {
changed |= fg.setState(state);
@@ -19588,6 +19622,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackground != null) {
mBackground.setHotspot(x, y);
}
if (mDefaultFocusHighlight != null) {
mDefaultFocusHighlight.setHotspot(x, y);
}
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
mForegroundInfo.mDrawable.setHotspot(x, y);
}
@@ -19623,6 +19660,104 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
/**
* Create a default focus highlight if it doesn't exist.
* @return a default focus highlight.
*/
private Drawable getDefaultFocusHighlightDrawable() {
if (mDefaultFocusHighlightCache == null) {
if (mContext != null) {
final int[] attrs = new int[] { android.R.attr.selectableItemBackground };
final TypedArray ta = mContext.obtainStyledAttributes(attrs);
mDefaultFocusHighlightCache = ta.getDrawable(0);
ta.recycle();
}
}
return mDefaultFocusHighlightCache;
}
/**
* Set the current default focus highlight.
* @param highlight the highlight drawable, or {@code null} if it's no longer needed.
*/
private void setDefaultFocusHighlight(Drawable highlight) {
mDefaultFocusHighlight = highlight;
mDefaultFocusHighlightSizeChanged = true;
if (highlight != null) {
if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
mPrivateFlags &= ~PFLAG_SKIP_DRAW;
}
highlight.setLayoutDirection(getLayoutDirection());
if (highlight.isStateful()) {
highlight.setState(getDrawableState());
}
if (isAttachedToWindow()) {
highlight.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);
}
// Set callback last, since the view may still be initializing.
highlight.setCallback(this);
} else if ((mViewFlags & WILL_NOT_DRAW) != 0 && mBackground == null
&& (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
requestLayout();
invalidate();
}
/**
* Check whether we need to draw a default focus highlight when this view gets focused,
* which requires:
* <ul>
* <li>In the background, {@link android.R.attr#state_focused} is not defined.</li>
* <li>This view is not in touch mode.</li>
* <li>This view doesn't opt out for a default focus highlight, via
* {@link #setDefaultFocusHighlightEnabled(boolean)}.</li>
* </ul>
* @return {@code true} if a default focus highlight is needed.
*/
private boolean isDefaultFocusHighlightNeeded(Drawable background) {
final boolean hasFocusStateSpecified = background == null || !background.isStateful()
|| !background.hasFocusStateSpecified();
return !isInTouchMode() && getDefaultFocusHighlightEnabled() && hasFocusStateSpecified;
}
/**
* When this view is focused, switches on/off the default focused highlight.
* <p>
* This always happens when this view is focused, and only at this moment the default focus
* highlight can be visible.
*/
private void switchDefaultFocusHighlight() {
if (isFocused()) {
final boolean needed = isDefaultFocusHighlightNeeded(mBackground);
final boolean active = mDefaultFocusHighlight != null;
if (needed && !active) {
setDefaultFocusHighlight(getDefaultFocusHighlightDrawable());
} else if (!needed && active) {
// The highlight is no longer needed, so tear it down.
setDefaultFocusHighlight(null);
}
}
}
/**
* Draw the default focus highlight onto the canvas.
* @param canvas the canvas where we're drawing the highlight.
*/
private void drawDefaultFocusHighlight(Canvas canvas) {
if (mDefaultFocusHighlight != null) {
if (mDefaultFocusHighlightSizeChanged) {
mDefaultFocusHighlightSizeChanged = false;
final int l = mScrollX;
final int r = l + mRight - mLeft;
final int t = mScrollY;
final int b = t + mBottom - mTop;
mDefaultFocusHighlight.setBounds(l, t, r, b);
}
mDefaultFocusHighlight.draw(canvas);
}
}
/**
* Return an array of resource IDs of the drawable states representing the
* current state of the view.
@@ -19763,6 +19898,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mStateListAnimator != null) {
mStateListAnimator.jumpToCurrentState();
}
if (mDefaultFocusHighlight != null) {
mDefaultFocusHighlight.jumpToCurrentState();
}
if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
mForegroundInfo.mDrawable.jumpToCurrentState();
}
@@ -19907,6 +20045,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/* Remove the background */
mBackground = null;
if ((mViewFlags & WILL_NOT_DRAW) != 0
&& (mDefaultFocusHighlight == null)
&& (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
@@ -20098,7 +20237,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
// Set callback last, since the view may still be initializing.
foreground.setCallback(this);
} else if ((mViewFlags & WILL_NOT_DRAW) != 0 && mBackground == null) {
} else if ((mViewFlags & WILL_NOT_DRAW) != 0 && mBackground == null
&& (mDefaultFocusHighlight == null)) {
mPrivateFlags |= PFLAG_SKIP_DRAW;
}
requestLayout();
@@ -21913,6 +22053,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Similarly, we remove the foreground drawable's non-transparent parts.
applyDrawableToTransparentRegion(mForegroundInfo.mDrawable, region);
}
if (mDefaultFocusHighlight != null
&& mDefaultFocusHighlight.getOpacity() != PixelFormat.TRANSPARENT) {
// Similarly, we remove the default focus highlight's non-transparent parts.
applyDrawableToTransparentRegion(mDefaultFocusHighlight, region);
}
}
}
return true;