一、需求:

1.在画布中的控件A长按能进行控件的连接,只有在控件B范围内抬起控件之间的连线才能连接成功;

2.当控件连线成功后,拖动控件AB之间的连线随着控件的拖动随之变化;

3.控件连线在屏幕上随着手势滑动触碰到连接则断开连线;

4.控件拖动时如拖动到其他控件的范围位置之内则弹回原来的位置且控件的连线也恢复原来的位置(控件在屏幕上不能重叠);

5.控件放置时如有控件之间的连线相交则也恢复原来的位置(在屏幕上控件间的连线不能相交);

6.控件拖动到连线上也会弹回;

7.进行控件的连续连接。

二、实现思想:

1.画布中的其他功能在我之前的博文中已经实现了,可以参考我的另外两篇博客:Android中自定义画布Canvas的实现和Android自定义万能Camvas画布;

2.之前的画布中已经实现了画布的长按接口的调用,在控件内抬起事件的接口调用。这样通过判断这两个事件可以获取到要连线的两点的位置坐标,而在之前画布中所有的控件都是以一个bean类添加到画布中的,所以在两个控件间的连线就可以实现;

3.之前的画布中也实现了拖动的监听事件,在这监听事件中能获取控件的拖动的实时坐标值。这样就可以实时对连线的两点的坐标进行更改,此步可以实现:当控件连线成功后,拖动控件AB之间的连线随着控件的拖动随之变化;

4.控件的连线在画布中是以集合类去存储的,在画布中也设置了控件之外触摸事件的监听接口所以能实时获取到滑动的坐标值这样通过实时的前一个点和后一个点就获取一个线段值更屏幕上的连接进行相交的比对就能判断滑动是否触碰到连接。经过判断是否触碰到控件之间的连线就可以进行断开控件的操作;

5.设计控件的类时,要考虑到所存有的属性有:跟上一控件连接的ID值、跟下一控件连线的ID值、上一连线的ID值、下一连线的ID值;

6.在判断控件拖动放置的位置时,要在集合中遍历控件是否拖动之后抬起的位置是否处于其他控件在画布中的坐标位置。拖动时前按下先保存整个画布中所有关于控件的位置信息(包括控件和连线)这跟拖动控件抬起后的坐标值进行遍历判断该拖动的控件是否处于未拖动时其他控件位置范围内,如在这个范围内则把该控件位置和其连线的位置设回初始状态;

7.当控件在画布中滑动时则把移动到其他位置点记录起来,如手势抬起时不再控件的内部范围内则把这记录删除,如手势抬起时在控件内部则按顺序划线,实现多控件的连线。

三、效果展示:

四、具体实现:

1.实现两个控件的连线:

