SurfaceView canvas 具有透明度的绘图路径
SurfaceView canvas drawing path with transparency
我的绘图应用程序有问题。我需要抓住正确的触摸屏并将它们绘制到 canvas。更改画笔的大小工作正常。但是当我更改透明度设置时,程序无法正常运行。它在之前的路径之上强加了一条新路径,并且失去了透明度。 Screenshot。我哪里会出错?我需要你的帮助。谢谢。
这是我的 SurfaceView 代码:
public class PainterView extends SurfaceView implements SurfaceHolder.Callback {
private PainterThread painterThread;
private BrushParameters brushParameters;
private Bitmap bitmap;
public PainterView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
brushParameters = new BrushParameters();
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
setWillNotDraw(false);
getThread().setRunning(true);
getThread().start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int width, int height) {
if (bitmap == null) {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
getThread().setBitmap(bitmap, true);
} else {
getThread().setBitmap(bitmap, false);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
getThread().setRunning(false);
boolean retry = true;
while (retry) {
try {
getThread().join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
painterThread = null;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
painterThread.startDraw(x, y);
break;
case MotionEvent.ACTION_MOVE:
painterThread.continueDraw(x, y);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
painterThread.finishDraw(x, y);
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
}
public BrushParameters getBrushParameters() {
return brushParameters;
}
public void setBrushColor(int color) {
brushParameters.setColor(color);
getThread().setBrushParameters(brushParameters);
}
public void setBrushSize(int size) {
brushParameters.setSize(size);
getThread().setBrushParameters(brushParameters);
}
public void setBrushAlpha(int alpha) {
brushParameters.setAlpha(alpha);
getThread().setBrushParameters(brushParameters);
}
public PainterThread getThread() {
if (painterThread == null) {
painterThread = new PainterThread(getHolder(), this);
}
return painterThread;
}
}
还有我的主题class:
public class PainterThread extends Thread {
private SurfaceHolder surfaceHolder;
private PainterView painterView;
private boolean running = false;
private Paint paint;
private Path path;
private Bitmap mBitmap;
private Canvas mCanvas;
private float lastX, lastY;
private static float TOUCH_TOLERANCE = 4;
public PainterThread(SurfaceHolder surfaceHolder, PainterView painterView) {
this.surfaceHolder = surfaceHolder;
this.painterView = painterView;
path = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.BLACK);
paint.setAlpha(255);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
public void setRunning(boolean running) {
this.running = running;
}
@Override
public void run() {
Canvas mCanvas;
while (running) {
mCanvas = null;
try {
mCanvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
if (mCanvas != null) {
mCanvas.drawBitmap(mBitmap, 0, 0, paint);
painterView.postInvalidate();
}
}
} finally {
if (mCanvas != null) {
surfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
public void setBitmap(Bitmap bitmap, boolean clear) {
mBitmap = bitmap;
if (clear) {
mBitmap.eraseColor(Color.WHITE);
}
mCanvas = new Canvas(mBitmap);
}
public void setBrushParameters(BrushParameters brushParameters) {
paint.setColor(brushParameters.getColor());
paint.setAlpha(brushParameters.getAlpha());
paint.setStrokeWidth(brushParameters.getSize());
}
public void startDraw(float x, float y) {
path.reset();
path.moveTo(x, y);
lastX = x;
lastY = y;
}
public void continueDraw(float x, float y) {
float dx = Math.abs(x - lastX);
float dy = Math.abs(y - lastY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
path.quadTo(lastX, lastY, (x + lastX) / 2, (y + lastY) / 2);
mCanvas.drawPath(path, paint);
lastX = x;
lastY = y;
}
}
public void finishDraw(float x, float y) {
path.moveTo(x, y);
mCanvas.drawPath(path, paint);
}
}
感谢您的帮助。我花了几天时间才找到问题的原因...
我建议摆脱 SurfaceView。你有一些看起来很混乱的代码(例如 setBitmap()
设置 mCanvas
,但它在循环中被 run()
覆盖),我认为你只是让自己的生活更艰难.
SurfaceView 有两部分,Surface 和 View。 Surface 是一个单独的层(默认情况下)位于 View 层的后面。 SurfaceView 的 View 部分通常只是一个透明孔,可以让你 "see through" 到后面的 Surface 层。
在您的例子中,您已经覆盖了 View 对象中的 onDraw()
,因此您实际上是在 View 中绘图。在您的其他线程中,您正在将相同的位图绘制到表面上。即使您的位图具有透明像素,您也会看到两个相同的位图相互叠加。
看起来您正在共享位图,并且可能在两个同时执行的线程之间共享 Canvas,这是导致不愉快的原因。
如果去掉 SurfaceView,只使用 custom View,我认为一切都会更有意义。另一种方法是摆脱 onDraw()
和对 postInvalidate()
的调用并在 Surface 上完成所有操作,但要利用硬件加速渲染,最好使用自定义视图。
我的绘图应用程序有问题。我需要抓住正确的触摸屏并将它们绘制到 canvas。更改画笔的大小工作正常。但是当我更改透明度设置时,程序无法正常运行。它在之前的路径之上强加了一条新路径,并且失去了透明度。 Screenshot。我哪里会出错?我需要你的帮助。谢谢。
这是我的 SurfaceView 代码:
public class PainterView extends SurfaceView implements SurfaceHolder.Callback {
private PainterThread painterThread;
private BrushParameters brushParameters;
private Bitmap bitmap;
public PainterView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
brushParameters = new BrushParameters();
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
setWillNotDraw(false);
getThread().setRunning(true);
getThread().start();
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int width, int height) {
if (bitmap == null) {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
getThread().setBitmap(bitmap, true);
} else {
getThread().setBitmap(bitmap, false);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
getThread().setRunning(false);
boolean retry = true;
while (retry) {
try {
getThread().join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
painterThread = null;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
painterThread.startDraw(x, y);
break;
case MotionEvent.ACTION_MOVE:
painterThread.continueDraw(x, y);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
painterThread.finishDraw(x, y);
break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
}
public BrushParameters getBrushParameters() {
return brushParameters;
}
public void setBrushColor(int color) {
brushParameters.setColor(color);
getThread().setBrushParameters(brushParameters);
}
public void setBrushSize(int size) {
brushParameters.setSize(size);
getThread().setBrushParameters(brushParameters);
}
public void setBrushAlpha(int alpha) {
brushParameters.setAlpha(alpha);
getThread().setBrushParameters(brushParameters);
}
public PainterThread getThread() {
if (painterThread == null) {
painterThread = new PainterThread(getHolder(), this);
}
return painterThread;
}
}
还有我的主题class:
public class PainterThread extends Thread {
private SurfaceHolder surfaceHolder;
private PainterView painterView;
private boolean running = false;
private Paint paint;
private Path path;
private Bitmap mBitmap;
private Canvas mCanvas;
private float lastX, lastY;
private static float TOUCH_TOLERANCE = 4;
public PainterThread(SurfaceHolder surfaceHolder, PainterView painterView) {
this.surfaceHolder = surfaceHolder;
this.painterView = painterView;
path = new Path();
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.BLACK);
paint.setAlpha(255);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
}
public void setRunning(boolean running) {
this.running = running;
}
@Override
public void run() {
Canvas mCanvas;
while (running) {
mCanvas = null;
try {
mCanvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
if (mCanvas != null) {
mCanvas.drawBitmap(mBitmap, 0, 0, paint);
painterView.postInvalidate();
}
}
} finally {
if (mCanvas != null) {
surfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
public void setBitmap(Bitmap bitmap, boolean clear) {
mBitmap = bitmap;
if (clear) {
mBitmap.eraseColor(Color.WHITE);
}
mCanvas = new Canvas(mBitmap);
}
public void setBrushParameters(BrushParameters brushParameters) {
paint.setColor(brushParameters.getColor());
paint.setAlpha(brushParameters.getAlpha());
paint.setStrokeWidth(brushParameters.getSize());
}
public void startDraw(float x, float y) {
path.reset();
path.moveTo(x, y);
lastX = x;
lastY = y;
}
public void continueDraw(float x, float y) {
float dx = Math.abs(x - lastX);
float dy = Math.abs(y - lastY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
path.quadTo(lastX, lastY, (x + lastX) / 2, (y + lastY) / 2);
mCanvas.drawPath(path, paint);
lastX = x;
lastY = y;
}
}
public void finishDraw(float x, float y) {
path.moveTo(x, y);
mCanvas.drawPath(path, paint);
}
}
感谢您的帮助。我花了几天时间才找到问题的原因...
我建议摆脱 SurfaceView。你有一些看起来很混乱的代码(例如 setBitmap()
设置 mCanvas
,但它在循环中被 run()
覆盖),我认为你只是让自己的生活更艰难.
SurfaceView 有两部分,Surface 和 View。 Surface 是一个单独的层(默认情况下)位于 View 层的后面。 SurfaceView 的 View 部分通常只是一个透明孔,可以让你 "see through" 到后面的 Surface 层。
在您的例子中,您已经覆盖了 View 对象中的 onDraw()
,因此您实际上是在 View 中绘图。在您的其他线程中,您正在将相同的位图绘制到表面上。即使您的位图具有透明像素,您也会看到两个相同的位图相互叠加。
看起来您正在共享位图,并且可能在两个同时执行的线程之间共享 Canvas,这是导致不愉快的原因。
如果去掉 SurfaceView,只使用 custom View,我认为一切都会更有意义。另一种方法是摆脱 onDraw()
和对 postInvalidate()
的调用并在 Surface 上完成所有操作,但要利用硬件加速渲染,最好使用自定义视图。