如何用多个手指在画布上绘图

And*_*Dev 2 android multi-touch android-canvas

我正在使用Canvas创建绘图应用程序的 android 类。这是我第一次尝试使用 Canvas 类。到目前为止,我使用的代码工作正常,绘图工作正常。但我在这段代码中意识到,它允许用户仅用一根手指进行绘画,我的意思是说,如果用户使用多于一根手指在画布上绘画,则不允许用户用多根手指进行绘画。我浏览了有关多个触摸事件的文档,但未能在我的代码中实现它。那么有人可以帮我解决这个问题吗?

我用于在画布上绘图的代码:

public class DrawView extends View implements OnTouchListener 
{
    private Canvas      m_Canvas;
    
    private Path        m_Path;
    
    private Paint       m_Paint;
    
    ArrayList<Pair<Path, Paint>> arrayListPaths = new ArrayList<Pair<Path, Paint>>();
    
    ArrayList<Pair<Path, Paint>> undonePaths = new ArrayList<Pair<Path, Paint>>(); 
    
    private float mX, mY;
    
    private Bitmap bitmapToCanvas;
    
    private static final float TOUCH_TOLERANCE = 4;
        
    public DrawView(Context context) 
    {
        super(context);
        setFocusable(true);
        setFocusableInTouchMode(true);      
        this.setOnTouchListener(this);
        
        onCanvasInitialization();
    }      
    
    public void onCanvasInitialization()
    {
        m_Paint = new Paint();
        m_Paint.setAntiAlias(true);
        m_Paint.setDither(true);
        m_Paint.setColor(Color.parseColor("#37A1D1"));
        m_Paint.setStyle(Paint.Style.STROKE);
        m_Paint.setStrokeJoin(Paint.Join.ROUND);
        m_Paint.setStrokeCap(Paint.Cap.ROUND);
        m_Paint.setStrokeWidth(2);      
                
        m_Path = new Path();    
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
    {
        super.onSizeChanged(w, h, oldw, oldh);
        
        bitmapToCanvas = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        m_Canvas = new Canvas(bitmapToCanvas);
    }
    
    @Override
    protected void onDraw(Canvas canvas)
    {    
        canvas.drawBitmap(bitmapToCanvas, 0f, 0f, null);
        canvas.drawPath(m_Path, m_Paint);
    }
    
    public boolean onTouch(View arg0, MotionEvent event) 
    {
        float x = event.getX();
        float y = event.getY();

        switch (event.getAction()) 
        {
            case MotionEvent.ACTION_DOWN:
            touch_start(x, y);
            invalidate();
            break;
            case MotionEvent.ACTION_MOVE:
            {
                touch_move(x, y);
                invalidate();
                break;
            }
            case MotionEvent.ACTION_UP:
            touch_up();
            invalidate();
            break;
        }
        return true;
    }

    private void touch_start(float x, float y) 
    {
        undonePaths.clear();
        m_Path.reset();
        m_Path.moveTo(x, y);
        mX = x;
        mY = y;
    }
    
    private void touch_move(float x, float y) 
    {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) 
        {
            m_Path.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
            mX = x;
            mY = y;
        }
    }
    private void touch_up() 
    {
        m_Path.lineTo(mX, mY);
                
        // commit the path to our offscreen
        m_Canvas.drawPath(m_Path, m_Paint);
        
        // kill this so we don't double draw                    
        Paint newPaint = new Paint(m_Paint); // Clones the mPaint object
        arrayListPaths.add(new Pair<Path, Paint>(m_Path, newPaint));
        m_Path = new Path();
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试更改代码以支持多点触摸,但它无法正常工作。这是我更改的代码。

Ish*_*Ish 5

由于工作代码没有答案,我可以分享一个工作示例。关键是要有一个当前活动指针 ID 及其路径的数组。同样重要的是要知道,在多个移动指针的情况下,onTouchEvent 只会为所有指针调用一次,并且您需要迭代所有指针来绘制它们的新位置。

public class DrawView extends View {

    private Paint drawPaint, canvasPaint;
    private Canvas drawCanvas;
    private Bitmap canvasBitmap;

    private SparseArray<Path> paths;

    public DrawingView(Context context) {
        super(context);
        setupDrawing();
    }

    public DrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setupDrawing();
    }

    public DrawingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setupDrawing();
    }

    private void setupDrawing() {
        paths = new SparseArray<>();

        drawPaint = new Paint();
        drawPaint.setColor(Color.RED);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(20);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);

        canvasPaint = new Paint(Paint.DITHER_FLAG);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawCanvas = new Canvas(canvasBitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
        for (int i=0; i<paths.size(); i++) {
            canvas.drawPath(paths.valueAt(i), drawPaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int index = event.getActionIndex();
        int id = event.getPointerId(index);

        Path path;
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                path = new Path();
                path.moveTo(event.getX(index), event.getY(index));
                paths.put(id, path);
                break;

            case MotionEvent.ACTION_MOVE:
                for (int i=0; i<event.getPointerCount(); i++) {
                    id = event.getPointerId(i);
                    path = paths.get(id);
                    if (path != null) path.lineTo(event.getX(i), event.getY(i));
                }
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                path = paths.get(id);
                if (path != null) {
                    drawCanvas.drawPath(path, drawPaint);
                    paths.remove(id);
                }
                break;
            default:
                return false;
        }
        invalidate();
        return true;
    }

}
Run Code Online (Sandbox Code Playgroud)

  • 此侦听器仅针对 UP 和 DOWN 事件独立触发,但不适用于 MOVE 事件。对于同时发生的 MOVE 事件,它只会触发一次,这就是为什么您需要循环遍历所有指针。 (2认同)