package com.reactnativenavigation.views.collapsingToolbar; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.support.annotation.CheckResult; import android.support.annotation.FloatRange; import android.support.v4.widget.TextViewCompat; import android.support.v7.widget.TintTypedArray; import android.text.TextPaint; import android.text.TextUtils; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import com.reactnativenavigation.R; import com.reactnativenavigation.params.StyleParams; import com.reactnativenavigation.utils.ViewUtils; public class CollapsingTextView extends FrameLayout { private static final float TEXT_SCALE_FACTOR = 1.75f; private static final float INTERPOLATOR_EASING_FACTOR = 0.5f; private final int collapsedHeight; private TextView dummy; private CharSequence textToDraw; private CharSequence expendedTextToDraw; private String originalText; private TextPaint paint; private float initialY = -1; private float currentY; private float collapseY; private float collapseFraction; private Interpolator scaleInterpolator; private float expendedTextSize; private float collapsedTextSize; public CollapsingTextView(Context context, int collapsedHeight) { super(context); this.collapsedHeight = collapsedHeight; setWillNotDraw(false); createDummyTextView(context); createTextPaint(); scaleInterpolator = new DecelerateInterpolator(INTERPOLATOR_EASING_FACTOR); setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); } @SuppressLint("PrivateResource") private void createDummyTextView(Context context) { final TintTypedArray a = TintTypedArray.obtainStyledAttributes(getContext(), null, R.styleable.Toolbar, R.attr.toolbarStyle, 0); int titleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); a.recycle(); dummy = new TextView(context); dummy.setSingleLine(); dummy.setEllipsize(TextUtils.TruncateAt.END); TextViewCompat.setTextAppearance(dummy, titleTextAppearance); collapsedTextSize = dummy.getTextSize(); expendedTextSize = collapsedTextSize * TEXT_SCALE_FACTOR; dummy.setTextSize(ViewUtils.convertPixelToSp(expendedTextSize)); dummy.setVisibility(INVISIBLE); addView(dummy, new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); } private void createTextPaint() { paint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG); paint.setColor(Color.WHITE); paint.setTextSize(expendedTextSize); } public void setText(String text) { this.originalText = text; dummy.setText(text); } public void setTextColor(StyleParams params) { if (params.titleBarTitleColor.hasColor()) { paint.setColor(params.titleBarTitleColor.getColor()); } } public void collapseBy(float translation) { collapseFraction = calculateFraction(translation); currentY = linearInterpolation(initialY, collapseY, collapseFraction); invalidate(); } /* * A value of {@code 0.0} indicates that the layout is fully expanded. * A value of {@code 1.0} indicates that the layout is fully collapsed. */ @FloatRange(from = 0.0, to = 1.0) private float calculateFraction(float translation) { return Math.abs(translation / (getMeasuredHeight() - collapsedHeight)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (initialY == -1) { calculateTextPosition(canvas); } paint.setTextSize(linearInterpolation(expendedTextSize, collapsedTextSize, collapseFraction)); calculateTextToDraw(); canvas.drawText(textToDraw, 0, textToDraw.length(), 0, currentY, paint); } private void calculateTextToDraw() { if (currentY == collapseY) { textToDraw = calculateText(); } else { if (expendedTextToDraw == null) { expendedTextToDraw = calculateText(); } textToDraw = expendedTextToDraw; } } private void calculateTextPosition(Canvas canvas) { final int[] positionOnScreen = new int[2]; getLocationInWindow(positionOnScreen); currentY = initialY = positionOnScreen[1] + canvas.getHeight() - dummy.getMeasuredHeight(); float bottomMargin = ViewUtils.convertDpToPixel(10); collapseY = positionOnScreen[1] + bottomMargin; } private float linearInterpolation(float from, float to, float fraction) { fraction = scaleInterpolator.getInterpolation(fraction); return from + (fraction * (to - from)); } @CheckResult private String calculateText() { return (String) TextUtils.ellipsize(originalText, paint, getWidth(), TextUtils.TruncateAt.END); } }