1 /* 2 * Copyright (C) 2010 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 com.android.launcher2; 18 19 import android.content.Context; 20 import android.graphics.Bitmap; 21 import android.graphics.Bitmap.Config; 22 import android.graphics.Canvas; 23 import android.graphics.Paint; 24 import android.graphics.PorterDuff.Mode; 25 import android.graphics.drawable.Drawable; 26 import android.text.Layout; 27 import android.util.AttributeSet; 28 import android.widget.TextView; 29 30 /* 31 * This class is a bit of a hack, designed to speed up long text labels in Launcher. It caches the 32 * text in a TextView to a bitmap and then just draws that Bitmap instead afterward, speeding up 33 * rendering. Marquee scrolling is not currently supported. 34 * 35 */ 36 public class CachedTextView extends TextView { 37 private Bitmap mCache; 38 private final Paint mCachePaint = new Paint(); 39 private final Canvas mCacheCanvas = new Canvas(); 40 41 private int mPrevAlpha = -1; 42 private boolean mIsBuildingCache; 43 boolean mIsTextCacheDirty; 44 float mTextCacheLeft; 45 float mTextCacheTop; 46 float mTextCacheScrollX; 47 float mRectLeft, mRectTop; 48 private float mPaddingH = 0; 49 private float mPaddingV = 0; 50 private CharSequence mText; 51 private boolean mEnabled = true; 52 53 public CachedTextView(Context context) { 54 super(context); 55 } 56 57 public CachedTextView(Context context, AttributeSet attrs) { 58 super(context, attrs); 59 } 60 61 public CachedTextView(Context context, AttributeSet attrs, int defStyle) { 62 super(context, attrs, defStyle); 63 } 64 65 protected int getCacheTopPadding() { 66 return 0; 67 } 68 protected int getCacheLeftPadding() { 69 return 0; 70 } 71 protected int getCacheRightPadding() { 72 return 0; 73 } 74 protected int getCacheBottomPadding() { 75 return 0; 76 } 77 78 public void disableCache() { 79 mEnabled = false; 80 } 81 82 public void setText(CharSequence text, BufferType type) { 83 super.setText(text, type); 84 mIsTextCacheDirty = true; 85 } 86 87 private void buildAndUpdateCache() { 88 final Layout layout = getLayout(); 89 final int left = getCompoundPaddingLeft(); 90 final int top = getExtendedPaddingTop(); 91 final float prevAlpha = getAlpha(); 92 93 mTextCacheLeft = layout.getLineLeft(0) - getCacheLeftPadding(); 94 mTextCacheTop = top + layout.getLineTop(0) - mPaddingV - getCacheTopPadding(); 95 96 mRectLeft = mScrollX + getLeft(); 97 mRectTop = 0; 98 mTextCacheScrollX = mScrollX; 99 100 final float textCacheRight = 101 Math.min(left + layout.getLineRight(0) + mPaddingH, mScrollX + mRight - mLeft) + 102 getCacheRightPadding(); 103 final float textCacheBottom = top + layout.getLineBottom(0) + mPaddingV + 104 getCacheBottomPadding(); 105 final float xCharWidth = getPaint().measureText("x"); 106 107 int width = (int) (textCacheRight - mTextCacheLeft + (2 * xCharWidth)); 108 int height = (int) (textCacheBottom - mTextCacheTop); 109 110 if (width > 0 && height > 0) { 111 if (mCache != null) { 112 if (mCache.getWidth() != width || mCache.getHeight() != height) { 113 mCache.recycle(); 114 mCache = null; 115 } 116 } 117 if (mCache == null) { 118 mCache = Bitmap.createBitmap(width, height, Config.ARGB_8888); 119 mCacheCanvas.setBitmap(mCache); 120 } else { 121 mCacheCanvas.drawColor(0, Mode.CLEAR); 122 } 123 124 mCacheCanvas.save(); 125 mCacheCanvas.translate(-mTextCacheLeft, -mTextCacheTop); 126 127 mIsBuildingCache = true; 128 setAlpha(1.0f); 129 draw(mCacheCanvas); 130 setAlpha(prevAlpha); 131 mIsBuildingCache = false; 132 mCacheCanvas.restore(); 133 mCacheCanvas.setBitmap(null); 134 135 // A hack-- we set the text to be one space (we don't make it empty just to avoid any 136 // potential issues with text measurement, like line height, etc.) so that the text view 137 // doesn't draw it anymore, since it's been cached. 138 mText = getText(); 139 setText(" "); 140 } 141 } 142 143 public CharSequence getText() { 144 return (mText == null) ? super.getText() : mText; 145 } 146 147 public void draw(Canvas canvas) { 148 if (mEnabled && mIsTextCacheDirty && !mIsBuildingCache) { 149 buildAndUpdateCache(); 150 mIsTextCacheDirty = false; 151 } 152 if (mCache != null && !mIsBuildingCache) { 153 canvas.drawBitmap(mCache, mTextCacheLeft - mTextCacheScrollX + mScrollX, 154 mTextCacheTop, mCachePaint); 155 } 156 super.draw(canvas); 157 } 158 159 protected boolean isBuildingCache() { 160 return mIsBuildingCache; 161 } 162 163 @Override 164 protected boolean onSetAlpha(int alpha) { 165 if (mPrevAlpha != alpha) { 166 mPrevAlpha = alpha; 167 mCachePaint.setAlpha(alpha); 168 169 // We manually update the drawables alpha since the default TextView implementation may 170 // not do this if there is a background set (which we may due to the focus bg) 171 final Drawable[] dr = getCompoundDrawables(); 172 for (int i = 0; i < dr.length; ++i) { 173 if (dr[i] != null) { 174 dr[i].mutate().setAlpha(alpha); 175 } 176 } 177 178 super.onSetAlpha(alpha); 179 } 180 return true; 181 } 182 } 183