Home | History | Annotate | Download | only in launcher2
      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