如何在点之间画线并拖动这些点?

11
我希望在视图上绘制点之间的线条,然后将这些点拉到所需位置,即使形状会改变也能实现。我知道如何在两个点之间绘制线条canvas.drawLine(10, 10, 90, 10, paint);,使用这个方法我可以绘制点之间的线条。编辑:这里我附上了一张图片以便更清楚地解释,从Paul的答案中,我现在能够绘制点之间的线条,但仍有拉动点的问题...

enter image description here


你能否请发布整个代码?因为我非常需要这段代码...提前感谢。 - Suleman Khan
请告诉我如何在 onDraw() 方法中实现这个。 - Suleman Khan
4个回答

8

以下是完成方法。假设您有点,将它们设置为全局变量:

PointF topLeft = new PointF(10,10);
PointF topRight = new PointF(90,10);
PointF bottomLeft = new PointF(10,90);
PointF bottomRight = new PointF(90,90);

您需要做的是在每个点周围创建一个RectF。RectF越大,点的触摸区域就越大。

float sizeOfRect = 5f;
RectF topLeftTouchArea = new RectF(topLeft.x - sizeOfRect, topLeft.y - sizeOfRect, topLeft.x + sizeOfRect, topLeft.y + sizeOfRect);
//Do this for the other points too

定义一些全局变量来跟踪用户在onTouch中的操作。一个int是被触摸的角落,另外四个是角落的标识符。
private final int NONE = -1, TOUCH_TOP_LEFT = 0, TOUCH_TOP_RIGHT = 1, TOUCH_BOT_LEFT = 2, TOUCH_BOT_RIGHT = 3;
int currentTouch = NONE;

