1 /* 2 * Copyright (C) 2015 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 package com.android.launcher3; 17 18 import android.animation.Animator; 19 import android.animation.ObjectAnimator; 20 import android.content.res.Resources; 21 import android.graphics.Canvas; 22 import android.graphics.Color; 23 import android.graphics.Paint; 24 import android.graphics.Rect; 25 import android.graphics.drawable.Drawable; 26 27 /** 28 * The fast scroller popup that shows the section name the list will jump to. 29 */ 30 public class BaseRecyclerViewFastScrollPopup { 31 32 private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f; 33 34 private Resources mRes; 35 private BaseRecyclerView mRv; 36 37 private Drawable mBg; 38 // The absolute bounds of the fast scroller bg 39 private Rect mBgBounds = new Rect(); 40 private int mBgOriginalSize; 41 private Rect mInvalidateRect = new Rect(); 42 private Rect mTmpRect = new Rect(); 43 44 private String mSectionName; 45 private Paint mTextPaint; 46 private Rect mTextBounds = new Rect(); 47 private float mAlpha; 48 49 private Animator mAlphaAnimator; 50 private boolean mVisible; 51 BaseRecyclerViewFastScrollPopup(BaseRecyclerView rv, Resources res)52 public BaseRecyclerViewFastScrollPopup(BaseRecyclerView rv, Resources res) { 53 mRes = res; 54 mRv = rv; 55 mBgOriginalSize = res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_size); 56 mBg = res.getDrawable(R.drawable.container_fastscroll_popup_bg); 57 mBg.setBounds(0, 0, mBgOriginalSize, mBgOriginalSize); 58 mTextPaint = new Paint(); 59 mTextPaint.setColor(Color.WHITE); 60 mTextPaint.setAntiAlias(true); 61 mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_text_size)); 62 } 63 64 /** 65 * Sets the section name. 66 */ setSectionName(String sectionName)67 public void setSectionName(String sectionName) { 68 if (!sectionName.equals(mSectionName)) { 69 mSectionName = sectionName; 70 mTextPaint.getTextBounds(sectionName, 0, sectionName.length(), mTextBounds); 71 // Update the width to use measureText since that is more accurate 72 mTextBounds.right = (int) (mTextBounds.left + mTextPaint.measureText(sectionName)); 73 } 74 } 75 76 /** 77 * Updates the bounds for the fast scroller. 78 * @return the invalidation rect for this update. 79 */ updateFastScrollerBounds(int lastTouchY)80 public Rect updateFastScrollerBounds(int lastTouchY) { 81 mInvalidateRect.set(mBgBounds); 82 83 if (isVisible()) { 84 // Calculate the dimensions and position of the fast scroller popup 85 int edgePadding = mRv.getMaxScrollbarWidth(); 86 int bgPadding = (mBgOriginalSize - mTextBounds.height()) / 2; 87 int bgHeight = mBgOriginalSize; 88 int bgWidth = Math.max(mBgOriginalSize, mTextBounds.width() + (2 * bgPadding)); 89 if (Utilities.isRtl(mRes)) { 90 mBgBounds.left = mRv.getBackgroundPadding().left + (2 * mRv.getMaxScrollbarWidth()); 91 mBgBounds.right = mBgBounds.left + bgWidth; 92 } else { 93 mBgBounds.right = mRv.getWidth() - mRv.getBackgroundPadding().right - 94 (2 * mRv.getMaxScrollbarWidth()); 95 mBgBounds.left = mBgBounds.right - bgWidth; 96 } 97 mBgBounds.top = lastTouchY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgHeight); 98 mBgBounds.top = Math.max(edgePadding, 99 Math.min(mBgBounds.top, mRv.getHeight() - edgePadding - bgHeight)); 100 mBgBounds.bottom = mBgBounds.top + bgHeight; 101 } else { 102 mBgBounds.setEmpty(); 103 } 104 105 // Combine the old and new fast scroller bounds to create the full invalidate rect 106 mInvalidateRect.union(mBgBounds); 107 return mInvalidateRect; 108 } 109 110 /** 111 * Animates the visibility of the fast scroller popup. 112 */ animateVisibility(boolean visible)113 public void animateVisibility(boolean visible) { 114 if (mVisible != visible) { 115 mVisible = visible; 116 if (mAlphaAnimator != null) { 117 mAlphaAnimator.cancel(); 118 } 119 mAlphaAnimator = ObjectAnimator.ofFloat(this, "alpha", visible ? 1f : 0f); 120 mAlphaAnimator.setDuration(visible ? 200 : 150); 121 mAlphaAnimator.start(); 122 } 123 } 124 125 // Setter/getter for the popup alpha for animations setAlpha(float alpha)126 public void setAlpha(float alpha) { 127 mAlpha = alpha; 128 mRv.invalidate(mBgBounds); 129 } 130 getAlpha()131 public float getAlpha() { 132 return mAlpha; 133 } 134 getHeight()135 public int getHeight() { 136 return mBgOriginalSize; 137 } 138 draw(Canvas c)139 public void draw(Canvas c) { 140 if (isVisible()) { 141 // Draw the fast scroller popup 142 int restoreCount = c.save(Canvas.MATRIX_SAVE_FLAG); 143 c.translate(mBgBounds.left, mBgBounds.top); 144 mTmpRect.set(mBgBounds); 145 mTmpRect.offsetTo(0, 0); 146 mBg.setBounds(mTmpRect); 147 mBg.setAlpha((int) (mAlpha * 255)); 148 mBg.draw(c); 149 mTextPaint.setAlpha((int) (mAlpha * 255)); 150 c.drawText(mSectionName, (mBgBounds.width() - mTextBounds.width()) / 2, 151 mBgBounds.height() - (mBgBounds.height() - mTextBounds.height()) / 2, 152 mTextPaint); 153 c.restoreToCount(restoreCount); 154 } 155 } 156 isVisible()157 public boolean isVisible() { 158 return (mAlpha > 0f) && (mSectionName != null); 159 } 160 } 161