Android 画布Canvas之控件连线操作

一、需求:

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() {
            @Override
            public 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() {
            @Override
            public 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() {
            @Override
            public 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;
        else
            return 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;
        else
            return 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() {
            @Override
            public 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() {
            @Override
            public 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;
    }

    /**
     * 判断控件中的连线是否与画布中其他的连线相交
     */
    @Nullable
    private 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() {
            @Override
            public 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


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值