我正在使用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。
答案 0 :(得分:4)
由于工作代码没有答案,我可以分享一个有效的例子。关键是要有一个当前活动指针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;
}
}
答案 1 :(得分:3)
见Making 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);
}
}
答案 2 :(得分: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;
}