2

我正在使用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();
    }
}

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

4

3 回答 3

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;
    }

}
于 2016-10-20T21:30:41.833 回答
4

请参阅Make Sense of Multitouch,它对我帮助很大。它解释了如何处理多点触控

要记住的要点

1.确保你打开action & MotionEvent.ACTION_MASK

2.如果要同时绘制多条线,按照MotionEvent.ACTION_POINTER_DOWN中每个指针的PointerId,通过比较指针id在MotionEvent.ACTION_POINTER_UP中释放。

private static final int INVALID_POINTER_ID = -1;

// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;

// Existing code ...

@Override
public boolean onTouchEvent(MotionEvent ev) {
    final int action = ev.getAction();
    switch (action & MotionEvent.ACTION_MASK) {
    case MotionEvent.ACTION_DOWN: {
        final float x = ev.getX();
        final float y = ev.getY();

        mLastTouchX = x;
        mLastTouchY = y;

        // Save the ID of this pointer
        mActivePointerId = ev.getPointerId(0);
        break;
    }

    case MotionEvent.ACTION_MOVE: {
        // Find the index of the active pointer and fetch its position
        final int pointerIndex = ev.findPointerIndex(mActivePointerId);
        final float x = ev.getX(pointerIndex);
        final float y = ev.getY(pointerIndex);

        final float dx = x - mLastTouchX;
        final float dy = y - mLastTouchY;

        mPosX += dx;
        mPosY += dy;

        mLastTouchX = x;
        mLastTouchY = y;

        invalidate();
        break;
    }

    case MotionEvent.ACTION_UP: {
        mActivePointerId = INVALID_POINTER_ID;
        break;
    }

    case MotionEvent.ACTION_CANCEL: {
        mActivePointerId = INVALID_POINTER_ID;
        break;
    }

    case MotionEvent.ACTION_POINTER_UP: {
        // Extract the index of the pointer that left the touch sensor
        final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) 
                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastTouchX = ev.getX(newPointerIndex);
            mLastTouchY = ev.getY(newPointerIndex);
            mActivePointerId = ev.getPointerId(newPointerIndex);
        }
        break;
    }
    }

    return true;
}

编辑

请查看此代码...这仍然存在一些问题,但我认为您可以对其进行调试并修复这些问题...此外,逻辑不存在持续行请实现...

package com.example.stackgmfdght;

import java.util.ArrayList;


import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;

public class JustDoIt extends View
{
    private Canvas          m_Canvas;

 //   private Path            m_Path;

    int current_path_count=-1; 
    ArrayList <Path> m_Path_list =  new ArrayList<Path>();
    ArrayList <Float> mX_list =  new ArrayList<Float>();
    ArrayList <Float> mY_list =  new ArrayList<Float>();
    ArrayList <Integer> mActivePointerId_list =  new ArrayList<Integer>();

    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 JustDoIt (Context context)
    {
            super(context);
            setFocusable(true);
            setFocusableInTouchMode(true);      

            onCanvasInitialization();
    }     

