2013-05-09 2 views
1

도면 응용 프로그램을 만드는 중 android Canvas 클래스를 사용하고 있습니다. 이것은 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(); 
    } 
} 

이지만, 제대로 작동하지 않습니다. 이것은 나의 변경 코드 http://pastebin.com/W6qvpYGW입니다.

답변

2

Making Sense of Multitouch을 참조하면 나에게 많은 도움이되었습니다. 그것은 당신이 각 포인터의 PointerId에 따라, 동시에 여러 선을 그릴 할 2.if 당신이 action & MotionEvent.ACTION_MASK

에 전환하는 것이

1.Make 확실히 기억

포인트 멀티 터치를 처리하는 방법을 explanes하는 MotionEvent.ACTION_POINTER_DOWN에 들어 와서 포인터 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); 
    } 
} 
+0

사실 나는이 문서를 통해 이동,하지만 문서에 그들은 그것이 우리가 MotionEvent.ACTION_POINTER_DOWN 필요 다수의 터치를 등록, 그러나 그들의 예제 코드가 있음을 사용한 적이, 그리고 u는 U 수 괜찮다 둘째 경우 언급 내 코드에서 구현하도록 도와주세요. 사실 나는 시도했지만 작동하지 않습니다. – AndroidDev

+0

내 제안에 따라 코드를 변경했지만 한 손가락으로도 그릴 때마다 충돌이 발생합니다. http://pastebin.com/uKwz24y5 – AndroidDev

+0

코드에서 ACTION_DOWN은 비어 있으며 Action_down 자체에서 포인터 ID를 얻을 수 있습니다. ACTION_DOWN 터치 이벤트의 모든 코스에서 첫 번째 이벤트가되고, 그 다음에 와드에서 ACTION_POINTER_DOWN 손가락이 서로 호출됩니다. 포인터 수를 구한 다음 포인터 색인을 계산하고 ACTION_POINTER_DOWN 및 ACTION_POINTER_UP에서 관리해야합니다. 그것은 완벽하게 구현하는 데 약간의 시간이 걸릴 수 있습니다 .... –

0

복사하여 붙여 넣기의 예입니다. 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; 
} 
+0

세 개의 ACTION 이벤트에 for 루프가 필요한 이유는 무엇입니까? 나는 각 손가락이이 수신기를 독립적으로 발사 할 것이라고 생각했을까요? –

3

작동 코드에 대한 답변이 없으므로 작업 예제를 공유 할 수 있습니다. 핵심은 현재 활성화 된 포인터 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; 
    } 

} 
+0

ACTION_MOVE에 for 루프가 필요한 이유는 무엇입니까? 나는 각 손가락이이 수신기를 독립적으로 발사 할 것이라고 생각했을까요? –

+2

이 리스너는 UP 및 DOWN 이벤트에 대해서만 독립적으로 시작되지만 MOVE 이벤트에는 발생하지 않습니다. 동시 MOVE 이벤트의 경우 한 번만 실행되므로 모든 포인터를 반복해야합니다. – Ish

+0

감사합니다. 매우 유익합니다. –

관련 문제