现在,在您的onTouch事件中,您可以像这样检查用户正在触摸的点:
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
    //The user just put their finger down.
    //We check to see which corner the user is touching
    //And set our global, currentTouch, to the appropriate constant.
    case MotionEvent.ACTION_DOWN:
        if (topLeftTouchArea.contains(event.getX(), event.getY()) {
            currentTouch = TOUCH_TOP_LEFT;
        } else if (topRightTouchArea.contains(event.getX(),event.getY()) {
            currentTouch = TOUCH_TOP_RIGHT;
        } else if (botLeftTouchArea.contains(event.getX(),event.getY()) {
            currentTouch = TOUCH_BOT_LEFT;
        } else if (botRightTouchArea.contains(event.getX(), event.getY()) {
            currentTouch = TOUCH_BOT_RIGHT;
        } else {
            return false; //Return false if user touches none of the corners
        }
        return true; //Return true if the user touches one of the corners
    //Now we know which corner the user is touching.
    //When the user moves their finger, we update the point to the user position and invalidate.
    case MotionEvent.ACTION_MOVE:
        switch (currentTouch) {
        case TOUCH_TOP_LEFT:
             topLeft.x = event.getX();
             topLeft.y = event.getY();
             //The bottom left x position has to move with the top left corner
             bottomLeft.x = topLeft.x;
             //The top right y position has to move with the top left corner
             topRight.y = topLeft.y;
             invalidate();
             return true;
        case TOUCH_TOP_RIGHT:
             topRight.x = event.getX();
             topRight.y = event.getY();
             //The top left y position has to move with the top right corner
             topLeft.y = topRight.y;
             //The bottom right x position has to move with the top right corner
             bottomRight.x = topRight.x;
             invalidate();
             return true;
        case TOUCH_BOT_LEFT:
             bottomLeft.x = event.getX();
             bottomLeft.y = event.getY();
             bottomRight.y = bottomLeft.y;
             topLeft.x = bottomLeft.x;
             invalidate();
             return true;
        case TOUCH_BOT_RIGHT:
             bottomRight.x = event.getX();
             bottomRight.y = event.getY();
             topRight.x = bottomRight.x;
             bottomLeft.y = bottomRight.y;
             invalidate();
             return true;
        }
        //We returned true for all of the above cases, because we used the event
        return false; //If currentTouch is none of the above cases, return false

    //Here the user lifts up their finger.
    //We update the points one last time, and set currentTouch to NONE.
    case MotionEvent.ACTION_UP:
        switch (currentTouch) {
        case TOUCH_TOP_LEFT:
             topLeft.x = event.getX();
             topLeft.y = event.getY();
             //The bottom left x position has to move with the top left corner
             bottomLeft.x = topLeft.x;
             //The top right y position has to move with the top left corner
             topRight.y = topLeft.y;
             invalidate();
             currentTouch = NONE;
             return true;
        case TOUCH_TOP_RIGHT:
             topRight.x = event.getX();
             topRight.y = event.getY();
             //The top left y position has to move with the top right corner
             topLeft.y = topRight.y;
             //The bottom right x position has to move with the top right corner
             bottomRight.x = topRight.x;
             invalidate();
             currentTouch = NONE;
             return true;
        case TOUCH_BOT_LEFT:
             bottomLeft.x = event.getX();
             bottomLeft.y = event.getY();
             bottomRight.y = bottomLeft.y;
             topLeft.x = bottomLeft.x;
             invalidate();
             currentTouch = NONE;
             return true;
        case TOUCH_BOT_RIGHT:
             bottomRight.x = event.getX();
             bottomRight.y = event.getY();
             topRight.x = bottomRight.x;
             bottomLeft.y = bottomRight.y;
             invalidate();
             currentTouch = NONE;
             return true;
        }
        return false;
    }
}

这段代码会在你的点周围画一个矩形。可以想象在图片中的点周围画出方框,这些方框是通过Rect对象创建的“触摸区域”。矩形的大小由sizeOfRect设定。在onTouchEvent事件中,会检查每个矩形对象,看用户的触摸是否在矩形内部,从而判断用户是否试图触摸该点。

我尝试过这个,但仍然无法完全理解。你能分享一些关于这方面的教程吗? - RajaReddy PolamReddy
我用4个不同的点绘制正方形,现在无法在这些点上获取触摸。 - RajaReddy PolamReddy
我能够在第一次移动这条线,但第二次却无法运行,并且那些RecF也不会改变。 - RajaReddy PolamReddy
@RajaReddyPolamReddy,你能否发布整个代码吗?因为我非常需要这段代码...提前感谢。 - Suleman Khan
@ZedScio请帮帮我,我的问题是:https://dev59.com/UGrXa4cB1Zd3GeqPBKSu,与您上面的答案类似但有些不同。 - Suleman Khan
显示剩余6条评论

4

我认为您可能正在寻找Path类:

Path类封装了由直线段、二次曲线和三次曲线组成的复合(多轮廓)几何路径。它可以使用canvas.drawPath(path,paint)绘制,可以填充或描边(基于paint的样式),也可以用于剪切或在路径上绘制文本。

请参阅this tutorial,了解canvas.drawPath的示例。


我不认为这些存在,但它们很容易创建。只需在正方形中心绘制小正方形点,然后进行命中测试以查看哪个被命中并对该点执行拖动操作。实际上,哈哈,这有点复杂,但并不难做到正确。 - Paul Sasik
我已经编辑了我的问题并添加了图片,我能够使用路径绘制线条,但是触摸监听器存在问题... - RajaReddy PolamReddy

2
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.View;

public class TestView extends View
{

    private Paint paint;
    private PointF startPoint, endPoint;
    private boolean isDrawing;

    public TestView(Context context)
    {
        super(context);
        init();
    }

    private void init()
    {
        paint = new Paint();
        paint.setColor(Color.RED);
        paint.setStyle(Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        if(isDrawing)
        {
            canvas.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y, paint);
        }
    }


    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                startPoint = new PointF(event.getX(), event.getY());
                endPoint = new PointF();
                isDrawing = true;
                break;
            case MotionEvent.ACTION_MOVE:
                if(isDrawing)
                {
                    endPoint.x = event.getX();
                    endPoint.y = event.getY();
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                if(isDrawing)
                {
                    endPoint.x = event.getX();
                    endPoint.y = event.getY();
                    isDrawing = false;
                    invalidate();
                }
                break;
            default:
                break;
        }
        return true;
    }
}

你的代码像是重新绘制从你想要拖动的点开始的线条,我不想重新绘制,只想用另一条线扩展该线并保留前一个点。 - RajaReddy PolamReddy

2

编辑:

在Android中,确实需要使用Path类。很抱歉我不能提供代码片段。但是这里有一些让您开始的东西。

当您绘制一条线时- canvas.drawLine(x1, y1, x2, y2, paint);,您的起点是(x1,y1)。现在,如果您需要从任何一端拉出该行,则需要先固定另一端。假设您从(x2,y2) 拉取。因此,(x1,y1)变为常量,并且您从另一端拉动。使用Path类时,请首先调用moveTo() 到这个固定点。它所做的是提供一个关于线条必须移动的点。然后,您可以在触摸事件上使用lineTo() 调用来相应地拉伸该线。需要进行大量调整。但这就是可以让您开始的内容。很抱歉我没有想到一个代码片段,时间有点短。请参阅Path类的文档。您可能会找到更多有用的内容。


编辑:

关于将触摸监听器添加到点:

假设您有一条线从(x1,y1)(x2,y2)

现在,要为点添加触摸监听器,可以将onTouchListener添加到整个视图。

final View touchView = findViewById(R.id.touchView);
    touchView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
               if(event.getX() == x2 && event.getY() == y2)
               {
                  //you know that you have clicked on the rear end of the line,so now you can do your dragging stuff.
                  if(event.getAction() == MotionEvent.ACTION_DOWN){
                       //you know user has clicked in order to draw
                       //redraw the existing line with black color to remove it
                       paint.setColor(Color.BLACK);
                       canvas.drawLine(x1, y1, x2, y2, paint);
                       //reset the paint object
                       paint.setColor(Color.WHITE); 
                       //now use moveTo() and lineTo() to attain the functionality of dragging on your Path object
                       path.moveTo(x1,y1);
                       path.lineTo(event.getX(),event.getY());
                  }else if(event.getAction() == MotionEvent.ACTION_MOVE){
                       path.lineTo(event.getX(),event.getY());
                  }else if(event.getAction() == MotionEvent.ACTION_UP){
                  }
       }  
               return true;
        }
    });

这只是一个想法,我还没有机会去测试它。希望它有所帮助。


这只是用手指绘图,如果你不明白我的问题,请阅读我的问题并让我知道。 - RajaReddy PolamReddy
哦,好的。你想让用户先点击一个点,然后再点击另一个点,然后这两个点之间就会出现一条线,对吗? - Kazekage Gaara
不需要,我现在已经有两个点之间的线了,我想拖动其中一个点来延长线而不改变另一个点,就像改变三角形大小一样。 - RajaReddy PolamReddy
@RajaReddyP,请查看编辑。我已经尝试了一些东西,实际上不知道它是否可行。 - Kazekage Gaara
我已经按照您的指导完成了创建点和绘制点之间连线的部分,尚未完成触摸监听器部分... - RajaReddy PolamReddy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接