1).控件内的长按监听,记录控件属性的值:

 mCanvasConnection.setOnWidgetLongPressListener(new ActionCanvasView.onWidgetLongPressListener() {@Overridepublic void onWidgetLongPress(int index, int x, int y) {//  如长按的话控件可进行连线模式if (index < 0) {return;}Toast.makeText(MainActivity.this, "进入划线模式:" + index, Toast.LENGTH_SHORT).show();if (isActionFrameWidgetTypeInstance(drawableMap.get(index))) {mCanvasConnection.setDragMode(false);mProWidget = index;      //记录长按下去控件的值mDownXcoords = drawableMap.get(index).getXcoords();mDownYcoords = drawableMap.get(index).getYcoords();if (((ActionWidget) drawableMap.get(index)).getNextWidget() > -1) {Toast.makeText(MainActivity.this, "该控件已经链接满不能连接下一个控件了" + index, Toast.LENGTH_SHORT).show();}if (((ActionWidget) drawableMap.get(index)).getProLineIndex() > -1) {int proLineIndex = ((ActionWidget) drawableMap.get(index)).getProLineIndex();mPath = (CPath) drawableMap.get(proLineIndex);mXcoords = mPath.getXcoords();mYcoords = mPath.getYcoords();mEndX = mPath.getEndX();mEndY = mPath.getEndY();isChangeCoords = true;}}}});

2).如控件拖动之后抬起在其他可连线控件的范围内则进行连线的添加:

 mCanvasConnection.setOnWidgetUpListener(new ActionCanvasView.onWidgetUpListener() {@Overridepublic void onWidgetUp(int index, int x, int y) {//  当长按之后进入划线,移动到另一个坐标抬起则进行取两点进行划线if (!mCanvasConnection.isDragWidgetMode()) {if (index > -1) {//获取抬起时触摸点落在控件的坐标范围if (isActionFrameWidgetTypeInstance(drawableMap.get(index))) {mUpXcoords = drawableMap.get(index).getXcoords();mUpYcoords = drawableMap.get(index).getYcoords();//如抬起时是在长按的控件内则不划线if (!((mDownXcoords == mUpXcoords) && (mDownYcoords == mDownYcoords))) {if (((ActionWidget) drawableMap.get(index)).getProWidget() > -1) {Toast.makeText(MainActivity.this, "已有前一连接:" + index, Toast.LENGTH_SHORT).show();return;}if (isActionFrameWidgetTypeInstance(drawableMap.get(mProWidget))) {((ActionWidget) drawableMap.get(mProWidget)).setNextWidget(index);//设置长按控件的后连接值} else {return;}((ActionWidget) drawableMap.get(index)).setProWidget(mProWidget);//当前控件的前连接值setLineTo(index, mDownXcoords, mDownYcoords, mUpXcoords, mUpYcoords);}}}mCanvasConnection.setDragMode(true);} else {//控件拖拽到其他控件的的为则弹回if (getDown2Widget(x, y, mDrawableMapClone) > -1) {(drawableMap.get(cloneIndex)).setXcoords(mXcoordsClone);(drawableMap.get(cloneIndex)).setYcoords(mYcoordsClone);if (mProLineIndex > -1) {((CPath) drawableMap.get(mProLineIndex)).setEndX(mEndXClone);((CPath) drawableMap.get(mProLineIndex)).setEndY(mEndYClone);}if (mNextLineIndex > -1) {drawableMap.get(mNextLineIndex).setXcoords(mLineStartX);drawableMap.get(mNextLineIndex).setYcoords(mLineStartY);}mCanvasConnection.invalidate();Toast.makeText(MainActivity.this, "已经拖动到原控件中" + cloneIndex, Toast.LENGTH_SHORT).show();}}}});

2.当控件连线成功后,在拖动的控件的监听中获取坐标值把控件间的连线的坐标值进行设置,实现拖动控件之间的连线随着控件的拖动随之变化;

 mCanvasConnection.setOnWidgetMoveListener(new ActionCanvasView.onWidgetMoveListener() {@Overridepublic void onWidgetMove(int index, int x, int y) {//  当控件拖动时控件间的连线也随之变化if ((index > -1) && isActionFrameWidgetTypeInstance(drawableMap.get(index))) {ActionWidget currentFrameWidget = (ActionWidget) drawableMap.get(index);int proWidgetIndex = currentFrameWidget.getProWidget();int nextWidgetIndex = currentFrameWidget.getNextWidget();//控件没有前连接if (proWidgetIndex < 0) {//控件没有后连接if (nextWidgetIndex < 0) {return;} else {//只有后连接,则获取后连接控件setNextLineMove(x, y, nextWidgetIndex);}} else {                //控件有前连接if (((ActionWidget) drawableMap.get(index)).getNextWidget() < 0) {//只有前连接setProLineMove(x, y, proWidgetIndex);} else {//前后连接都有setNextLineMove(x, y, nextWidgetIndex);setProLineMove(x, y, proWidgetIndex);}}}}});

3.在控件之外监接口中获取滑动的坐标值,取滑动的相连两点成一条线段跟连线进行相交判断:

1).判断两线段相交判断的类:

a.计算两直线相交的SementsIntersect类:

package com.example.administrator.canvasdemo;/*** @author wangyao* @package* @date 2017/7/20  17:03* @describe TODO* @project*/public class SegmentsIntersect {private PointModle p1;private PointModle p2;private PointModle p3;private PointModle p4;public SegmentsIntersect(PointModle p1, PointModle p2, PointModle p3, PointModle p4) {super();this.p1 = p1;this.p2 = p2;this.p3 = p3;this.p4 = p4;}public double det(PointModle pi, PointModle pj) { // 叉积return pi.getX() * pj.getY() - pj.getX() * pi.getY();}public PointModle PiPj(PointModle pi, PointModle pj) { // 構造向量PointModle p = new PointModle();p.setX(pj.getX() - pi.getX());p.setY(pj.getY() - pi.getY());return p;}public double Direction(PointModle pi, PointModle pj, PointModle pk) { // 大於零表示順時針,即右轉,小於零表示逆時針,即左轉,等於零,表示共綫。return det(PiPj(pk, pi), PiPj(pj, pi));}public boolean On_Segment(PointModle pi, PointModle pj, PointModle pk) {double max_x = (pi.getX() - pj.getX()) > 0 ? pi.getX() : pj.getX();double min_x = (pi.getX() - pj.getX()) < 0 ? pi.getX() : pj.getX();double max_y = (pi.getY() - pj.getY()) > 0 ? pi.getY() : pj.getY();double min_y = (pi.getY() - pj.getY()) < 0 ? pi.getY() : pj.getY();if ((min_x <= pk.getX()) && (pk.getX() <= max_x)&& (min_y <= pk.getY()) && (pk.getY() <= max_y))return true;elsereturn false;}public boolean SegmentsIntersect() {double d1 = Direction(this.p3, this.p4, this.p1);double d2 = Direction(p3, p4, p2);double d3 = Direction(p1, p2, p3);double d4 = Direction(p1, p2, p4);if (((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0))&& ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)))return true;else if (d1 == 0 && On_Segment(p3, p4, p1))return true;else if (d2 == 0 && On_Segment(p3, p4, p2))return true;else if (d3 == 0 && On_Segment(p1, p2, p3))return true;else if (d4 == 0 && On_Segment(p1, p2, p4))return true;elsereturn false;}}

b.用于存点的PointModel类:

package com.example.administrator.canvasdemo;/*** @author wangyao* @package* @date 2017/7/20  17:05* @describe TODO* @project*/public class PointModle {private double x;private double y;public PointModle() {super();}public PointModle(double x, double y) {super();this.x = x;this.y = y;}public double getX() {return x;}public void setX(double x) {this.x = x;}public double getY() {return y;}public void setY(double y) {this.y = y;}}

2).在控件之外监接口中判断两直线相交判断,之后把连线断开并把连线在控件之间的属性值置为-1:

控件连线在屏幕上随着手势滑动触碰到连接则断开连线;

mCanvasConnection.setOnOutOfWidgetMoveListener(new ActionCanvasView.onOutOfWidgetMoveListener() {@Overridepublic void onOutOfWidgetMove(int x, int y) {
//                Toast.makeText(ConnectionSegmentActivity.this, "手势滑动不是在控件中X轴为:" + x+"Y轴为:"+y, Toast.LENGTH_SHORT).show();//  根据手势滑到的坐标位置判断,手势是否与连线产生碰撞int nextPonitCoordsX = x;int nextPonitCoordsY = y;PointModle pointModle3 = new PointModle();pointModle3.setX((double) mProPonitCoordsX);pointModle3.setY((double) mProPonitCoordsY);PointModle pointModle4 = new PointModle();pointModle4.setX((double) nextPonitCoordsX);pointModle4.setY((double) nextPonitCoordsY);// TODO: 2017/7/20 获取连线的两点横纵坐标值Iterator iterator = drawableMap.keySet().iterator();while (iterator.hasNext()) {int next = (int) iterator.next();if (drawableMap.get(next) instanceof CPath) {int xcoords = (drawableMap.get(next)).getXcoords();int ycoords = (drawableMap.get(next)).getYcoords();int endX = ((CPath) drawableMap.get(next)).getEndX();int endY = ((CPath) drawableMap.get(next)).getEndY();PointModle pointModle1 = new PointModle();pointModle1.setX((double) xcoords);pointModle1.setY((double) ycoords);PointModle pointModle2 = new PointModle();pointModle2.setX((double) endX);pointModle2.setY((double) endY);mSegmentsIntersect = new SegmentsIntersect(pointModle1, pointModle2, pointModle3, pointModle4);if (mSegmentsIntersect.SegmentsIntersect()) {Toast.makeText(MainActivity.this, "产生碰撞:", Toast.LENGTH_SHORT).show();// TODO: 2017/7/20 删除连线并把控件中存在的关系解除iterator.remove();Iterator iterator1 = drawableMap.keySet().iterator();while (iterator1.hasNext()) {int next1 = (int) iterator1.next();if (isActionFrameWidgetTypeInstance(drawableMap.get(next1))) {if (((ActionWidget) drawableMap.get(next1)).getProLineIndex() == next) {((ActionWidget) drawableMap.get(next1)).setProLineIndex(-1);((ActionWidget) drawableMap.get(next1)).setProWidget(-1);}if (((ActionWidget) drawableMap.get(next1)).getNextLineIndex() == next) {((ActionWidget) drawableMap.get(next1)).setNextLineIndex(-1);((ActionWidget) drawableMap.get(next1)).setNextWidget(-1);}}}}mCanvasConnection.invalidate();}}}});

4.在控件down下去的监听事件中clone画布中的储存控件信息的集合,之后在控件弹起的事件监听中判断是否是处在原来画布中控件信息集合中的控件位置范围:

1).clone画布储存控件信息的集合:

 mCanvasConnection.setOnWidgetDownListener(new ActionCanvasView.onWidgetDownListener() {@Overridepublic void onWidgetDown(int index, int x, int y) {mDrawableMapClone.clear();cloneIndex = index;mDrawableMapClone.putAll(drawableMap);mXcoordsClone = drawableMap.get(index).getXcoords();mYcoordsClone = drawableMap.get(index).getYcoords();if (isActionFrameWidgetTypeInstance(drawableMap.get(index))) {mProLineIndex = ((ActionWidget) drawableMap.get(index)).getProLineIndex();if (mProLineIndex > -1) {mEndXClone = ((CPath) drawableMap.get(mProLineIndex)).getEndX();mEndYClone = ((CPath) drawableMap.get(mProLineIndex)).getEndY();}mNextLineIndex = ((ActionWidget) drawableMap.get(index)).getNextLineIndex();if (mNextLineIndex > -1) {mLineStartX = (drawableMap.get(mNextLineIndex)).getXcoords();mLineStartY = (drawableMap.get(mNextLineIndex)).getYcoords();}}mDrawableMapClone.remove(index);}});

2).在控件内部抬起事件监听中判断拖动的控件是否落在初始画布储存的控件范围之内、及拖动后两连线是否相交、控件拖动到连线之上:

//控件拖拽到其他控件的位置则弹回if (getDown2Widget(x, y, mDrawableMapClone) > -1) {Toast.makeText(MainActivity.this, "两控件相交!" + cloneIndex, Toast.LENGTH_SHORT).show();recoveryPosition();}//  如果连线与连线相交if (isLineToSegmentsIntersect(drawableMap,index,x,y)){Toast.makeText(MainActivity.this, "两线相交!" + cloneIndex, Toast.LENGTH_SHORT).show();recoveryPosition();}// TODO: 2017/7/21 拖动的控件在连接线上if (isWidgetIntoLine(drawableMap,index,x,y)){Toast.makeText(MainActivity.this, "控件在直线上!" + cloneIndex, Toast.LENGTH_SHORT).show();recoveryPosition();}private boolean isWidgetIntoLine(Map<Integer, CDrawable> map,int index,int x,int y){Iterator iterator=map.keySet().iterator();int proLineIndex = -1;int nextLineIndex = -1;if (isActionFrameWidgetTypeInstance(map.get(index))){ActionWidget drawable = (ActionWidget) map.get(index);proLineIndex=drawable.getProLineIndex();nextLineIndex=drawable.getNextLineIndex();}while (iterator.hasNext()) {int next = (int) iterator.next();if (map.get(next) instanceof CPath){if ((nextLineIndex==next)||(proLineIndex==next)){continue;}int xcoords = (map.get(next)).getXcoords();int ycoords = (map.get(next)).getYcoords();int endX = ((CPath) map.get(next)).getEndX();int endY = ((CPath) map.get(next)).getEndY();if (pointToLine(xcoords,ycoords,endX,endY,x,y)< ActionWidget.RADIUS*1.1){return true;}}}return false;}/*** 点到直线的最短距离的判断 点(x0,y0) 到由两点组成的线段(x1,y1) ,( x2,y2 )* @param x1* @param y1* @param x2* @param y2* @param x0* @param y0* @return*/private double pointToLine(int x1, int y1, int x2, int y2, int x0,int y0) {double space = 0;double a, b, c;a = lineSpace(x1, y1, x2, y2);// 线段的长度b = lineSpace(x1, y1, x0, y0);// (x1,y1)到点的距离c = lineSpace(x2, y2, x0, y0);// (x2,y2)到点的距离if (c <= 0.000001 || b <= 0.000001) {space = 0;return space;}if (a <= 0.000001) {space = b;return space;}if (c * c >= a * a + b * b) {space = b;return space;}if (b * b >= a * a + c * c) {space = c;return space;}double p = (a + b + c) / 2;// 半周长double s = Math.sqrt(p * (p - a) * (p - b) * (p - c));// 海伦公式求面积space = 2 * s / a;// 返回点到线的距离(利用三角形面积公式求高)return space;}// 计算两点之间的距离private double lineSpace(int x1, int y1, int x2, int y2) {double lineLength = 0;lineLength = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2)* (y1 - y2));return lineLength;}/*** 判断map中连线是否相交* @param map* @param index* @return*/private boolean isLineToSegmentsIntersect(Map<Integer, CDrawable> map,int index,int x,int y){if (isActionFrameWidgetTypeInstance(map.get(index))){ActionWidget actionFrameWidget = (ActionWidget) map.get(index);int proLineIndex = actionFrameWidget.getProLineIndex();int nextLineIndex = actionFrameWidget.getNextLineIndex();if (proLineIndex>-1){//控件的前一连线值return isWidgetLineSegmentIntersect(map, proLineIndex,x,y,false);}if (nextLineIndex>-1){return isWidgetLineSegmentIntersect(map, nextLineIndex,x,y,true);}}return false;}/*** 判断控件中的连线是否与画布中其他的连线相交*/@Nullableprivate Boolean isWidgetLineSegmentIntersect(Map<Integer, CDrawable> map,  int nextLineIndex,int x,int y,boolean isNext) {CPath cPath = (CPath) map.get(nextLineIndex);PointModle pointModle3;PointModle pointModle4;if (isNext){pointModle3=new PointModle(x,y);pointModle4=new PointModle(cPath.getEndX(),cPath.getEndY());}else {pointModle3=new PointModle(cPath.getXcoords(),cPath.getYcoords());pointModle4=new PointModle(x,y);}Iterator iterator=map.keySet().iterator();while (iterator.hasNext()) {int next = (int) iterator.next();if (nextLineIndex==next){continue;}if (map.get(next) instanceof CPath){int xcoords = (map.get(next)).getXcoords();int ycoords = (map.get(next)).getYcoords();PointModle pointModle1=new PointModle();pointModle1.setX((double) xcoords);pointModle1.setY((double)ycoords);int endX = ((CPath) map.get(next)).getEndX();int endY = ((CPath) map.get(next)).getEndY();PointModle pointModle2=new PointModle();pointModle2.setX((double) endX);pointModle2.setY((double)endY);//两线同处于控件中if ((pointModle1.getX()==pointModle3.getX()&&pointModle1.getY()==pointModle3.getY())||(pointModle1.getX()==pointModle4.getX()&&pointModle1.getY()==pointModle4.getY())||(pointModle2.getX()==pointModle3.getX()&&pointModle2.getY()==pointModle3.getY())||(pointModle2.getX()==pointModle4.getX()&&pointModle2.getY()==pointModle4.getY())){continue;}SegmentsIntersect sege= new SegmentsIntersect(pointModle1,pointModle2,pointModle3,pointModle4);if (sege.SegmentsIntersect()){return true;}}}return false;}/*** 恢复到原有的位置状态*/private void recoveryPosition() {(drawableMap.get(cloneIndex)).setXcoords(mXcoordsClone);(drawableMap.get(cloneIndex)).setYcoords(mYcoordsClone);if (mProLineIndex>-1){((CPath)drawableMap.get(mProLineIndex)).setEndX(mEndXClone);((CPath)drawableMap.get(mProLineIndex)).setEndY(mEndYClone);}if (mNextLineIndex>-1){drawableMap.get(mNextLineIndex).setXcoords(mLineStartX);drawableMap.get(mNextLineIndex).setYcoords(mLineStartY);}mCanvasConnection.invalidate();}

3)、进行多控件的滑动连接:

滑动时的处理:

 mCanvasConnection.setOnWidgetMoveListener(new ActionFrameCanvasView.onWidgetMoveListener() {@Overridepublic void onWidgetMove(int index, int x, int y) {if (!mCanvasConnection.isDragWidgetMode()){// TODO: 2017/7/24 进入划线模式,把移动划线储存起来if (index>-1){mDrawableMap.clear();mDrawableMap.putAll(drawableMap);mDrawableMap.remove(index);int moveToWidgetIndex = getDown2Widget(x, y, mDrawableMap);if (moveToWidgetIndex>-1){mMoveXcoords = drawableMap.get(moveToWidgetIndex).getXcoords();mMoveYcoords = drawableMap.get(moveToWidgetIndex).getYcoords();double abs = Math.sqrt((mMoveXcoords - mMoveXcoordsStart) * (mMoveXcoords - mMoveXcoordsStart)+ (mMoveYcoords - mMoveYcoordsStart) * (mMoveYcoords - mMoveYcoordsStart));if (abs>ActionWidget.RADIUS) {//设置控件内部的关系if (setWidgetRelationship(drawableMap,moveToWidgetIndex,key, mProMoveWidget)){Toast.makeText(ConnectionSegmentActivity.this, "不能再连了" + index, Toast.LENGTH_SHORT).show();return;}setLineTo(index, mMoveXcoordsStart, mMoveYcoordsStart, mMoveXcoords, mMoveYcoords);mMoveXcoordsStart=mMoveXcoords;mMoveYcoordsStart=mMoveYcoords;mMoveLineList.add(key);                                 //把滑动划线的key值存入mMoveLineToWidgetList.add(moveToWidgetIndex);          //把滑动连接的控件key值存入mProMoveWidget=moveToWidgetIndex;}}}}else {//  当控件拖动时控件间的连线也随之变化if ((index > -1)&&isActionFrameWidgetTypeInstance(drawableMap.get(index))) {ActionFrameWidget currentFrameWidget = (ActionFrameWidget) drawableMap.get(index);int proWidgetIndex = currentFrameWidget.getProWidget();int nextWidgetIndex = currentFrameWidget.getNextWidget();//控件没有前连接if (proWidgetIndex < 0) {//控件没有后连接if (nextWidgetIndex < 0) {return;} else {//只有后连接,则获取后连接控件setNextLineMove(x, y, nextWidgetIndex);}} else {                //控件有前连接if (((ActionFrameWidget) drawableMap.get(index)).getNextWidget() < 0) {//只有前连接setProLineMove(x, y, proWidgetIndex);} else {//前后连接都有setNextLineMove(x, y, nextWidgetIndex);setProLineMove(x, y, proWidgetIndex);}}}}}});

添加控件间的连线、建立控件内部的联系和画布中删除控件间的连线、取消滑动手势时存储的控件的内部关系

  //  当控件拖动时控件间的连线也随之变化if ((index > -1)) {mDrawableMap.clear();mDrawableMap.putAll(drawableMap);mDrawableMap.remove(index);int moveToWidgetIndex = getDown2Widget(x, y, mDrawableMap);if (moveToWidgetIndex>-1){mMoveXcoords = drawableMap.get(moveToWidgetIndex).getXcoords();mMoveYcoords = drawableMap.get(moveToWidgetIndex).getYcoords();double abs = Math.sqrt((mMoveXcoords - mMoveXcoordsStart) * (mMoveXcoords - mMoveXcoordsStart)+ (mMoveYcoords - mMoveYcoordsStart) * (mMoveYcoords - mMoveYcoordsStart));if (abs>ActionWidget.RADIUS) {//设置控件内部的关系if (setWidgetRelationship(drawableMap,moveToWidgetIndex,key, mProMoveWidget)){Toast.makeText(MainActivity.this, "不能再连了" + index, Toast.LENGTH_SHORT).show();return;}setLineTo(index, mMoveXcoordsStart, mMoveYcoordsStart, mMoveXcoords, mMoveYcoords);mMoveXcoordsStart=mMoveXcoords;mMoveYcoordsStart=mMoveYcoords;mMoveLineList.add(key);                                 //把滑动划线的key值存入mMoveLineToWidgetList.add(moveToWidgetIndex);          //把滑动连接的控件key值存入mProMoveWidget=moveToWidgetIndex;}}(!mCanvasConnection.isDragWidgetMode()) {if (index > -1) {//获取抬起时触摸点落在控件的坐标范围if (isActionFrameWidgetTypeInstance(drawableMap.get(index))) {mUpXcoords = drawableMap.get(index).getXcoords();mUpYcoords = drawableMap.get(index).getYcoords();//如抬起时是在长按的控件内则不划线if (!((mDownXcoords == mUpXcoords) && (mDownYcoords == mDownYcoords))) {if (((ActionWidget) drawableMap.get(index)).getProWidget() > -1) {Toast.makeText(MainActivity.this, "已有前一连接:" + index, Toast.LENGTH_SHORT).show();return;}if (isActionFrameWidgetTypeInstance(drawableMap.get(mProWidget))) {((ActionWidget) drawableMap.get(mProWidget)).setNextWidget(index);//设置长按控件的后连接值} else {return;}((ActionWidget) drawableMap.get(index)).setProWidget(mProWidget);//当前控件的前连接值setLineTo(index, mDownXcoords, mDownYcoords, mUpXcoords, mUpYcoords);}}}else {//清除控件内部关系removeWidgetRelationship(drawableMap,mMoveLineToWidgetList);//把连接从画布中去除removeMoveLine(drawableMap,mMoveLineList);}mCanvasConnection.setDragMode(true);/*** 擦除画布中的连线* @param drawableMap* @param list*/private void removeMoveLine(Map<Integer, CDrawable> drawableMap, List<Integer> list) {for (int i=0;i<list.size();i++){drawableMap.remove(list.get(i));mCanvasConnection.invalidate();}}/*** 取消控件内部的关系* @param drawableMap* @param list*/private void removeWidgetRelationship(Map<Integer, CDrawable> drawableMap, List<Integer> list) {for (int i=0;i<list.size();i++){if (isActionFrameWidgetTypeInstance(drawableMap.get(list.get(i)))){ActionWidget drawable = (ActionWidget) drawableMap.get(list.get(i));if (isActionFrameWidgetTypeInstance(drawableMap.get(drawable.getProWidget()))){ActionWidget drawable1 = (ActionWidget) drawableMap.get(drawable.getProWidget());drawable1.setNextWidget(-1);drawable1.setNextLineIndex(-1);}drawable.setProWidget(-1);drawable.setProLineIndex(-1);drawable.setNextWidget(-1);drawable.setNextLineIndex(-1);}}}/*** 设置控件内部的关系* @param drawableMap* @param index* @param key* @param proWidget* @return*/private boolean setWidgetRelationship(Map<Integer, CDrawable> drawableMap, int index, int key, int proWidget) {if (isRelation(drawableMap, index, proWidget)) {return true;}((ActionWidget) drawableMap.get(index)).setProWidget(proWidget);//当前控件的前连接值((ActionWidget) drawableMap.get(index)).setProLineIndex(key);//设置当前控件的连接值((ActionWidget) drawableMap.get(proWidget)).setNextLineIndex(key);//设置长按控件的连接值return false;}/*** 控件能不能再连接* @param drawableMap* @param index* @param proWidget* @return*/private boolean isRelation(Map<Integer, CDrawable> drawableMap, int index, int proWidget) {if (((ActionWidget) drawableMap.get(index)).getProWidget() > -1) {     //有前一连接return true;}if (isActionFrameWidgetTypeInstance(drawableMap.get(proWidget))) {((ActionWidget) drawableMap.get(proWidget)).setNextWidget(index);//设置长按控件的后连接值}else {return true;}return false;}private boolean isWidgetIntoLine(Map<Integer, CDrawable> map,int index,int x,int y){Iterator iterator=map.keySet().iterator();int proLineIndex = -1;int nextLineIndex = -1;if (isActionFrameWidgetTypeInstance(map.get(index))){ActionWidget drawable = (ActionWidget) map.get(index);proLineIndex=drawable.getProLineIndex();nextLineIndex=drawable.getNextLineIndex();}while (iterator.hasNext()) {int next = (int) iterator.next();if (map.get(next) instanceof CPath){if ((nextLineIndex==next)||(proLineIndex==next)){continue;}int xcoords = (map.get(next)).getXcoords();int ycoords = (map.get(next)).getYcoords();int endX = ((CPath) map.get(next)).getEndX();int endY = ((CPath) map.get(next)).getEndY();if (pointToLine(xcoords,ycoords,endX,endY,x,y)< ActionWidget.RADIUS*1.1){return true;}}}return false;}

五、项目Demo地址:http://download.csdn.net/detail/wangyongyao1989/9909113

Android 画布Canvas之控件连线操作相关推荐

  1. android自定义刻度线,Android自定义控件之刻度尺控件

    今天我做的是一个自定义刻度尺控件,由于项目需求需要使用刻度尺那样滑动选择,由于对自定义控件的认识还不够深入,于是花了一周多时间才把这个控件给整出来,也是呕心沥血的经历啊,也让我对自定义控件有了自己的认 ...

  2. Android拼图滑块验证码控件

    大咖好,博主毕业工作半年多了.今天给大噶呈献博主博客处女作--Android拼图滑块验证码控件.由于初写博客,很多地方可能不够好,望各位多多给点意见.工作半年才送出第一篇博客很惭愧555. 概述 验证 ...

  3. Android常用酷炫控件(开源项目)github地址汇总

    转载一个很牛逼的控件收集帖... 第一部分 个性化控件(View) 主要介绍那些不错个性化的 View,包括 ListView.ActionBar.Menu.ViewPager.Gallery.Gri ...

  4. Android仿照薄荷appBMI控件实现

    Android仿照薄荷appBMI控件实现 BMI原图和实现的效果图 BMI控件的实现 在XML中自定义属性 在java代码中自定义view 使用自定义控件 添加背景和虚线分割线 结语 BMI原图和实 ...

  5. Android RadarScanView雷达扫描控件

    Android RadarScanView雷达扫描控件 简介 雷达扫描控件常用于检索场景,扫描文件.搜索目标等都可将雷达扫描识图展示给用户,增强用户体验. 样例 实现 在onDraw中使用Canvas ...

  6. android xml图片缩放,Android通过自定义ImageView控件实现图片的缩放和拖动的实现代码...

    概述:通过自定义ImageView控件,在xml布局里面调用自定的组件实现图片的缩放. /** * 自定义的ImageView控制,可对图片进行多点触控缩放和拖动 * * @author qiuwan ...

  7. android线性布局设置控件固定在底部,Android UI组件LinearLayout线性布局详解

    LinearLayout 线性布局,该布局的继承关系: 1. 什么是线性布局 通俗的说感觉起来和线有关,参照线的特点,有么是横向的,要么是竖向的. LinearLayout是线性布局控件,它包含的子控 ...

  8. Android 滑动拼图验证码控件

    Android 滑动拼图验证码控件 简介: 很多软件为了安全防止恶意攻击,会在登录/注册时进行人机验证,常见的人机验证方式有:谷歌点击复选框进行验证,输入验证码验证,短信验证码,语音验证,文字按顺序选 ...

  9. 小视频app源码,Android 滑动拼图验证码控件

    小视频app源码,Android 滑动拼图验证码控件 代码实现: 滑块视图类:SlideImageView.java.实现小视频APP源码随机选取拼图位置,对拼图位置进行验证等功能. public c ...

最新文章

  1. linux flatpak 简介 同一个应用在不同linux发行版运行
  2. 小场景带来大提升:“AI之眼”加持工厂品控流水线,质检质量提升80%
  3. DCMTK:修改DICOM文件的类
  4. Simple2D-15(音乐播放器)使用 glfw 库
  5. 木兰许可证专业解读及首批采用“木兰”开源项目列表
  6. HTTP,FTP,SMTP错误码
  7. Keras深度学习实战(1)——神经网络基础与模型训练过程详解
  8. 3.15PMP试题每日一题
  9. python批量打印word文件
  10. android 切换语言不起作用,Android 语言切换实例及踩坑
  11. 崇明东平森林公园一日游
  12. 什么浏览器最好用,五款浏览器对比那个最好?
  13. fastq与fasta文件格式解析
  14. 员工缺乏责任心的四大原因
  15. 向大家推荐一下我的笔记APP『百灵』,里面有丰富的面试资料
  16. 关于虚拟机无法启动(The VMware Authorization Service is not running)
  17. 前端与后端的区别(保姆级整理-很详细-2021-05-26)
  18. 投资理财——量化交易角度理解宏观概念
  19. Android studio 中搜狗输入法 中文提示不跟随光标
  20. 《工程制图基础》课程笔记(一)——国标规定

热门文章

  1. EasyPoiUtil纯代码生成excel进行下载
  2. 今天是一位朋友的生日,送给她最美丽的祝福.
  3. Coremail2022Q4邮件安全报告:暴力破解骤降,盗号问题有所缓解?
  4. protoc-gen-go的坑
  5. 权限系统就该这么设计(万能通用),稳的一批!
  6. ffmpeg Intel硬件加速总结
  7. 未来计算机畅想英语作文,畅想未来的生活life in the future
  8. tzset函数与locatime时间函数的关系
  9. 这么多处理器(CPU/SOC)牌子,到底哪家强
  10. element 表格全局筛选(筛选结果请求后端接口)