    public JustDoIt(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        setFocusable(true);
        setFocusableInTouchMode(true);      

        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);
            for(int i=0;i<=current_path_count;i++)
            {
            canvas.drawPath(m_Path_list.get(i), m_Paint);
            }
    }


    public void onDrawCanvas()
    {
            for (Pair<Path, Paint> p : arrayListPaths)
            {
                m_Canvas.drawPath(p.first, p.second);
            }
    }

    private static final int INVALID_POINTER_ID = -1;

    // The ‘active pointer’ is the one currently moving our object.
    private int mActivePointerId = INVALID_POINTER_ID;



    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
            super.onTouchEvent(event);

            final int action = event.getAction();  

            switch (action & MotionEvent.ACTION_MASK)
            {
                    case MotionEvent.ACTION_DOWN:
                    {
                            float x = event.getX();
                            float y = event.getY();


                            current_path_count=0;
                            mActivePointerId_list.add ( event.getPointerId(0),current_path_count);                                              
                            touch_start((x ),(y ),current_path_count );
                    }
                    break;

                    case MotionEvent.ACTION_POINTER_DOWN:
                    {

           if(event.getPointerCount()>current_path_count)
           {

                            current_path_count++;
                            float x = event.getX(current_path_count);
                            float y = event.getY(current_path_count);


                            mActivePointerId_list.add ( event.getPointerId(current_path_count),current_path_count);                                              
                                touch_start((x ),(y ),current_path_count);  
           } 
                    }
                    break;

                    case MotionEvent.ACTION_MOVE:
                    {
                         for(int i=0;i<=current_path_count;i++)
                         { try{
                                     int pointerIndex = event
                                    .findPointerIndex(mActivePointerId_list.get(i));

                                    float x = event.getX(pointerIndex);
                                    float y = event.getY(pointerIndex);

                                    touch_move((x ),(y ),i);
                         }
                         catch(Exception e)
                         {
                             e.printStackTrace();
                         }
                         }


                    }
                    break;

                    case MotionEvent.ACTION_UP:
                    { current_path_count=-1; 
                         for(int i=0;i<=current_path_count;i++)
                         {

                                    touch_up(i);
                         }
                         mActivePointerId_list =  new ArrayList<Integer>();


                    }
                    break;

                    case MotionEvent.ACTION_CANCEL:
                    {
                            mActivePointerId = INVALID_POINTER_ID;  
                            current_path_count=-1; 
                    }
                    break;

                    case MotionEvent.ACTION_POINTER_UP:
                    {
                            final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                            final int pointerId = event.getPointerId(pointerIndex);
                            for(int i=0;i<=current_path_count;i++)
                         {
                            if (pointerId == mActivePointerId_list.get(i))
                            {
                                    // This was our active pointer going up. Choose a new
                                    // active pointer and adjust accordingly.

                                     mActivePointerId_list.remove(i);
                                    touch_up(i);
                                    break;
                            }              
                         }
                    }    
                    break;

                    case MotionEvent.ACTION_OUTSIDE:
                    break;
    }

    invalidate();
    return true;
}

    private void touch_start(float x, float y, int count)
    {
        //    undonePaths.clear();
           Path  m_Path=new Path();

           m_Path_list.add(count,m_Path);

           m_Path_list.get(count).reset();


           m_Path_list.get(count).moveTo(x, y);

            mX_list.add(count,x);
            mY_list.add(count,y);

    }

    private void touch_move(float x, float y,int count)
    {
            float dx = Math.abs(x - mX_list.get(count));
            float dy = Math.abs(y - mY_list.get(count));
            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)
            {
                    m_Path_list.get(count).quadTo(mX_list.get(count), mY_list.get(count), (x + mX_list.get(count))/2, (y + mY_list.get(count))/2);
                    try{

                        mX_list.remove(count);
                        mY_list.remove(count);
                        }
                        catch(Exception e)
                        {
                           e.printStackTrace();
                        }
                    mX_list.add(count,x);
                    mY_list.add(count,y);
            }
    }
    private void touch_up(int count)
    {
         m_Path_list.get(count).lineTo(mX_list.get(count), mY_list.get(count));

            // commit the path to our offscreen
            m_Canvas.drawPath( m_Path_list.get(count), 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_list.get(count), newPaint));
            m_Path_list.remove(count);
            mX_list.remove(count);
            mY_list.remove(count);
    }
}
于 2013-05-09T05:35:29.340 回答
1

这里是复制粘贴的示例。只需创建扩展 View 的类并实现以下方法。

private final Paint paint = new Paint(); // Don't forgot to init color, form etc.

@Override
protected void onDraw(Canvas canvas) {
    for (int size = paths.size(), i = 0; i < size; i++) {
        Path path = paths.get(i);
        if (path != null) {
            canvas.drawPath(path, paint);
        }
    }
}

private HashMap<Integer, Float> mX = new HashMap<Integer, Float>();
private HashMap<Integer, Float> mY = new HashMap<Integer, Float>();
private HashMap<Integer, Path> paths = new HashMap<Integer, Path>();

@Override
public boolean onTouchEvent(MotionEvent event) {
    int maskedAction = event.getActionMasked();

    Log.d(TAG, "onTouchEvent");

    switch (maskedAction) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_POINTER_DOWN: {
            for (int size = event.getPointerCount(), i = 0; i < size; i++) {
                Path p = new Path();
                p.moveTo(event.getX(i), event.getY(i));
                paths.put(event.getPointerId(i), p);
                mX.put(event.getPointerId(i), event.getX(i));
                mY.put(event.getPointerId(i), event.getY(i));
            }
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            for (int size = event.getPointerCount(), i = 0; i < size; i++) {
                Path p = paths.get(event.getPointerId(i));
                if (p != null) {
                    float x = event.getX(i);
                    float y = event.getY(i);
                    p.quadTo(mX.get(event.getPointerId(i)), mY.get(event.getPointerId(i)), (x + mX.get(event.getPointerId(i))) / 2,
                            (y + mY.get(event.getPointerId(i))) / 2);
                    mX.put(event.getPointerId(i), event.getX(i));
                    mY.put(event.getPointerId(i), event.getY(i));
                }
            }
            invalidate();
            break;
        }
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_POINTER_UP:
        case MotionEvent.ACTION_CANCEL: {
            for (int size = event.getPointerCount(), i = 0; i < size; i++) {
                Path p = paths.get(event.getPointerId(i));
                if (p != null) {
                    p.lineTo(event.getX(i), event.getY(i));
                    invalidate();
                    paths.remove(event.getPointerId(i));
                    mX.remove(event.getPointerId(i));
                    mY.remove(event.getPointerId(i));
                }
            }
            break;
        }
    }

    return true;
}
于 2014-10-30T14:04:48.457 回答