package com.marshalchen.common.uimodule.matchview.util; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.PointF; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.animation.Transformation; import java.util.ArrayList; /** * Description:MatchBaseTextView * User: Lj * Date: 2014-12-03 * Time: 07:48 * FIXME */ public class MatchView extends View { // public ArrayList<MatchItem> mItemList = new ArrayList<MatchItem>(); private int mLineWidth; private float mScale = 1; private int mDropHeight; private float internalAnimationFactor = 0.7f; private int horizontalRandomness; private float mProgress = 0; private int mDrawZoneWidth = 0; private int mDrawZoneHeight = 0; private int mOffsetX = 0; private int mOffsetY = 0; private float mBarDarkAlpha = 0.4f; private float mFromAlpha = 1.0f; private float mToAlpha = 0.4f; private int mLoadingAniDuration = 1000; private int mLoadingAniSegDuration = 1000; private int mLoadingAniItemDuration = 400; private Transformation mTransformation = new Transformation(); private boolean mIsInLoading = false; private AniController mAniController = new AniController(); private int mTextColor = Color.WHITE; private Handler mHandler; private float progress = 0; private float mInTime = 0.8f;//划入时间(单位秒) private float mOutTime = 0.8f;//划出时间(单位秒) private boolean isBeginLight = true;//结束后是否播放动画(默认为是) private int mPaddingTop = 15;//滑动的高度 private float mTextSize = 25f; /** * 加载状态 1、划入 2、划出 */ private int STATE = 0; private MatchInListener mMatchInListener; private MatchOutListener mMatchOutListener; public void setMatchInListener(MatchInListener mMatchInListener) { this.mMatchInListener = mMatchInListener; } public void setMatchOutListener(MatchOutListener mMatchOutListener) { this.mMatchOutListener = mMatchOutListener; } public MatchView(Context context) { super(context); initView(); } public MatchView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public MatchView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } private void initView() { this.setLayerType(View.LAYER_TYPE_SOFTWARE, null); Utils.init(getContext()); mLineWidth = Utils.dp2px(1); mDropHeight = Utils.dp2px(40); horizontalRandomness = Utils.SCREEN_WIDTH_PIXELS / 2; setPadding(0, Utils.dp2px(mPaddingTop), 0, Utils.dp2px(mPaddingTop)); mHandler = new Handler() { @Override public void dispatchMessage(Message msg) { super.dispatchMessage(msg); if (STATE == 1) {//划入 if (progress < 100) { progress++; setProgress((progress * 1f / (100))); mHandler.sendEmptyMessageDelayed(0, (long) (mInTime * 10)); } else { STATE = 2; if (mMatchInListener != null) { mMatchInListener.onFinish(); } } } else if (STATE == 2) {//划出 if (mIsInLoading) { lightFinish(); } if (progress > 0) { progress--; setProgress((progress * 1f / (100))); mHandler.sendEmptyMessageDelayed(0, (long) (mOutTime * 10)); } else { progress = 0; if (mMatchOutListener != null) { mMatchOutListener.onFinish(); } STATE = 1; } } } }; } /** * 设置划入动画时长 * * @param mTime (单位秒) */ public void setInTime(float mTime) { mInTime = mTime; } /** * 设置划出动画时长 * * @param mTime (单位秒) */ public void setOutTime(float mTime) { mOutTime = mTime; } /** * 划入后是否开始闪光 * * @param isLight */ public void setLight(boolean isLight) { isBeginLight = isLight; } /** * 设置划入动画的高度 * * @param dp */ public void setPaddingTop(int dp) { mPaddingTop = dp; } /** * 设置字体大小 * * @param mTextSize */ public void setTextSize(float mTextSize) { this.mTextSize = mTextSize; } protected void show() { if (mItemList.size() == 0) { return; } STATE = 1; mHandler.sendEmptyMessage(0); if (mMatchInListener != null) { mMatchInListener.onBegin(); } } public void hide() { if (mMatchOutListener != null) { mMatchOutListener.onBegin(); } mHandler.sendEmptyMessage(0); } public void setProgress(float progress) { if (mMatchInListener != null && STATE == 1) { mMatchInListener.onProgressUpdate(progress); } else if (mMatchOutListener != null && STATE == 2) { mMatchOutListener.onProgressUpdate(progress); } if (progress == 1) { if (isBeginLight) { beginLight(); } } else if (mIsInLoading) { lightFinish(); } mProgress = progress; postInvalidate(); } public int getLoadingAniDuration() { return mLoadingAniDuration; } public void setLoadingAniDuration(int duration) { mLoadingAniDuration = duration; mLoadingAniSegDuration = duration; } public MatchView setLineWidth(int width) { mLineWidth = width; for (int i = 0; i < mItemList.size(); i++) { mItemList.get(i).setLineWidth(width); } return this; } public MatchView setTextColor(int color) { mTextColor = color; for (int i = 0; i < mItemList.size(); i++) { mItemList.get(i).setColor(color); } return this; } public MatchView setDropHeight(int height) { mDropHeight = height; return this; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = getTopOffset() + mDrawZoneHeight + getBottomOffset(); heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); mOffsetX = (getMeasuredWidth() - mDrawZoneWidth) / 2; mOffsetY = getTopOffset(); mDropHeight = getTopOffset(); } private int getTopOffset() { return getPaddingTop() + Utils.dp2px(10); } private int getBottomOffset() { return getPaddingBottom() + Utils.dp2px(10); } public void initWithString(String str) { initWithString(str, mTextSize); } public void initWithString(String str, float fontSize) { ArrayList<float[]> pointList = MatchPath.getPath(str, fontSize * 0.01f, 14); initWithPointList(pointList); } public void initWithStringArray(int id) { String[] points = getResources().getStringArray(id); ArrayList<float[]> pointList = new ArrayList<float[]>(); for (int i = 0; i < points.length; i++) { String[] x = points[i].split(","); float[] f = new float[4]; for (int j = 0; j < 4; j++) { f[j] = Float.parseFloat(x[j]); } pointList.add(f); } initWithPointList(pointList); } public float getScale() { return mScale; } public void setScale(float scale) { mScale = scale; } public void initWithPointList(ArrayList<float[]> pointList) { float drawWidth = 0; float drawHeight = 0; boolean shouldLayout = mItemList.size() > 0; mItemList.clear(); for (int i = 0; i < pointList.size(); i++) { float[] line = pointList.get(i); PointF startPoint = new PointF(Utils.dp2px(line[0]) * mScale, Utils.dp2px(line[1]) * mScale); PointF endPoint = new PointF(Utils.dp2px(line[2]) * mScale, Utils.dp2px(line[3]) * mScale); drawWidth = Math.max(drawWidth, startPoint.x); drawWidth = Math.max(drawWidth, endPoint.x); drawHeight = Math.max(drawHeight, startPoint.y); drawHeight = Math.max(drawHeight, endPoint.y); MatchItem item = new MatchItem(i, startPoint, endPoint, mTextColor, mLineWidth); item.resetPosition(horizontalRandomness); mItemList.add(item); } mDrawZoneWidth = (int) Math.ceil(drawWidth); mDrawZoneHeight = (int) Math.ceil(drawHeight); if (shouldLayout) { requestLayout(); } } public void beginLight() { mIsInLoading = true; mAniController.start(); invalidate(); } public void lightFinish() { mIsInLoading = false; mAniController.stop(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); float progress = mProgress; int c1 = canvas.save(); int len = mItemList.size(); for (int i = 0; i < mItemList.size(); i++) { canvas.save(); MatchItem LoadingViewItem = mItemList.get(i); float offsetX = mOffsetX + LoadingViewItem.midPoint.x; float offsetY = mOffsetY + LoadingViewItem.midPoint.y; if (mIsInLoading) { LoadingViewItem.getTransformation(getDrawingTime(), mTransformation); canvas.translate(offsetX, offsetY); } else { if (progress == 0) { LoadingViewItem.resetPosition(horizontalRandomness); continue; } float startPadding = (1 - internalAnimationFactor) * i / len; float endPadding = 1 - internalAnimationFactor - startPadding; // done if (progress == 1 || progress >= 1 - endPadding) { canvas.translate(offsetX, offsetY); LoadingViewItem.setAlpha(mBarDarkAlpha); } else { float realProgress; if (progress <= startPadding) { realProgress = 0; } else { realProgress = Math.min(1, (progress - startPadding) / internalAnimationFactor); } offsetX += LoadingViewItem.translationX * (1 - realProgress); offsetY += -mDropHeight * (1 - realProgress); Matrix matrix = new Matrix(); matrix.postRotate(360 * realProgress); matrix.postScale(realProgress, realProgress); matrix.postTranslate(offsetX, offsetY); LoadingViewItem.setAlpha(mBarDarkAlpha * realProgress); canvas.concat(matrix); } } LoadingViewItem.draw(canvas); canvas.restore(); } if (mIsInLoading) { invalidate(); } canvas.restoreToCount(c1); } private class AniController implements Runnable { private int mTick = 0; private int mCountPerSeg = 0; private int mSegCount = 0; private int mInterval = 0; private boolean mRunning = true; private void start() { mRunning = true; mTick = 0; mInterval = mLoadingAniDuration / mItemList.size(); mCountPerSeg = mLoadingAniSegDuration / mInterval; mSegCount = mItemList.size() / mCountPerSeg + 1; run(); } @Override public void run() { int pos = mTick % mCountPerSeg; for (int i = 0; i < mSegCount; i++) { int index = i * mCountPerSeg + pos; if (index > mTick) { continue; } index = index % mItemList.size(); MatchItem item = mItemList.get(index); item.setFillAfter(false); item.setFillEnabled(true); item.setFillBefore(false); item.setDuration(mLoadingAniItemDuration); item.start(mFromAlpha, mToAlpha); } mTick++; if (mRunning) { postDelayed(this, mInterval); } } private void stop() { mRunning = false; removeCallbacks(this); } } public interface MatchInListener { public void onBegin(); public void onProgressUpdate(float progress); public void onFinish(); } public interface MatchOutListener { public void onBegin(); public void onProgressUpdate(float progress); public void onFinish(); } }