diff options
| author | 2016-07-19 15:14:39 +0100 | |
|---|---|---|
| committer | 2016-07-25 16:14:55 +0100 | |
| commit | b19d0f9800ea4db15f4812db9ddf12839fd8efcd (patch) | |
| tree | e25cc3b7c4804157ec3a89c16626a05f401128fe | |
| parent | 0eadbe6d369844485d36e45f2b4ca9770828ae52 (diff) | |
Add support for round scroll bars to View.java
Change-Id: If94a29d68cee504f7d31519b96f7b4b8a3af6bfa
| -rw-r--r-- | core/java/android/view/RoundScrollbarRenderer.java | 121 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 62 |
2 files changed, 177 insertions, 6 deletions
diff --git a/core/java/android/view/RoundScrollbarRenderer.java b/core/java/android/view/RoundScrollbarRenderer.java new file mode 100644 index 000000000000..f258458a9b15 --- /dev/null +++ b/core/java/android/view/RoundScrollbarRenderer.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; + +/** + * Helper class for drawing round scroll bars on round Wear devices. + */ +class RoundScrollbarRenderer { + // The range of the scrollbar position represented as an angle in degrees. + private static final int SCROLLBAR_ANGLE_RANGE = 90; + private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16; + private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6; + private static final float WIDTH_PERCENTAGE = 0.02f; + private static final int DEFAULT_THUMB_COLOR = 0xFF757575; + private static final int DEFAULT_TRACK_COLOR = 0x21FFFFFF; + + private final Paint mThumbPaint = new Paint(); + private final Paint mTrackPaint = new Paint(); + private final RectF mRect = new RectF(); + private final View mParent; + + public RoundScrollbarRenderer(View parent) { + // Paints for the round scrollbar. + // Set up the thumb paint + mThumbPaint.setAntiAlias(true); + mThumbPaint.setStrokeCap(Paint.Cap.ROUND); + mThumbPaint.setStyle(Paint.Style.STROKE); + + // Set up the track paint + mTrackPaint.setAntiAlias(true); + mTrackPaint.setStrokeCap(Paint.Cap.ROUND); + mTrackPaint.setStyle(Paint.Style.STROKE); + + mParent = parent; + } + + public void drawRoundScrollbars(Canvas canvas, float alpha) { + if (alpha == 0) { + return; + } + // Get information about the current scroll state of the parent view. + float maxScroll = mParent.computeVerticalScrollRange(); + float scrollExtent = mParent.computeVerticalScrollExtent(); + if (scrollExtent <= 0 || maxScroll <= scrollExtent) { + return; + } + float currentScroll = Math.max(0, mParent.computeVerticalScrollOffset()); + float linearThumbLength = mParent.computeVerticalScrollExtent(); + float thumbWidth = mParent.getWidth() * WIDTH_PERCENTAGE; + mThumbPaint.setStrokeWidth(thumbWidth); + mTrackPaint.setStrokeWidth(thumbWidth); + + setThumbColor(applyAlpha(DEFAULT_THUMB_COLOR, alpha)); + setTrackColor(applyAlpha(DEFAULT_TRACK_COLOR, alpha)); + + // Normalize the sweep angle for the scroll bar. + float sweepAngle = (linearThumbLength / maxScroll) * SCROLLBAR_ANGLE_RANGE; + sweepAngle = clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE); + // Normalize the start angle so that it falls on the track. + float startAngle = (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle)) + / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2; + startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2, + SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle); + + // Draw the track and the scroll bar. + mRect.set( + 0 + thumbWidth / 2, + 0 + thumbWidth / 2, + mParent.getWidth() - thumbWidth / 2, + mParent.getHeight() - thumbWidth / 2); + canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false, + mTrackPaint); + canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint); + } + + private static float clamp(float val, float min, float max) { + if (val < min) { + return min; + } else if (val > max) { + return max; + } else { + return val; + } + } + + private static int applyAlpha(int color, float alpha) { + int alphaByte = (int) (Color.alpha(color) * alpha); + return Color.argb(alphaByte, Color.red(color), Color.green(color), Color.blue(color)); + } + + private void setThumbColor(int thumbColor) { + if (mThumbPaint.getColor() != thumbColor) { + mThumbPaint.setColor(thumbColor); + } + } + + private void setTrackColor(int trackColor) { + if (mTrackPaint.getColor() != trackColor) { + mTrackPaint.setColor(trackColor); + } + } +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index e6481147c295..3d47e1cf2520 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,6 +40,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Insets; import android.graphics.Interpolator; import android.graphics.LinearGradient; @@ -3980,6 +3981,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ String mStartActivityRequestWho; + @Nullable + private RoundScrollbarRenderer mRoundScrollbarRenderer; + /** * Simple constructor to use when creating a view from code. * @@ -14748,6 +14752,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, protected final void onDrawScrollBars(Canvas canvas) { // scrollbars are drawn only when the animation is running final ScrollabilityCache cache = mScrollCache; + if (cache != null) { int state = cache.state; @@ -14788,13 +14793,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final boolean drawVerticalScrollBar = isVerticalScrollBarEnabled() && !isVerticalScrollBarHidden(); - if (drawVerticalScrollBar || drawHorizontalScrollBar) { + // Fork out the scroll bar drawing for round wearable devices. + if (mRoundScrollbarRenderer != null) { + if (drawVerticalScrollBar) { + mRoundScrollbarRenderer.drawRoundScrollbars( + canvas, (float) cache.scrollBar.getAlpha() / 255f); + if (invalidate) { + invalidate(); + } + } + // Do not draw horizontal scroll bars for round wearable devices. + } else if (drawVerticalScrollBar || drawHorizontalScrollBar) { final ScrollBarDrawable scrollBar = cache.scrollBar; if (drawHorizontalScrollBar) { scrollBar.setParameters(computeHorizontalScrollRange(), - computeHorizontalScrollOffset(), - computeHorizontalScrollExtent(), false); + computeHorizontalScrollOffset(), + computeHorizontalScrollExtent(), false); final Rect bounds = cache.mScrollBarBounds; getHorizontalScrollBarBounds(bounds); onDrawHorizontalScrollBar(canvas, scrollBar, bounds.left, bounds.top, @@ -14806,8 +14821,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (drawVerticalScrollBar) { scrollBar.setParameters(computeVerticalScrollRange(), - computeVerticalScrollOffset(), - computeVerticalScrollExtent(), true); + computeVerticalScrollOffset(), + computeVerticalScrollExtent(), true); final Rect bounds = cache.mScrollBarBounds; getVerticalScrollBarBounds(bounds); onDrawVerticalScrollBar(canvas, scrollBar, bounds.left, bounds.top, @@ -17518,6 +17533,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) { onLayout(changed, l, t, r, b); + + if (shouldDrawRoundScrollbar()) { + if(mRoundScrollbarRenderer == null) { + mRoundScrollbarRenderer = new RoundScrollbarRenderer(this); + } + } else { + mRoundScrollbarRenderer = null; + } + mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED; ListenerInfo li = mListenerInfo; @@ -22898,7 +22922,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int[] mInvalidateChildLocation = new int[2]; /** - * Global to the view hierarchy used as a temporary for dealng with + * Global to the view hierarchy used as a temporary for dealing with * computing absolute on-screen location. */ final int[] mTmpLocation = new int[2]; @@ -23736,4 +23760,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, stream.addProperty("accessibility:labelFor", getLabelFor()); stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility()); } + + /** + * Determine if this view is rendered on a round wearable device and is the main view + * on the screen. + */ + private boolean shouldDrawRoundScrollbar() { + if (!mResources.getConfiguration().isScreenRound()) { + return false; + } + + final View rootView = getRootView(); + final WindowInsets insets = getRootWindowInsets(); + + int height = getHeight(); + int width = getWidth(); + int displayHeight = rootView.getHeight(); + int displayWidth = rootView.getWidth(); + + if (height != displayHeight || width != displayWidth) { + return false; + } + + getLocationOnScreen(mAttachInfo.mTmpLocation); + return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft() + && mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop(); + } } |