1c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes/* 2c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Copyright (C) 2015 The Android Open Source Project 3c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * 4c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * you may not use this file except in compliance with the License. 6c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * You may obtain a copy of the License at 7c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * 8c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 9c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * 10c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Unless required by applicable law or agreed to in writing, software 11c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * See the License for the specific language governing permissions and 14c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * limitations under the License. 15c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes */ 16c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 17c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banespackage android.support.design.widget; 18c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 198a40d691aa783c2e85298e895a3e51e98606aa85Chris Banesimport android.content.res.ColorStateList; 20c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.content.res.TypedArray; 21c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Bitmap; 22c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Canvas; 23c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Color; 24c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Paint; 25c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.graphics.Rect; 265c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banesimport android.graphics.RectF; 27388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banesimport android.graphics.Typeface; 28c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.os.Build; 298a40d691aa783c2e85298e895a3e51e98606aa85Chris Banesimport android.support.annotation.ColorInt; 3012e0a308d1ab838cb05c395baa6f1251fcbb2363Aurimas Liutikasimport android.support.v4.math.MathUtils; 31fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banesimport android.support.v4.text.TextDirectionHeuristicsCompat; 325c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banesimport android.support.v4.view.GravityCompat; 33c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.support.v4.view.ViewCompat; 348a40d691aa783c2e85298e895a3e51e98606aa85Chris Banesimport android.support.v7.widget.TintTypedArray; 35c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.text.TextPaint; 36c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.text.TextUtils; 37c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.Gravity; 38c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.View; 39c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesimport android.view.animation.Interpolator; 40c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 41c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banesfinal class CollapsingTextHelper { 42c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 43c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // Pre-JB-MR2 doesn't support HW accelerated canvas scaled text so we will workaround it 44c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // by using our own texture 45c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private static final boolean USE_SCALING_TEXTURE = Build.VERSION.SDK_INT < 18; 46c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 47c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private static final boolean DEBUG_DRAW = false; 48c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private static final Paint DEBUG_DRAW_PAINT; 49c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes static { 50c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes DEBUG_DRAW_PAINT = DEBUG_DRAW ? new Paint() : null; 51c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (DEBUG_DRAW_PAINT != null) { 52c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes DEBUG_DRAW_PAINT.setAntiAlias(true); 53c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes DEBUG_DRAW_PAINT.setColor(Color.MAGENTA); 54c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 55c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 56c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 57c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private final View mView; 58c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 5966a875e525700de5eed715a52a3329ab5a310d96Chris Banes private boolean mDrawTitle; 60c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private float mExpandedFraction; 61c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 62c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private final Rect mExpandedBounds; 63c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private final Rect mCollapsedBounds; 645c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private final RectF mCurrentBounds; 655c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private int mExpandedTextGravity = Gravity.CENTER_VERTICAL; 665c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private int mCollapsedTextGravity = Gravity.CENTER_VERTICAL; 679934230aa2959ee9f1802aa81fd21e25030b0d10Chris Banes private float mExpandedTextSize = 15; 689934230aa2959ee9f1802aa81fd21e25030b0d10Chris Banes private float mCollapsedTextSize = 15; 698a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes private ColorStateList mExpandedTextColor; 708a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes private ColorStateList mCollapsedTextColor; 71c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 725c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private float mExpandedDrawY; 735c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private float mCollapsedDrawY; 745c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private float mExpandedDrawX; 755c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private float mCollapsedDrawX; 765c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private float mCurrentDrawX; 775c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private float mCurrentDrawY; 78d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes private Typeface mCollapsedTypeface; 79d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes private Typeface mExpandedTypeface; 80d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes private Typeface mCurrentTypeface; 81c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 82c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private CharSequence mText; 83c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private CharSequence mTextToDraw; 84fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes private boolean mIsRtl; 85c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 86c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private boolean mUseTexture; 87c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private Bitmap mExpandedTitleTexture; 88c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private Paint mTexturePaint; 89c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private float mTextureAscent; 90c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private float mTextureDescent; 91c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 92c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private float mScale; 93a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes private float mCurrentTextSize; 94c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 958a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes private int[] mState; 968a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes 979995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private boolean mBoundsChanged; 989995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 99c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private final TextPaint mTextPaint; 100c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 101c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private Interpolator mPositionInterpolator; 102c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private Interpolator mTextSizeInterpolator; 103c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 10423caabcc1fc96e3756d0c354b49290467080de6aChris Banes private float mCollapsedShadowRadius, mCollapsedShadowDx, mCollapsedShadowDy; 10523caabcc1fc96e3756d0c354b49290467080de6aChris Banes private int mCollapsedShadowColor; 10623caabcc1fc96e3756d0c354b49290467080de6aChris Banes 10723caabcc1fc96e3756d0c354b49290467080de6aChris Banes private float mExpandedShadowRadius, mExpandedShadowDx, mExpandedShadowDy; 10823caabcc1fc96e3756d0c354b49290467080de6aChris Banes private int mExpandedShadowColor; 10923caabcc1fc96e3756d0c354b49290467080de6aChris Banes 110c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes public CollapsingTextHelper(View view) { 111c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mView = view; 112c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 113973218c60005eb672a27b027403c9231d5d21303Chris Banes mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.SUBPIXEL_TEXT_FLAG); 114c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 115c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mCollapsedBounds = new Rect(); 116c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedBounds = new Rect(); 1175c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCurrentBounds = new RectF(); 118c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 119c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 120c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setTextSizeInterpolator(Interpolator interpolator) { 121c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mTextSizeInterpolator = interpolator; 122c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 123c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 124c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 125c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setPositionInterpolator(Interpolator interpolator) { 126c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mPositionInterpolator = interpolator; 127c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 128c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 129c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 130c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setExpandedTextSize(float textSize) { 131c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mExpandedTextSize != textSize) { 132c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedTextSize = textSize; 133c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 134c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 135c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 136c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 137c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setCollapsedTextSize(float textSize) { 138c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mCollapsedTextSize != textSize) { 139c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mCollapsedTextSize = textSize; 140c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 141c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 142c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 143c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 1448a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes void setCollapsedTextColor(ColorStateList textColor) { 145c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mCollapsedTextColor != textColor) { 146c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mCollapsedTextColor = textColor; 147c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 148c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 149c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 150c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 1518a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes void setExpandedTextColor(ColorStateList textColor) { 152c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mExpandedTextColor != textColor) { 153c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedTextColor = textColor; 154c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 155c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 156c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 157c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 158c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setExpandedBounds(int left, int top, int right, int bottom) { 1599995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (!rectEquals(mExpandedBounds, left, top, right, bottom)) { 1609995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mExpandedBounds.set(left, top, right, bottom); 1619995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mBoundsChanged = true; 16266a875e525700de5eed715a52a3329ab5a310d96Chris Banes onBoundsChanged(); 1639995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 164c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 165c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 166c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setCollapsedBounds(int left, int top, int right, int bottom) { 1679995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes if (!rectEquals(mCollapsedBounds, left, top, right, bottom)) { 1689995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mCollapsedBounds.set(left, top, right, bottom); 1699995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mBoundsChanged = true; 17066a875e525700de5eed715a52a3329ab5a310d96Chris Banes onBoundsChanged(); 1719995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 172c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 173c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 17466a875e525700de5eed715a52a3329ab5a310d96Chris Banes void onBoundsChanged() { 17566a875e525700de5eed715a52a3329ab5a310d96Chris Banes mDrawTitle = mCollapsedBounds.width() > 0 && mCollapsedBounds.height() > 0 17666a875e525700de5eed715a52a3329ab5a310d96Chris Banes && mExpandedBounds.width() > 0 && mExpandedBounds.height() > 0; 17766a875e525700de5eed715a52a3329ab5a310d96Chris Banes } 17866a875e525700de5eed715a52a3329ab5a310d96Chris Banes 1795c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes void setExpandedTextGravity(int gravity) { 1805c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes if (mExpandedTextGravity != gravity) { 1815c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mExpandedTextGravity = gravity; 182c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 183c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 184c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 185c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 1865c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes int getExpandedTextGravity() { 1875c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes return mExpandedTextGravity; 1885c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes } 189c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 1905c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes void setCollapsedTextGravity(int gravity) { 1915c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes if (mCollapsedTextGravity != gravity) { 1925c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCollapsedTextGravity = gravity; 193c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 194c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 195c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 196c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 1975c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes int getCollapsedTextGravity() { 1985c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes return mCollapsedTextGravity; 1995c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes } 2005c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes 201c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setCollapsedTextAppearance(int resId) { 2028a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), resId, 2036759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance); 2046759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor)) { 2058a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes mCollapsedTextColor = a.getColorStateList( 2068a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor); 207c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 2086759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize)) { 209c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mCollapsedTextSize = a.getDimensionPixelSize( 2106759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize, 2116759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski (int) mCollapsedTextSize); 212c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 2136759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mCollapsedShadowColor = a.getInt( 2146759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowColor, 0); 2156759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mCollapsedShadowDx = a.getFloat( 2166759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDx, 0); 2176759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mCollapsedShadowDy = a.getFloat( 2186759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDy, 0); 2196759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mCollapsedShadowRadius = a.getFloat( 2206759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowRadius, 0); 221c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes a.recycle(); 222c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 223d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes if (Build.VERSION.SDK_INT >= 16) { 224d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mCollapsedTypeface = readFontFamilyTypeface(resId); 225d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 226d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes 227c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 228c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 229c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 230c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setExpandedTextAppearance(int resId) { 2318a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes TintTypedArray a = TintTypedArray.obtainStyledAttributes(mView.getContext(), resId, 2326759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance); 2336759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor)) { 2348a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes mExpandedTextColor = a.getColorStateList( 2358a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes android.support.v7.appcompat.R.styleable.TextAppearance_android_textColor); 236c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 2376759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski if (a.hasValue(android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize)) { 238c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedTextSize = a.getDimensionPixelSize( 2396759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_textSize, 2406759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski (int) mExpandedTextSize); 241c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 2426759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mExpandedShadowColor = a.getInt( 2436759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowColor, 0); 2446759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mExpandedShadowDx = a.getFloat( 2456759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDx, 0); 2466759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mExpandedShadowDy = a.getFloat( 2476759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowDy, 0); 2486759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski mExpandedShadowRadius = a.getFloat( 2496759b1021d8198ad1d239bb30e5a102b99624bceAdam Lesinski android.support.v7.appcompat.R.styleable.TextAppearance_android_shadowRadius, 0); 250c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes a.recycle(); 251c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 252d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes if (Build.VERSION.SDK_INT >= 16) { 253d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mExpandedTypeface = readFontFamilyTypeface(resId); 254d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 255d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes 256c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 257c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 258c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 259d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes private Typeface readFontFamilyTypeface(int resId) { 260d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes final TypedArray a = mView.getContext().obtainStyledAttributes(resId, 261d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes new int[]{android.R.attr.fontFamily}); 262d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes try { 263d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes final String family = a.getString(0); 264d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes if (family != null) { 265d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes return Typeface.create(family, Typeface.NORMAL); 266d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 267d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } finally { 268d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes a.recycle(); 269d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 270d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes return null; 271d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 272d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes 273d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes void setCollapsedTypeface(Typeface typeface) { 274ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas if (areTypefacesDifferent(mCollapsedTypeface, typeface)) { 275d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mCollapsedTypeface = typeface; 276d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes recalculate(); 277388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes } 278d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 279d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes 280d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes void setExpandedTypeface(Typeface typeface) { 281ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas if (areTypefacesDifferent(mExpandedTypeface, typeface)) { 282d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mExpandedTypeface = typeface; 283388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes recalculate(); 284388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes } 285388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes } 286388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes 287d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes void setTypefaces(Typeface typeface) { 288d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mCollapsedTypeface = mExpandedTypeface = typeface; 289d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes recalculate(); 290d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 291d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes 292d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes Typeface getCollapsedTypeface() { 293d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes return mCollapsedTypeface != null ? mCollapsedTypeface : Typeface.DEFAULT; 294d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 295d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes 296d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes Typeface getExpandedTypeface() { 297d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes return mExpandedTypeface != null ? mExpandedTypeface : Typeface.DEFAULT; 298388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes } 299388841ca2f07d0da9bbdc66c51abe789e05cf17bChris Banes 300c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes /** 301c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Set the value indicating the current scroll value. This decides how much of the 302c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * background will be displayed, as well as the title metrics/positioning. 303c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * 304c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * A value of {@code 0.0} indicates that the layout is fully expanded. 305c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * A value of {@code 1.0} indicates that the layout is fully collapsed. 306c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes */ 307c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setExpansionFraction(float fraction) { 30812e0a308d1ab838cb05c395baa6f1251fcbb2363Aurimas Liutikas fraction = MathUtils.clamp(fraction, 0f, 1f); 309a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 310c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (fraction != mExpandedFraction) { 311c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedFraction = fraction; 3125c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes calculateCurrentOffsets(); 313c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 314c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 315c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 3168a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes final boolean setState(final int[] state) { 3178a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes mState = state; 3188a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes 3198a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes if (isStateful()) { 3208a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes recalculate(); 3218a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes return true; 3228a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } 3238a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes 3248a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes return false; 3258a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } 3268a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes 3278a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes final boolean isStateful() { 3288a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes return (mCollapsedTextColor != null && mCollapsedTextColor.isStateful()) 3298a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes || (mExpandedTextColor != null && mExpandedTextColor.isStateful()); 3308a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } 3318a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes 332c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float getExpansionFraction() { 333c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return mExpandedFraction; 334c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 335c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 336c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float getCollapsedTextSize() { 337c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return mCollapsedTextSize; 338c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 339c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 340c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float getExpandedTextSize() { 341c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return mExpandedTextSize; 342c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 343c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 3445c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private void calculateCurrentOffsets() { 34523caabcc1fc96e3756d0c354b49290467080de6aChris Banes calculateOffsets(mExpandedFraction); 34623caabcc1fc96e3756d0c354b49290467080de6aChris Banes } 347c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 34823caabcc1fc96e3756d0c354b49290467080de6aChris Banes private void calculateOffsets(final float fraction) { 3495c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes interpolateBounds(fraction); 3505c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCurrentDrawX = lerp(mExpandedDrawX, mCollapsedDrawX, fraction, 3515c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mPositionInterpolator); 3525c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCurrentDrawY = lerp(mExpandedDrawY, mCollapsedDrawY, fraction, 3535c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mPositionInterpolator); 3545c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes 3555c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes setInterpolatedTextSize(lerp(mExpandedTextSize, mCollapsedTextSize, 356c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes fraction, mTextSizeInterpolator)); 357c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 358c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mCollapsedTextColor != mExpandedTextColor) { 359c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // If the collapsed and expanded text colors are different, blend them based on the 360c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // fraction 3618a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes mTextPaint.setColor(blendColors( 3628a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes getCurrentExpandedTextColor(), getCurrentCollapsedTextColor(), fraction)); 363c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } else { 3648a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes mTextPaint.setColor(getCurrentCollapsedTextColor()); 365c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 366c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 36723caabcc1fc96e3756d0c354b49290467080de6aChris Banes mTextPaint.setShadowLayer( 36823caabcc1fc96e3756d0c354b49290467080de6aChris Banes lerp(mExpandedShadowRadius, mCollapsedShadowRadius, fraction, null), 36923caabcc1fc96e3756d0c354b49290467080de6aChris Banes lerp(mExpandedShadowDx, mCollapsedShadowDx, fraction, null), 37023caabcc1fc96e3756d0c354b49290467080de6aChris Banes lerp(mExpandedShadowDy, mCollapsedShadowDy, fraction, null), 37123caabcc1fc96e3756d0c354b49290467080de6aChris Banes blendColors(mExpandedShadowColor, mCollapsedShadowColor, fraction)); 37223caabcc1fc96e3756d0c354b49290467080de6aChris Banes 373c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes ViewCompat.postInvalidateOnAnimation(mView); 374c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 375c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 3768a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes @ColorInt 3778a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes private int getCurrentExpandedTextColor() { 3788a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes if (mState != null) { 3798a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes return mExpandedTextColor.getColorForState(mState, 0); 3808a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } else { 3818a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes return mExpandedTextColor.getDefaultColor(); 3828a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } 3838a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } 3848a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes 3858a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes @ColorInt 3868a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes private int getCurrentCollapsedTextColor() { 3878a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes if (mState != null) { 3888a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes return mCollapsedTextColor.getColorForState(mState, 0); 3898a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } else { 3908a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes return mCollapsedTextColor.getDefaultColor(); 3918a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } 3928a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes } 3938a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes 3945c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private void calculateBaseOffsets() { 39537430e835e22a791b83b01befab44ead852bf555Chris Banes final float currentTextSize = mCurrentTextSize; 39637430e835e22a791b83b01befab44ead852bf555Chris Banes 397c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // We then calculate the collapsed text size, using the same logic 39837430e835e22a791b83b01befab44ead852bf555Chris Banes calculateUsingTextSize(mCollapsedTextSize); 3995c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes float width = mTextToDraw != null ? 4005c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0; 4015c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes final int collapsedAbsGravity = GravityCompat.getAbsoluteGravity(mCollapsedTextGravity, 4025c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR); 4035c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes switch (collapsedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) { 404c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes case Gravity.BOTTOM: 4055c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCollapsedDrawY = mCollapsedBounds.bottom; 406c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes break; 407c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes case Gravity.TOP: 4085c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCollapsedDrawY = mCollapsedBounds.top - mTextPaint.ascent(); 409c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes break; 410c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes case Gravity.CENTER_VERTICAL: 411c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes default: 412c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float textHeight = mTextPaint.descent() - mTextPaint.ascent(); 413c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float textOffset = (textHeight / 2) - mTextPaint.descent(); 4145c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCollapsedDrawY = mCollapsedBounds.centerY() + textOffset; 4155c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes break; 4165c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes } 417f828412cf077314f63e9d989c889f69275a28b19Chris Banes switch (collapsedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) { 4185c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes case Gravity.CENTER_HORIZONTAL: 4195c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCollapsedDrawX = mCollapsedBounds.centerX() - (width / 2); 4205c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes break; 4215c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes case Gravity.RIGHT: 4225c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCollapsedDrawX = mCollapsedBounds.right - width; 4235c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes break; 4245c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes case Gravity.LEFT: 4255c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes default: 4265c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCollapsedDrawX = mCollapsedBounds.left; 427c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes break; 428c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 429c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 43037430e835e22a791b83b01befab44ead852bf555Chris Banes calculateUsingTextSize(mExpandedTextSize); 4315c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes width = mTextToDraw != null 4325c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes ? mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0; 4335c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes final int expandedAbsGravity = GravityCompat.getAbsoluteGravity(mExpandedTextGravity, 4345c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR); 4355c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes switch (expandedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) { 436c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes case Gravity.BOTTOM: 4375c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mExpandedDrawY = mExpandedBounds.bottom; 438c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes break; 439c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes case Gravity.TOP: 4405c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mExpandedDrawY = mExpandedBounds.top - mTextPaint.ascent(); 441c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes break; 442c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes case Gravity.CENTER_VERTICAL: 443c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes default: 444c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float textHeight = mTextPaint.descent() - mTextPaint.ascent(); 445c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float textOffset = (textHeight / 2) - mTextPaint.descent(); 4465c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mExpandedDrawY = mExpandedBounds.centerY() + textOffset; 4475c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes break; 4485c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes } 449f828412cf077314f63e9d989c889f69275a28b19Chris Banes switch (expandedAbsGravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK) { 4505c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes case Gravity.CENTER_HORIZONTAL: 4515c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mExpandedDrawX = mExpandedBounds.centerX() - (width / 2); 4525c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes break; 4535c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes case Gravity.RIGHT: 4545c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mExpandedDrawX = mExpandedBounds.right - width; 4555c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes break; 4565c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes case Gravity.LEFT: 4575c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes default: 4585c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mExpandedDrawX = mExpandedBounds.left; 459c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes break; 460c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 461c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 462c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // The bounds have changed so we need to clear the texture 463c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes clearTexture(); 46437430e835e22a791b83b01befab44ead852bf555Chris Banes // Now reset the text size back to the original 46537430e835e22a791b83b01befab44ead852bf555Chris Banes setInterpolatedTextSize(currentTextSize); 466c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 467c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 4685c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private void interpolateBounds(float fraction) { 4695c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCurrentBounds.left = lerp(mExpandedBounds.left, mCollapsedBounds.left, 4705c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes fraction, mPositionInterpolator); 4715c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCurrentBounds.top = lerp(mExpandedDrawY, mCollapsedDrawY, 4725c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes fraction, mPositionInterpolator); 4735c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCurrentBounds.right = lerp(mExpandedBounds.right, mCollapsedBounds.right, 4745c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes fraction, mPositionInterpolator); 4755c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mCurrentBounds.bottom = lerp(mExpandedBounds.bottom, mCollapsedBounds.bottom, 4765c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes fraction, mPositionInterpolator); 4775c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes } 4785c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes 479c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes public void draw(Canvas canvas) { 480c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes final int saveCount = canvas.save(); 481c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 48266a875e525700de5eed715a52a3329ab5a310d96Chris Banes if (mTextToDraw != null && mDrawTitle) { 4835c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes float x = mCurrentDrawX; 4845c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes float y = mCurrentDrawY; 485c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 486c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes final boolean drawTexture = mUseTexture && mExpandedTitleTexture != null; 487c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 488c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes final float ascent; 489c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes final float descent; 490c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (drawTexture) { 491c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes ascent = mTextureAscent * mScale; 492c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes descent = mTextureDescent * mScale; 493c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } else { 494c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes ascent = mTextPaint.ascent() * mScale; 495c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes descent = mTextPaint.descent() * mScale; 496c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 497c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 498c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (DEBUG_DRAW) { 499c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas // Just a debug tool, which drawn a magenta rect in the text bounds 5005c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes canvas.drawRect(mCurrentBounds.left, y + ascent, mCurrentBounds.right, y + descent, 501c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes DEBUG_DRAW_PAINT); 502c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 503c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 504c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (drawTexture) { 505c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes y += ascent; 506c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 507c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 508c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mScale != 1f) { 509c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes canvas.scale(mScale, mScale, x, y); 510c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 511c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 512c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (drawTexture) { 513c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // If we should use a texture, draw it instead of text 514c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint); 515c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } else { 516c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes canvas.drawText(mTextToDraw, 0, mTextToDraw.length(), x, y, mTextPaint); 517c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 518c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 519c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 520c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes canvas.restoreToCount(saveCount); 521c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 522c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 523fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes private boolean calculateIsRtl(CharSequence text) { 524fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes final boolean defaultIsRtl = ViewCompat.getLayoutDirection(mView) 525fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes == ViewCompat.LAYOUT_DIRECTION_RTL; 526fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes return (defaultIsRtl 527fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL 528fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR).isRtl(text, 0, text.length()); 529fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes } 530fef6dbf449d6e2992872a725170c5fcb4aa16975Chris Banes 53137430e835e22a791b83b01befab44ead852bf555Chris Banes private void setInterpolatedTextSize(float textSize) { 53237430e835e22a791b83b01befab44ead852bf555Chris Banes calculateUsingTextSize(textSize); 53337430e835e22a791b83b01befab44ead852bf555Chris Banes 53437430e835e22a791b83b01befab44ead852bf555Chris Banes // Use our texture if the scale isn't 1.0 53537430e835e22a791b83b01befab44ead852bf555Chris Banes mUseTexture = USE_SCALING_TEXTURE && mScale != 1f; 53637430e835e22a791b83b01befab44ead852bf555Chris Banes 53737430e835e22a791b83b01befab44ead852bf555Chris Banes if (mUseTexture) { 53837430e835e22a791b83b01befab44ead852bf555Chris Banes // Make sure we have an expanded texture if needed 53937430e835e22a791b83b01befab44ead852bf555Chris Banes ensureExpandedTexture(); 54037430e835e22a791b83b01befab44ead852bf555Chris Banes } 54137430e835e22a791b83b01befab44ead852bf555Chris Banes 54237430e835e22a791b83b01befab44ead852bf555Chris Banes ViewCompat.postInvalidateOnAnimation(mView); 54337430e835e22a791b83b01befab44ead852bf555Chris Banes } 54437430e835e22a791b83b01befab44ead852bf555Chris Banes 545ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas private boolean areTypefacesDifferent(Typeface first, Typeface second) { 546ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas return (first != null && !first.equals(second)) || (first == null && second != null); 547ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas } 548ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas 54937430e835e22a791b83b01befab44ead852bf555Chris Banes private void calculateUsingTextSize(final float textSize) { 550c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mText == null) return; 551c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 552b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes final float collapsedWidth = mCollapsedBounds.width(); 553b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes final float expandedWidth = mExpandedBounds.width(); 554b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes 555a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final float availableWidth; 556a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes final float newTextSize; 557a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes boolean updateDrawText = false; 558c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 559c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (isClose(textSize, mCollapsedTextSize)) { 560a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes newTextSize = mCollapsedTextSize; 561a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mScale = 1f; 562ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas if (areTypefacesDifferent(mCurrentTypeface, mCollapsedTypeface)) { 563d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mCurrentTypeface = mCollapsedTypeface; 564d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes updateDrawText = true; 565d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 566b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes availableWidth = collapsedWidth; 567c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } else { 568a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes newTextSize = mExpandedTextSize; 569ad1b0e82100ee31e70040d77bfa4d847b2bf0864Aurimas Liutikas if (areTypefacesDifferent(mCurrentTypeface, mExpandedTypeface)) { 570d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mCurrentTypeface = mExpandedTypeface; 571d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes updateDrawText = true; 572d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes } 573c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (isClose(textSize, mExpandedTextSize)) { 574c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // If we're close to the expanded text size, snap to it and use a scale of 1 575c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mScale = 1f; 576c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } else { 577c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // Else, we'll scale down from the expanded text size 578c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mScale = textSize / mExpandedTextSize; 579c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 580b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes 581b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes final float textSizeRatio = mCollapsedTextSize / mExpandedTextSize; 582b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes // This is the size of the expanded bounds when it is scaled to match the 583b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes // collapsed text size 584b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes final float scaledDownWidth = expandedWidth * textSizeRatio; 585b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes 586b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes if (scaledDownWidth > collapsedWidth) { 587b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes // If the scaled down size is larger than the actual collapsed width, we need to 588b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes // cap the available width so that when the expanded text scales down, it matches 589b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes // the collapsed width 590b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes availableWidth = Math.min(collapsedWidth / textSizeRatio, expandedWidth); 591b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes } else { 592b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes // Otherwise we'll just use the expanded width 593b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes availableWidth = expandedWidth; 594b2535ce06945e0b3b95590819b8e6467ea3ee29dChris Banes } 595c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 596c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 597a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (availableWidth > 0) { 598d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes updateDrawText = (mCurrentTextSize != newTextSize) || mBoundsChanged || updateDrawText; 599a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mCurrentTextSize = newTextSize; 6009995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes mBoundsChanged = false; 601a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes } 602a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 603a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes if (mTextToDraw == null || updateDrawText) { 604a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes mTextPaint.setTextSize(mCurrentTextSize); 605d29e79fa09f428c510d1d9ca758dbda35a323f71Chris Banes mTextPaint.setTypeface(mCurrentTypeface); 606973218c60005eb672a27b027403c9231d5d21303Chris Banes // Use linear text scaling if we're scaling the canvas 607973218c60005eb672a27b027403c9231d5d21303Chris Banes mTextPaint.setLinearText(mScale != 1f); 608a6a508b2296730ca6954aaebcca52a9962a5cb55Chris Banes 609c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // If we don't currently have text to draw, or the text size has changed, ellipsize... 610c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes final CharSequence title = TextUtils.ellipsize(mText, mTextPaint, 611c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes availableWidth, TextUtils.TruncateAt.END); 61237430e835e22a791b83b01befab44ead852bf555Chris Banes if (!TextUtils.equals(title, mTextToDraw)) { 613c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mTextToDraw = title; 61437430e835e22a791b83b01befab44ead852bf555Chris Banes mIsRtl = calculateIsRtl(mTextToDraw); 615c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 616c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 617c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 618c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 619c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private void ensureExpandedTexture() { 620c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mExpandedTitleTexture != null || mExpandedBounds.isEmpty() 621c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes || TextUtils.isEmpty(mTextToDraw)) { 622c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return; 623c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 624c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 62523caabcc1fc96e3756d0c354b49290467080de6aChris Banes calculateOffsets(0f); 6265c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mTextureAscent = mTextPaint.ascent(); 6275c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mTextureDescent = mTextPaint.descent(); 628c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 629c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes final int w = Math.round(mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length())); 6305c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes final int h = Math.round(mTextureDescent - mTextureAscent); 631c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 6326fa3c92dcaf584ac56bff4c0b52b7b1ac379592eChris Banes if (w <= 0 || h <= 0) { 633c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return; // If the width or height are 0, return 634c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 635c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 636c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedTitleTexture = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 637c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 638c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes Canvas c = new Canvas(mExpandedTitleTexture); 639c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes c.drawText(mTextToDraw, 0, mTextToDraw.length(), 0, h - mTextPaint.descent(), mTextPaint); 640c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 641c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mTexturePaint == null) { 642c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // Make sure we have a paint 6435c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes mTexturePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); 644c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 645c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 646c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 6471bf6093c6dab532bc57c75767470afa3cbbe0384Chris Banes public void recalculate() { 6481bf6093c6dab532bc57c75767470afa3cbbe0384Chris Banes if (mView.getHeight() > 0 && mView.getWidth() > 0) { 649c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // If we've already been laid out, calculate everything now otherwise we'll wait 650c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes // until a layout 6515c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes calculateBaseOffsets(); 6525c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes calculateCurrentOffsets(); 653c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 654c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 655c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 656c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes /** 657c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Set the title to display 658c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * 659c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * @param text 660c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes */ 661c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes void setText(CharSequence text) { 662c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (text == null || !text.equals(mText)) { 663c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mText = text; 664d9d3addb4628537f68348d40346765b7f407cf79Chris Banes mTextToDraw = null; 665c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes clearTexture(); 666c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes recalculate(); 667c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 668c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 669c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 670c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes CharSequence getText() { 671c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return mText; 672c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 673c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 674c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private void clearTexture() { 675c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (mExpandedTitleTexture != null) { 676c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedTitleTexture.recycle(); 677c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes mExpandedTitleTexture = null; 678c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 679c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 680c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 681c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes /** 682c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Returns true if {@code value} is 'close' to it's closest decimal value. Close is currently 683c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * defined as it's difference being < 0.001. 684c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes */ 685c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private static boolean isClose(float value, float targetValue) { 686c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return Math.abs(value - targetValue) < 0.001f; 687c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 688c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 6898a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes ColorStateList getExpandedTextColor() { 690c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return mExpandedTextColor; 691c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 692c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 6938a40d691aa783c2e85298e895a3e51e98606aa85Chris Banes ColorStateList getCollapsedTextColor() { 694c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return mCollapsedTextColor; 695c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 696c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 697c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes /** 698c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * Blend {@code color1} and {@code color2} using the given ratio. 699c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * 700c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * @param ratio of which to blend. 0.0 will return {@code color1}, 0.5 will give an even blend, 701c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes * 1.0 will return {@code color2}. 702c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes */ 703c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes private static int blendColors(int color1, int color2, float ratio) { 704c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes final float inverseRatio = 1f - ratio; 705c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio); 706c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio); 707c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio); 708c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio); 709c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return Color.argb((int) a, (int) r, (int) g, (int) b); 710c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 711c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes 7125c7d7bbceeaebc46dc2e4b2135cc330414f716d4Chris Banes private static float lerp(float startValue, float endValue, float fraction, 713c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes Interpolator interpolator) { 714c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes if (interpolator != null) { 715c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes fraction = interpolator.getInterpolation(fraction); 716c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 717c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes return AnimationUtils.lerp(startValue, endValue, fraction); 718c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes } 7199995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes 7209995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes private static boolean rectEquals(Rect r, int left, int top, int right, int bottom) { 7219995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes return !(r.left != left || r.top != top || r.right != right || r.bottom != bottom); 7229995f7bdb02dbe16155661545fd64046ad3d56c2Chris Banes } 723c5b63021dfda1dd155775a0b4845c5ca3c70b1b0Chris Banes} 724