package com.ufreedom.uikit; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PathMeasure; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.FrameLayout; import android.widget.TextView; /** * UFreedom */ public class FloatingTextView extends TextView { private static final String TAG = "FloatingTextView"; private static final int PATH_WIDTH = 2; private FloatingText.FloatingTextBuilder floatingTextBuilder; private Paint mTextPaint; private Paint mPathPaint; private PathMeasure mPathMeasure; private View mAttachedView; private boolean isMeasured; private boolean positionSet; public FloatingTextView(Context context) { this(context, null); } public FloatingTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public FloatingTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setStyle(Paint.Style.FILL); mTextPaint.setTextSize(100); mTextPaint.setColor(Color.RED); mPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPathPaint.setStyle(Paint.Style.STROKE); mPathPaint.setStrokeWidth(PATH_WIDTH); mPathPaint.setColor(Color.RED); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (floatingTextBuilder == null) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); return; } int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int widthPaddingOffset = getPaddingLeft() + getPaddingRight(); if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { float maxWidth = getDesireWidth(mTextPaint); Paint.FontMetricsInt fmi = mTextPaint.getFontMetricsInt(); setMeasuredDimension((int) maxWidth + widthPaddingOffset, fmi.bottom - fmi.top); } else if (widthMode == MeasureSpec.AT_MOST) { float maxWidth = getDesireWidth(mTextPaint); setMeasuredDimension((int) maxWidth + widthPaddingOffset, heightSize); } else if (heightMode == MeasureSpec.AT_MOST) { Paint.FontMetricsInt fmi = mTextPaint.getFontMetricsInt(); setMeasuredDimension(widthSize + widthPaddingOffset, fmi.bottom - fmi.top); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } fixPosition(); isMeasured = true; } private float getDesireWidth(Paint mTextPaint) { return mTextPaint.measureText(floatingTextBuilder.getTextContent()); } public void setFloatingTextBuilder(FloatingText.FloatingTextBuilder floatingTextBuilder) { this.floatingTextBuilder = floatingTextBuilder; initTextStyle(); } public void flyText(View view) { mAttachedView = view; Log.i(TAG, "flyText: called"); if(isMeasured){ fixPosition(); } else { // wait on measure getViewTreeObserver() .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Log.i(TAG, "onGlobalLayout: called"); if (Build.VERSION.SDK_INT < 16) { FloatingTextView.this.getViewTreeObserver().removeGlobalOnLayoutListener(this); } else { FloatingTextView.this.getViewTreeObserver().removeOnGlobalLayoutListener(this); } fixPosition(); } }); } FloatingPathEffect floatingPathEffect = floatingTextBuilder.getFloatingPathEffect(); if (floatingPathEffect != null) { FloatingPath floatingPath = floatingPathEffect.getFloatingPath(FloatingTextView.this); mPathMeasure = floatingPath.getPathMeasure(); } FloatingAnimator floatingAnimator = floatingTextBuilder.getFloatingAnimator(); floatingAnimator.applyFloatingAnimation(FloatingTextView.this); } private void fixPosition() { if(mAttachedView == null) return; if(!positionSet) { Rect rect = new Rect(); mAttachedView.getGlobalVisibleRect(rect); int[] location = new int[2]; ((ViewGroup) getParent()).getLocationOnScreen(location); rect.offset(-location[0], -location[1]); int topMargin = rect.top + ((mAttachedView.getHeight() - getMeasuredHeight()) / 2) + floatingTextBuilder.getOffsetY(); int leftMargin = rect.left + ((mAttachedView.getWidth() - getMeasuredWidth()) / 2) + floatingTextBuilder.getOffsetX(); FrameLayout.LayoutParams lp = ((FrameLayout.LayoutParams) getLayoutParams()); lp.topMargin = topMargin; lp.leftMargin = leftMargin; setLayoutParams(lp); Log.i(TAG, "flyText: width " + mAttachedView.getWidth() + " getMeasuredWidth: " + getMeasuredWidth() + " x: " + leftMargin); Log.i(TAG, "flyText: height " + mAttachedView.getHeight() + " getMeasuredHeight: " + getMeasuredHeight() + " y: " + topMargin); } positionSet = true; } private void initTextStyle() { mTextPaint.setTextSize(floatingTextBuilder.getTextSize()); mTextPaint.setColor(floatingTextBuilder.getTextColor()); } public View getAttachedView() { return mAttachedView; } public PathMeasure getPathMeasure() { return mPathMeasure; } public void dettachFromWindow() { ViewGroup viewParent = (ViewGroup) getParent(); viewParent.removeView(this); } @Override public void draw(Canvas canvas) { if (!isMeasured || !positionSet) return; Log.i(TAG, "draw: isMeasured:" + isMeasured + " positionSet:" + positionSet); super.draw(canvas); } @Override protected void onDraw(Canvas canvas) { if (!isMeasured || !positionSet) return; super.onDraw(canvas); if (floatingTextBuilder == null || mAttachedView == null) { return; } float x = (float) (getWidth() / 2.0); float y = (float) (getHeight() / 2.0); Paint.FontMetricsInt fmi = mTextPaint.getFontMetricsInt(); float baseline = (float) (y - (fmi.bottom / 2.0 + fmi.top / 2.0)); canvas.drawText(floatingTextBuilder.getTextContent(), x, baseline, mTextPaint); } }