Setters for CollapsingToolbarLayout title gravity
Most of the work is in CollapsingTextHelper to able
to support both v-grav and h-grav.
BUG: 22078891
Change-Id: Ie034c3a8077fb00174374d145ffba821fd49f0c1
diff --git a/design/api/current.txt b/design/api/current.txt
index 5ac4b7c..7ecf0f8 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -65,14 +65,18 @@
ctor public CollapsingToolbarLayout(android.content.Context);
ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet, int);
+ method public int getCollapsedTitleGravity();
method public android.graphics.drawable.Drawable getContentScrim();
+ method public int getExpandedTitleGravity();
method public android.graphics.drawable.Drawable getStatusBarScrim();
+ method public void setCollapsedTitleGravity(int);
method public void setCollapsedTitleTextAppearance(int);
method public void setCollapsedTitleTextColor(int);
method public void setContentScrim(android.graphics.drawable.Drawable);
method public void setContentScrimColor(int);
method public void setContentScrimResource(int);
method public void setExpandedTitleColor(int);
+ method public void setExpandedTitleGravity(int);
method public void setExpandedTitleTextAppearance(int);
method public void setStatusBarScrim(android.graphics.drawable.Drawable);
method public void setStatusBarScrimColor(int);
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index 9702497..a23b699 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -239,6 +239,54 @@
<!-- The id of the primary Toolbar child that you wish to use for the purpose of collapsing.
If you do not set this then the first Toolbar child found will be used. -->
<attr name="toolbarId" format="reference"/>
+
+ <!-- Specifies how the title should be positioned when collapsed. -->
+ <attr name="collapsedTitleGravity">
+ <!-- Push title to the top of its container, not changing its size. -->
+ <flag name="top" value="0x30"/>
+ <!-- Push title to the bottom of its container, not changing its size. -->
+ <flag name="bottom" value="0x50"/>
+ <!-- Push title to the left of its container, not changing its size. -->
+ <flag name="left" value="0x03"/>
+ <!-- Push title to the right of its container, not changing its size. -->
+ <flag name="right" value="0x05"/>
+ <!-- Place title in the vertical center of its container, not changing its size. -->
+ <flag name="center_vertical" value="0x10"/>
+ <!-- Grow the vertical size of the title if needed so it completely fills its container. -->
+ <flag name="fill_vertical" value="0x70"/>
+ <!-- Place title in the horizontal center of its container, not changing its size. -->
+ <flag name="center_horizontal" value="0x01"/>
+ <!-- Place the title in the center of its container in both the vertical and horizontal axis, not changing its size. -->
+ <flag name="center" value="0x11"/>
+ <!-- Push title to the beginning of its container, not changing its size. -->
+ <flag name="start" value="0x00800003"/>
+ <!-- Push title to the end of its container, not changing its size. -->
+ <flag name="end" value="0x00800005"/>
+ </attr>
+
+ <!-- Specifies how the title should be positioned when expanded. -->
+ <attr name="expandedTitleGravity">
+ <!-- Push title to the top of its container, not changing its size. -->
+ <flag name="top" value="0x30"/>
+ <!-- Push title to the bottom of its container, not changing its size. -->
+ <flag name="bottom" value="0x50"/>
+ <!-- Push title to the left of its container, not changing its size. -->
+ <flag name="left" value="0x03"/>
+ <!-- Push title to the right of its container, not changing its size. -->
+ <flag name="right" value="0x05"/>
+ <!-- Place title in the vertical center of its container, not changing its size. -->
+ <flag name="center_vertical" value="0x10"/>
+ <!-- Grow the vertical size of the title if needed so it completely fills its container. -->
+ <flag name="fill_vertical" value="0x70"/>
+ <!-- Place title in the horizontal center of its container, not changing its size. -->
+ <flag name="center_horizontal" value="0x01"/>
+ <!-- Place the title in the center of its container in both the vertical and horizontal axis, not changing its size. -->
+ <flag name="center" value="0x11"/>
+ <!-- Push title to the beginning of its container, not changing its size. -->
+ <flag name="start" value="0x00800003"/>
+ <!-- Push title to the end of its container, not changing its size. -->
+ <flag name="end" value="0x00800005"/>
+ </attr>
</declare-styleable>
<declare-styleable name="CollapsingAppBarLayout_LayoutParams">
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
index 636770e..8b3bd84 100644
--- a/design/src/android/support/design/widget/CollapsingTextHelper.java
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -22,10 +22,12 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.Build;
import android.support.design.R;
import android.support.v4.text.TextDirectionHeuristicsCompat;
+import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.text.TextPaint;
import android.text.TextUtils;
@@ -55,19 +57,23 @@
private final Rect mExpandedBounds;
private final Rect mCollapsedBounds;
- private int mExpandedTextVerticalGravity = Gravity.CENTER_VERTICAL;
- private int mCollapsedTextVerticalGravity = Gravity.CENTER_VERTICAL;
+ private final RectF mCurrentBounds;
+ private int mExpandedTextGravity = Gravity.CENTER_VERTICAL;
+ private int mCollapsedTextGravity = Gravity.CENTER_VERTICAL;
private float mExpandedTextSize;
private float mCollapsedTextSize;
private int mExpandedTextColor;
private int mCollapsedTextColor;
- private float mExpandedTop;
- private float mCollapsedTop;
+ private float mExpandedDrawY;
+ private float mCollapsedDrawY;
+ private float mExpandedDrawX;
+ private float mCollapsedDrawX;
+ private float mCurrentDrawX;
+ private float mCurrentDrawY;
private CharSequence mText;
private CharSequence mTextToDraw;
- private float mTextWidth;
private boolean mIsRtl;
private boolean mUseTexture;
@@ -76,9 +82,6 @@
private float mTextureAscent;
private float mTextureDescent;
- private float mCurrentLeft;
- private float mCurrentRight;
- private float mCurrentTop;
private float mScale;
private float mCurrentTextSize;
@@ -95,6 +98,7 @@
mCollapsedBounds = new Rect();
mExpandedBounds = new Rect();
+ mCurrentBounds = new RectF();
}
void setTextSizeInterpolator(Interpolator interpolator) {
@@ -143,24 +147,28 @@
mCollapsedBounds.set(left, top, right, bottom);
}
- void setExpandedTextVerticalGravity(int gravity) {
- gravity &= Gravity.VERTICAL_GRAVITY_MASK;
-
- if (mExpandedTextVerticalGravity != gravity) {
- mExpandedTextVerticalGravity = gravity;
+ void setExpandedTextGravity(int gravity) {
+ if (mExpandedTextGravity != gravity) {
+ mExpandedTextGravity = gravity;
recalculate();
}
}
- void setCollapsedTextVerticalGravity(int gravity) {
- gravity &= Gravity.VERTICAL_GRAVITY_MASK;
+ int getExpandedTextGravity() {
+ return mExpandedTextGravity;
+ }
- if (mCollapsedTextVerticalGravity != gravity) {
- mCollapsedTextVerticalGravity = gravity;
+ void setCollapsedTextGravity(int gravity) {
+ if (mCollapsedTextGravity != gravity) {
+ mCollapsedTextGravity = gravity;
recalculate();
}
}
+ int getCollapsedTextGravity() {
+ return mCollapsedTextGravity;
+ }
+
void setCollapsedTextAppearance(int resId) {
TypedArray a = mView.getContext().obtainStyledAttributes(resId, R.styleable.TextAppearance);
if (a.hasValue(R.styleable.TextAppearance_android_textColor)) {
@@ -215,7 +223,7 @@
if (fraction != mExpandedFraction) {
mExpandedFraction = fraction;
- calculateOffsets();
+ calculateCurrentOffsets();
}
}
@@ -231,15 +239,16 @@
return mExpandedTextSize;
}
- private void calculateOffsets() {
+ private void calculateCurrentOffsets() {
final float fraction = mExpandedFraction;
- mCurrentLeft = interpolate(mExpandedBounds.left, mCollapsedBounds.left,
- fraction, mPositionInterpolator);
- mCurrentTop = interpolate(mExpandedTop, mCollapsedTop, fraction, mPositionInterpolator);
- mCurrentRight = interpolate(mExpandedBounds.right, mCollapsedBounds.right,
- fraction, mPositionInterpolator);
- setInterpolatedTextSize(interpolate(mExpandedTextSize, mCollapsedTextSize,
+ interpolateBounds(fraction);
+ mCurrentDrawX = lerp(mExpandedDrawX, mCollapsedDrawX, fraction,
+ mPositionInterpolator);
+ mCurrentDrawY = lerp(mExpandedDrawY, mCollapsedDrawY, fraction,
+ mPositionInterpolator);
+
+ setInterpolatedTextSize(lerp(mExpandedTextSize, mCollapsedTextSize,
fraction, mTextSizeInterpolator));
if (mCollapsedTextColor != mExpandedTextColor) {
@@ -253,54 +262,93 @@
ViewCompat.postInvalidateOnAnimation(mView);
}
- private void calculateBaselines() {
+ private void calculateBaseOffsets() {
// We then calculate the collapsed text size, using the same logic
mTextPaint.setTextSize(mCollapsedTextSize);
- switch (mCollapsedTextVerticalGravity) {
+ float width = mTextToDraw != null ?
+ mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0;
+ final int collapsedAbsGravity = GravityCompat.getAbsoluteGravity(mCollapsedTextGravity,
+ mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);
+ switch (collapsedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.BOTTOM:
- mCollapsedTop = mCollapsedBounds.bottom;
+ mCollapsedDrawY = mCollapsedBounds.bottom;
break;
case Gravity.TOP:
- mCollapsedTop = mCollapsedBounds.top - mTextPaint.ascent();
+ mCollapsedDrawY = mCollapsedBounds.top - mTextPaint.ascent();
break;
case Gravity.CENTER_VERTICAL:
default:
float textHeight = mTextPaint.descent() - mTextPaint.ascent();
float textOffset = (textHeight / 2) - mTextPaint.descent();
- mCollapsedTop = mCollapsedBounds.centerY() + textOffset;
+ mCollapsedDrawY = mCollapsedBounds.centerY() + textOffset;
+ break;
+ }
+ switch (collapsedAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ mCollapsedDrawX = mCollapsedBounds.centerX() - (width / 2);
+ break;
+ case Gravity.RIGHT:
+ mCollapsedDrawX = mCollapsedBounds.right - width;
+ break;
+ case Gravity.LEFT:
+ default:
+ mCollapsedDrawX = mCollapsedBounds.left;
break;
}
mTextPaint.setTextSize(mExpandedTextSize);
- switch (mExpandedTextVerticalGravity) {
+ width = mTextToDraw != null
+ ? mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()) : 0;
+ final int expandedAbsGravity = GravityCompat.getAbsoluteGravity(mExpandedTextGravity,
+ mIsRtl ? ViewCompat.LAYOUT_DIRECTION_RTL : ViewCompat.LAYOUT_DIRECTION_LTR);
+ switch (expandedAbsGravity & Gravity.VERTICAL_GRAVITY_MASK) {
case Gravity.BOTTOM:
- mExpandedTop = mExpandedBounds.bottom;
+ mExpandedDrawY = mExpandedBounds.bottom;
break;
case Gravity.TOP:
- mExpandedTop = mExpandedBounds.top - mTextPaint.ascent();
+ mExpandedDrawY = mExpandedBounds.top - mTextPaint.ascent();
break;
case Gravity.CENTER_VERTICAL:
default:
float textHeight = mTextPaint.descent() - mTextPaint.ascent();
float textOffset = (textHeight / 2) - mTextPaint.descent();
- mExpandedTop = mExpandedBounds.centerY() + textOffset;
+ mExpandedDrawY = mExpandedBounds.centerY() + textOffset;
break;
}
- mTextureAscent = mTextPaint.ascent();
- mTextureDescent = mTextPaint.descent();
+ switch (expandedAbsGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ mExpandedDrawX = mExpandedBounds.centerX() - (width / 2);
+ break;
+ case Gravity.RIGHT:
+ mExpandedDrawX = mExpandedBounds.right - width;
+ break;
+ case Gravity.LEFT:
+ default:
+ mExpandedDrawX = mExpandedBounds.left;
+ break;
+ }
// The bounds have changed so we need to clear the texture
clearTexture();
}
+ private void interpolateBounds(float fraction) {
+ mCurrentBounds.left = lerp(mExpandedBounds.left, mCollapsedBounds.left,
+ fraction, mPositionInterpolator);
+ mCurrentBounds.top = lerp(mExpandedDrawY, mCollapsedDrawY,
+ fraction, mPositionInterpolator);
+ mCurrentBounds.right = lerp(mExpandedBounds.right, mCollapsedBounds.right,
+ fraction, mPositionInterpolator);
+ mCurrentBounds.bottom = lerp(mExpandedBounds.bottom, mCollapsedBounds.bottom,
+ fraction, mPositionInterpolator);
+ }
+
public void draw(Canvas canvas) {
final int saveCount = canvas.save();
if (mTextToDraw != null) {
- final boolean isRtl = mIsRtl;
-
- float x = isRtl ? mCurrentRight : mCurrentLeft;
- float y = mCurrentTop;
+ float x = mCurrentDrawX;
+ float y = mCurrentDrawY;
final boolean drawTexture = mUseTexture && mExpandedTitleTexture != null;
@@ -320,7 +368,7 @@
if (DEBUG_DRAW) {
// Just a debug tool, which drawn a Magneta rect in the text bounds
- canvas.drawRect(mCurrentLeft, y + ascent, mCurrentRight, y + descent,
+ canvas.drawRect(mCurrentBounds.left, y + ascent, mCurrentBounds.right, y + descent,
DEBUG_DRAW_PAINT);
}
@@ -332,10 +380,6 @@
canvas.scale(mScale, mScale, x, y);
}
- if (isRtl) {
- x -= mTextWidth;
- }
-
if (drawTexture) {
// If we should use a texture, draw it instead of text
canvas.drawBitmap(mExpandedTitleTexture, x, y, mTexturePaint);
@@ -394,7 +438,6 @@
mTextToDraw = title;
}
mIsRtl = calculateIsRtl(mTextToDraw);
- mTextWidth = mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length());
}
// Use our texture if the scale isn't 1.0
@@ -416,10 +459,11 @@
mTextPaint.setTextSize(mExpandedTextSize);
mTextPaint.setColor(mExpandedTextColor);
+ mTextureAscent = mTextPaint.ascent();
+ mTextureDescent = mTextPaint.descent();
final int w = Math.round(mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length()));
- final int h = Math.round(mTextPaint.descent() - mTextPaint.ascent());
- mTextWidth = w;
+ final int h = Math.round(mTextureDescent - mTextureAscent);
if (w <= 0 && h <= 0) {
return; // If the width or height are 0, return
@@ -432,9 +476,7 @@
if (mTexturePaint == null) {
// Make sure we have a paint
- mTexturePaint = new Paint();
- mTexturePaint.setAntiAlias(true);
- mTexturePaint.setFilterBitmap(true);
+ mTexturePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
}
}
@@ -442,8 +484,8 @@
if (mView.getHeight() > 0 && mView.getWidth() > 0) {
// If we've already been laid out, calculate everything now otherwise we'll wait
// until a layout
- calculateBaselines();
- calculateOffsets();
+ calculateBaseOffsets();
+ calculateCurrentOffsets();
}
}
@@ -503,7 +545,7 @@
return Color.argb((int) a, (int) r, (int) g, (int) b);
}
- private static float interpolate(float startValue, float endValue, float fraction,
+ private static float lerp(float startValue, float endValue, float fraction,
Interpolator interpolator) {
if (interpolator != null) {
fraction = interpolator.getInterpolation(fraction);
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index 71611e6..7febedc 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -28,6 +28,7 @@
import android.support.annotation.Nullable;
import android.support.design.R;
import android.support.v4.content.ContextCompat;
+import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.WindowInsetsCompat;
import android.support.v7.widget.Toolbar;
@@ -122,13 +123,19 @@
super(context, attrs, defStyleAttr);
mCollapsingTextHelper = new CollapsingTextHelper(this);
- mCollapsingTextHelper.setExpandedTextVerticalGravity(Gravity.BOTTOM);
mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CollapsingToolbarLayout, defStyleAttr,
R.style.Widget_Design_CollapsingToolbar);
+ mCollapsingTextHelper.setExpandedTextGravity(
+ a.getInt(R.styleable.CollapsingToolbarLayout_expandedTitleGravity,
+ GravityCompat.START | Gravity.BOTTOM));
+ mCollapsingTextHelper.setCollapsedTextGravity(
+ a.getInt(R.styleable.CollapsingToolbarLayout_collapsedTitleGravity,
+ GravityCompat.START | Gravity.CENTER_VERTICAL));
+
mExpandedMarginLeft = mExpandedMarginTop = mExpandedMarginRight = mExpandedMarginBottom =
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMargin, 0);
@@ -344,9 +351,10 @@
mCollapsingTextHelper.setCollapsedBounds(mTmpRect.left, bottom - mTmpRect.height(),
mTmpRect.right, bottom);
// Update the expanded bounds
- mCollapsingTextHelper.setExpandedBounds(left + mExpandedMarginLeft,
- mTmpRect.bottom + mExpandedMarginTop, right - mExpandedMarginRight,
- bottom - mExpandedMarginBottom);
+ mCollapsingTextHelper.setExpandedBounds(
+ mExpandedMarginLeft, mTmpRect.bottom,
+ right - left - mExpandedMarginRight,
+ bottom - top - mExpandedMarginBottom);
mCollapsingTextHelper.recalculate();
}
@@ -566,6 +574,26 @@
}
/**
+ * Sets the horizontal alignment of the collapsed title and the vertical gravity that will
+ * be used when there is extra space in the collapsed bounds beyond what is required for
+ * the title itself.
+ *
+ * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
+ */
+ public void setCollapsedTitleGravity(int gravity) {
+ mCollapsingTextHelper.setExpandedTextGravity(gravity);
+ }
+
+ /**
+ * Returns the horizontal and vertical alignment for title when collapsed.
+ *
+ * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_collapsedTitleGravity
+ */
+ public int getCollapsedTitleGravity() {
+ return mCollapsingTextHelper.getCollapsedTextGravity();
+ }
+
+ /**
* Sets the text color and size for the expanded title from the specified
* TextAppearance resource.
*
@@ -585,6 +613,26 @@
}
/**
+ * Sets the horizontal alignment of the expanded title and the vertical gravity that will
+ * be used when there is extra space in the expanded bounds beyond what is required for
+ * the title itself.
+ *
+ * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
+ */
+ public void setExpandedTitleGravity(int gravity) {
+ mCollapsingTextHelper.setExpandedTextGravity(gravity);
+ }
+
+ /**
+ * Returns the horizontal and vertical alignment for title when expanded.
+ *
+ * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleGravity
+ */
+ public int getExpandedTitleGravity() {
+ return mCollapsingTextHelper.getExpandedTextGravity();
+ }
+
+ /**
* The additional offset used to define when to trigger the scrim visibility change.
*/
final int getScrimTriggerOffset() {
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 4332d10..2f492c7 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -113,7 +113,7 @@
mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator());
- mCollapsingTextHelper.setCollapsedTextVerticalGravity(Gravity.TOP);
+ mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP);
final TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.TextInputLayout, 0, R.style.Widget_Design_TextInputLayout);