一.实现在画布中连线的动画的效果,如图所示:
二.功能实现的初始想法:
1.要实现划线的动画的效果,在我所写得画布中有这样两个方法用于连线的拖动,思路就是从这里来的,通过沿直线方向的拖动就可以实现如图的划线动画的效果:
/** * 设置控件前一连线的移动 * * @param drawableMap * @param x * @param y * @param nextWidgetIndex */ public static void setProLineMove(HashMap<Integer, CDrawable> drawableMap, int x, int y, int nextWidgetIndex) { CDrawable cd = drawableMap.get(nextWidgetIndex); if (null != cd) { int xcoords = cd.getXcoords(); int ycoords = cd.getYcoords(); if (CanvsUtils.isActionWidgetTypeInstance(cd)) { int lineIndex = ((ActionWidget) cd).getNextLineIndex(); //获取保存在控件中的连线值 CanvasViewLigatureUtils.setLineFollowMove(drawableMap, x, y, xcoords, ycoords, lineIndex); } } } /** * 设置控件后一连线的移动 * * @param drawableMap * @param x * @param y * @param nextWidgetIndex */ public static void setNextLineMove(HashMap<Integer, CDrawable> drawableMap, int x, int y, int nextWidgetIndex) { CDrawable cd = drawableMap.get(nextWidgetIndex); if (null != cd) { int xcoords = cd.getXcoords(); int ycoords = cd.getYcoords(); if (CanvsUtils.isActionWidgetTypeInstance(cd)) { int lineIndex = ((ActionWidget) cd).getProLineIndex(); //获取保存在控件中的连线值 setLineFollowMove(drawableMap, xcoords, ycoords, x, y, lineIndex); } } }
/** * 设置坐标使得连线跟随着滑动变化 * * @param x * @param y * @param xcoords * @param ycoords * @param lineIndex */ public static void setLineFollowMove(HashMap<Integer, CDrawable> drawableMap, int x, int y, int xcoords, int ycoords, int lineIndex) { if (lineIndex < 0) { return; } CPath drawable = (CPath) drawableMap.get(lineIndex); if (drawable!=null){ drawable.setXcoords(xcoords); drawable.setYcoords(ycoords); drawable.setEndX(x); drawable.setEndY(y); } }
2.首要解决的就是获取到线段上点的集合:
/** * 从直线中获取点 * * @param startX * @param startY * @param endX * @param endY * @param span 获取点的跨度值,可用于连线动画的速度计算 * @return */ public static List<PointModle> getPointFromLine(int startX, int startY, int endX, int endY, int span) { List<PointModle> list = new ArrayList<>(); if (startX == endX) { int from = Math.min(startY, endY); int to = Math.max(startX, endY); for (int y = from; y <= to; y++) { list.add(new PointModle(startX, y)); } } else { double slope = ((double) (endY - startY)) / ((double) (endX - startX)); int step = (endX > startX) ? span : -span; double y = 0; if (step>0){ for (int x = startX; x < endX; x += step) { y = ((x - startX) * slope + startY); list.add(new PointModle(x, y)); } }else { for (int x = startX; x > endX; x += step) { y = ((x - startX) * slope + startY); list.add(new PointModle(x, y)); } } } return list; }
3.接下来就是用定时器来控制划线的速度(即是设置沿着直线方向的所要设置的X、Y的坐标):
/** * 实现直线随着直线方向进行移动 * * @param pointFromLine * @param key * @param startX * @param startY */ private void setLineAnim(List<PointModle> pointFromLine, int key, int startX, int startY, int endX, int endY) { if (pointFromLine.size() < 0) { return; } if ((pointAmount < pointFromLine.size()) && (pointFromLine.size() > 0)) { Log.i("everb","划线中"); CanvasViewLigatureUtils.setLineFollowMove(drawableMap, startX, startY, (int) pointFromLine.get(pointAmount).getX(), (int) pointFromLine.get(pointAmount).getY(), key); } else { CanvasViewLigatureUtils.setLineFollowMove(drawableMap, startX, startY, endX, endY, key); mLink++; ((ActionWidget) drawableMap.get(mActionFrame2Linked.get(mActionFrame))).setPaint(redPaint); mActionFrame++; mHandler.sendEmptyMessageAtTime(NEXT_LINK_ANIM, 0); } }4.这里在画一条线段时用定时器去设置,等一条线的动画完成之后。用Handler去通知下一条线段动画的开始。这样周而复始就可以实现如上GIT图的连线动画的效果:
mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // Log.i("everb", "handler的值:" + msg.what); switch (msg.what) { case NEXT_LINK_ANIM: { pointAmount = 0; if (mLink < mSegment2Linked.size()) { motorPreviewDrawProgress(50, Color.BLACK, 20f); } else { //划线动画结束的操作 setActionFrameAlpha(false); motorPreviewDrawProgressLineClean(); mTimer.cancel(); } } break; } } };
三.这个功能是基于我之前写的有关画布的基础上实现,下面贴出连线动画的帮助类的代码:
package com.example.administrator.canvasdemo.helper; import android.content.Context; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.Message; import android.util.Log; import com.example.administrator.canvasdemo.MainActivity; import com.example.administrator.canvasdemo.modle.PointModle; import com.example.administrator.canvasdemo.utils.CanvasViewLigatureUtils; import com.example.administrator.canvasdemo.view.ActionCanvasView; import com.example.administrator.canvasdemo.view.ActionWidget; import com.example.administrator.canvasdemo.view.CDrawable; import com.example.administrator.canvasdemo.view.CPath; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; /** * @author wangyao * @package com.example.administrator.canvasdemo.helper * @date 2017/12/9 18:50 * @describe TODO * @project */ public class LineToAnimHelper { private List<Integer> mPreviewLineList; private TimerTask mTimerTask; private Timer mTimer; private Context mContext; private int mScreenWidth; private int pointAmount; //线段上点的数量 private HashMap<Integer, CDrawable> drawableMap; private ActionCanvasView mActionCanvasView; private int mKey; //标记index的变化 private List<Integer> mActionFrame2Linked; private List<CPath> mSegment2Linked; private int mLink; //当前正在划线的index private int mActionFrame = 1; //连线的关键帧集合中的index private Handler mHandler; private List<PointModle> mPointFromLine; //获取线段的点的集合 private static final int NEXT_LINK_ANIM = -1; private boolean isActionFrameAnimStop = false; private Paint redPaint; private Paint bluePaint; public LineToAnimHelper(Context context, HashMap<Integer, CDrawable> drawableMap, ActionCanvasView actionCanvasView, int key) { mTimer = new Timer(); mPreviewLineList = new ArrayList<>(); this.mContext = context; this.drawableMap = drawableMap; this.mActionCanvasView = actionCanvasView; this.mKey = key; mPointFromLine = new ArrayList<>(); redPaint=new Paint(); bluePaint=new Paint(); } /** * 执行的划线动画 */ public void executeLineToAnim() { redPaint.setColor(Color.RED); bluePaint.setColor(Color.BLUE); mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); // Log.i("everb", "handler的值:" + msg.what); switch (msg.what) { case NEXT_LINK_ANIM: { pointAmount = 0; if (mLink < mSegment2Linked.size()) { motorPreviewDrawProgress(50, Color.BLACK, 20f); } else { //划线动画结束的操作 setActionFrameAlpha(false); motorPreviewDrawProgressLineClean(); mTimer.cancel(); } } break; } } }; if (drawableMap.size() < 0 ) { return; } mActionFrame2Linked = getActionFrame2Linked(); mSegment2Linked = getSegment2Linked(); setActionFrameAlpha(true); motorPreviewDrawProgress(50, Color.BLACK, 20f); mTimer.schedule(mTimerTask, 0, 100); } /** * 退出预览的划线动画 */ public void motorPreviewQuit() { mTimer.cancel(); setActionFrameAlpha(false); recoveryActionFrameBitmap(); motorPreviewDrawProgressLineClean(); } /** * 获取连上线的线段 * * @return */ private List<CPath> getSegment2Linked() { List<CPath> segment2LinkedList = new ArrayList<>(); int searchIndex = 0; ActionWidget fristActionFrame = (ActionWidget) drawableMap.get(0); if (fristActionFrame.getNextLineIndex() < 0) { return null; } segment2LinkedList.add((CPath) drawableMap.get(fristActionFrame.getNextLineIndex())); searchIndex = fristActionFrame.getNextWidget(); while (searchIndex > 0) { int nextLineIndex = ((ActionWidget) drawableMap.get(searchIndex)).getNextLineIndex(); if (nextLineIndex > 0) { segment2LinkedList.add((CPath) drawableMap.get(nextLineIndex)); } searchIndex = ((ActionWidget) drawableMap.get(searchIndex)).getNextWidget(); } return segment2LinkedList; } /** * 设置未连接上关键帧的透明度 * * @param isSetAlpha */ private void setActionFrameAlpha(boolean isSetAlpha) { for (Map.Entry<Integer, CDrawable> entry : drawableMap.entrySet()) { if (entry.getValue() instanceof ActionWidget) { ActionWidget widget = (ActionWidget) entry.getValue(); if ((widget.getNextLineIndex() < 0) && (widget.getProLineIndex() < 0)) { if (isSetAlpha) { setActionFrameAlpha(widget); } else { recoveryActionFrameAlpha(widget); } } } } } /** * 获取连上线控件的Index值及顺序 * * @return */ private List<Integer> getActionFrame2Linked() { List<Integer> actionFrame2LinedList = new ArrayList<>(); int searchIndex = 0; ActionWidget fristActionFrame = (ActionWidget) drawableMap.get(0); if (fristActionFrame.getNextLineIndex() < 0) { return null; } actionFrame2LinedList.add(searchIndex); searchIndex = fristActionFrame.getNextWidget(); while (searchIndex > 0) { actionFrame2LinedList.add(searchIndex); searchIndex = ((ActionWidget) drawableMap.get(searchIndex)).getNextWidget(); } return actionFrame2LinedList; } /** * 清除预览模式的划线 */ private void motorPreviewDrawProgressLineClean() { for (int i = 0; i < mPreviewLineList.size(); i++) { drawableMap.remove(mPreviewLineList.get(i)); } mActionCanvasView.invalidate(); } /** * 传入两点的坐标在画布中进行划线动画 * * @param time 两帧运行时间 * @param color * @param width */ private void motorPreviewDrawProgress(int time, int color, float width) { mPointFromLine.clear(); // 定时器的间隔时间可以计算出传入的点的跨度值从而控制动画的速度 mPointFromLine = CanvasViewLigatureUtils.getPointFromLine(mSegment2Linked.get(mLink).getXcoords(), mSegment2Linked.get(mLink).getYcoords(), mSegment2Linked.get(mLink).getEndX(), mSegment2Linked.get(mLink).getEndY(), 5); mKey++; mPreviewLineList.add(mKey); CanvasViewLigatureUtils.setLineTo(mActionCanvasView, mKey, 0, 0, 0, 0, color, width); mTimerTask = new TimerTask() { @Override public void run() { setLineAnim(mPointFromLine, mKey, mSegment2Linked.get(mLink).getXcoords(), mSegment2Linked.get(mLink).getYcoords(), mSegment2Linked.get(mLink).getEndX(), mSegment2Linked.get(mLink).getEndY()); if (!isActionFrameAnimStop) { pointAmount++; } mActionCanvasView.post(new Runnable() { @Override public void run() { mActionCanvasView.invalidate(); } }); } }; } /** * 实现直线随着直线方向进行移动 * * @param pointFromLine * @param key * @param startX * @param startY */ private void setLineAnim(List<PointModle> pointFromLine, int key, int startX, int startY, int endX, int endY) { if (pointFromLine.size() < 0) { return; } if ((pointAmount < pointFromLine.size()) && (pointFromLine.size() > 0)) { Log.i("everb","划线中"); CanvasViewLigatureUtils.setLineFollowMove(drawableMap, startX, startY, (int) pointFromLine.get(pointAmount).getX(), (int) pointFromLine.get(pointAmount).getY(), key); } else { CanvasViewLigatureUtils.setLineFollowMove(drawableMap, startX, startY, endX, endY, key); mLink++; ((ActionWidget) drawableMap.get(mActionFrame2Linked.get(mActionFrame))).setPaint(redPaint); mActionFrame++; mHandler.sendEmptyMessageAtTime(NEXT_LINK_ANIM, 0); } } /** * 获取两点的距离 * * @param pointModle1 * @param pointModle2 * @return */ private double getDistance(PointModle pointModle1, PointModle pointModle2) { double x = Math.abs(pointModle1.getX() - pointModle2.getY()); double y = Math.abs(pointModle1.getY() - pointModle2.getY()); return Math.sqrt(x * x + y * y); } /** * 设置控件透明度 * * @param keyFrameWidget */ private void setActionFrameAlpha(ActionWidget keyFrameWidget) { Paint paint = new Paint(); paint.setAlpha(90); keyFrameWidget.setPaint(paint); } /** * 恢复控件透明度 * * @param keyFrameWidget */ private void recoveryActionFrameAlpha(ActionWidget keyFrameWidget) { Paint paint = new Paint(); paint.setAlpha(255); keyFrameWidget.setPaint(paint); } /** * 恢复划线动画之前的图片原样 */ private void recoveryActionFrameBitmap() { ((ActionWidget) drawableMap.get(0)).setPaint(bluePaint); for (int i = 1; i < mActionFrame2Linked.size(); i++) { ((ActionWidget) drawableMap.get(mActionFrame2Linked.get(i))).setPaint(bluePaint); } } /** * 控制划线动画的停止与继续 * * @param isAnimStop */ public void motorPreViewCtrl(boolean isAnimStop) { isActionFrameAnimStop = isAnimStop; if (isActionFrameAnimStop) { // 划线动画暂停 } } }