1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import android.graphics.Canvas; 20 import android.graphics.Color; 21 import android.graphics.Paint; 22 import android.graphics.Rect; 23 import android.graphics.RectF; 24 25 /** 26 * Helper class for drawing round scroll bars on round Wear devices. 27 */ 28 class RoundScrollbarRenderer { 29 // The range of the scrollbar position represented as an angle in degrees. 30 private static final int SCROLLBAR_ANGLE_RANGE = 90; 31 private static final int MAX_SCROLLBAR_ANGLE_SWIPE = 16; 32 private static final int MIN_SCROLLBAR_ANGLE_SWIPE = 6; 33 private static final float WIDTH_PERCENTAGE = 0.02f; 34 private static final int DEFAULT_THUMB_COLOR = 0xFFE8EAED; 35 private static final int DEFAULT_TRACK_COLOR = 0x4CFFFFFF; 36 37 private final Paint mThumbPaint = new Paint(); 38 private final Paint mTrackPaint = new Paint(); 39 private final RectF mRect = new RectF(); 40 private final View mParent; 41 private final int mMaskThickness; 42 RoundScrollbarRenderer(View parent)43 public RoundScrollbarRenderer(View parent) { 44 // Paints for the round scrollbar. 45 // Set up the thumb paint 46 mThumbPaint.setAntiAlias(true); 47 mThumbPaint.setStrokeCap(Paint.Cap.ROUND); 48 mThumbPaint.setStyle(Paint.Style.STROKE); 49 50 // Set up the track paint 51 mTrackPaint.setAntiAlias(true); 52 mTrackPaint.setStrokeCap(Paint.Cap.ROUND); 53 mTrackPaint.setStyle(Paint.Style.STROKE); 54 55 mParent = parent; 56 57 // Fetch the resource indicating the thickness of CircularDisplayMask, rounding in the same 58 // way WindowManagerService.showCircularMask does. The scroll bar is inset by this amount so 59 // that it doesn't get clipped. 60 mMaskThickness = parent.getContext().getResources().getDimensionPixelSize( 61 com.android.internal.R.dimen.circular_display_mask_thickness); 62 } 63 drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds)64 public void drawRoundScrollbars(Canvas canvas, float alpha, Rect bounds) { 65 if (alpha == 0) { 66 return; 67 } 68 // Get information about the current scroll state of the parent view. 69 float maxScroll = mParent.computeVerticalScrollRange(); 70 float scrollExtent = mParent.computeVerticalScrollExtent(); 71 if (scrollExtent <= 0 || maxScroll <= scrollExtent) { 72 return; 73 } 74 float currentScroll = Math.max(0, mParent.computeVerticalScrollOffset()); 75 float linearThumbLength = mParent.computeVerticalScrollExtent(); 76 float thumbWidth = mParent.getWidth() * WIDTH_PERCENTAGE; 77 mThumbPaint.setStrokeWidth(thumbWidth); 78 mTrackPaint.setStrokeWidth(thumbWidth); 79 80 setThumbColor(applyAlpha(DEFAULT_THUMB_COLOR, alpha)); 81 setTrackColor(applyAlpha(DEFAULT_TRACK_COLOR, alpha)); 82 83 // Normalize the sweep angle for the scroll bar. 84 float sweepAngle = (linearThumbLength / maxScroll) * SCROLLBAR_ANGLE_RANGE; 85 sweepAngle = clamp(sweepAngle, MIN_SCROLLBAR_ANGLE_SWIPE, MAX_SCROLLBAR_ANGLE_SWIPE); 86 // Normalize the start angle so that it falls on the track. 87 float startAngle = (currentScroll * (SCROLLBAR_ANGLE_RANGE - sweepAngle)) 88 / (maxScroll - linearThumbLength) - SCROLLBAR_ANGLE_RANGE / 2; 89 startAngle = clamp(startAngle, -SCROLLBAR_ANGLE_RANGE / 2, 90 SCROLLBAR_ANGLE_RANGE / 2 - sweepAngle); 91 92 // Draw the track and the thumb. 93 float inset = thumbWidth / 2 + mMaskThickness; 94 mRect.set( 95 bounds.left + inset, 96 bounds.top + inset, 97 bounds.right - inset, 98 bounds.bottom - inset); 99 canvas.drawArc(mRect, -SCROLLBAR_ANGLE_RANGE / 2, SCROLLBAR_ANGLE_RANGE, false, 100 mTrackPaint); 101 canvas.drawArc(mRect, startAngle, sweepAngle, false, mThumbPaint); 102 } 103 clamp(float val, float min, float max)104 private static float clamp(float val, float min, float max) { 105 if (val < min) { 106 return min; 107 } else if (val > max) { 108 return max; 109 } else { 110 return val; 111 } 112 } 113 applyAlpha(int color, float alpha)114 private static int applyAlpha(int color, float alpha) { 115 int alphaByte = (int) (Color.alpha(color) * alpha); 116 return Color.argb(alphaByte, Color.red(color), Color.green(color), Color.blue(color)); 117 } 118 setThumbColor(int thumbColor)119 private void setThumbColor(int thumbColor) { 120 if (mThumbPaint.getColor() != thumbColor) { 121 mThumbPaint.setColor(thumbColor); 122 } 123 } 124 setTrackColor(int trackColor)125 private void setTrackColor(int trackColor) { 126 if (mTrackPaint.getColor() != trackColor) { 127 mTrackPaint.setColor(trackColor); 128 } 129 } 130 } 131