为了制作一个简单的游戏,我使用了一个模板,该模板使用这样的位图绘制画布:
private void doDraw(Canvas canvas) {
for (int i=0;i<8;i++)
for (int j=0;j<9;j++)
for (int k=0;k<7;k++) {
canvas.drawBitmap(mBits[allBits[i][j][k]], i*50 -k*7, j*50 -k*7, null); } }
(画布在“run()”中定义/SurfaceView 位于 GameThread 中。)
我的第一个问题是如何清除(或重绘)整个画布以进行新布局?其次,我怎样才能只更新屏幕的一部分?
// This is the routine that calls "doDraw":
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING)
updateGame();
doDraw(c); }
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c); } } } }
使用 PorterDuff 清除模式绘制透明颜色可以达到我想要的效果。
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
如何清除(或重绘)整个画布以获得新布局(= 尝试游戏)?
只需调用 Canvas.drawColor(Color.BLACK)
或您想用任何颜色清除 Canvas
即可。
并且:我怎样才能只更新屏幕的一部分?
没有这种方法可以只更新“屏幕的一部分”,因为 Android 操作系统在更新屏幕时会重绘每个像素。但是,当您不清除 Canvas
上的旧绘图时,旧绘图仍会显示在表面上,这可能是“仅更新一部分”屏幕的一种方式。
因此,如果您想“更新屏幕的一部分”,请避免调用 Canvas.drawColor()
方法。
mSurfaceHolder.unlockCanvasAndPost(c)
,然后再调用 c = mSurfaceHolder.lockCanvas(null)
,那么新的 c
与之前的 c
包含的内容不同。您不能只更新 SurfaceView 的一部分,我猜这是 OP 所要求的。
Canvas
保留旧图纸。
在谷歌群组中找到了这个,这对我有用..
Paint clearPaint = new Paint();
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, clearPaint);
这将删除绘图矩形等,同时保持设置位图..
使用 Path 类的 reset 方法
Path.reset();
我尝试了@mobistry 的答案:
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
但这对我不起作用。
对我来说,解决方案是:
canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);
也许有人有同样的问题。
mBitmap.eraseColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
请在surfaceview扩展类构造函数上粘贴下面的代码............
构造函数编码
SurfaceHolder holder = getHolder();
holder.addCallback(this);
SurfaceView sur = (SurfaceView)findViewById(R.id.surfaceview);
sur.setZOrderOnTop(true); // necessary
holder = sur.getHolder();
holder.setFormat(PixelFormat.TRANSPARENT);
xml编码
<com.welcome.panelview.PanelViewWelcomeScreen
android:id="@+id/one"
android:layout_width="600px"
android:layout_height="312px"
android:layout_gravity="center"
android:layout_marginTop="10px"
android:background="@drawable/welcome" />
试试上面的代码...
这是一个最小示例的代码,显示您总是必须在每一帧重绘 Canvas 的每个像素。
这个活动每秒在 SurfaceView 上绘制一个新的 Bitmap,之前没有清屏。如果你测试一下,你会发现位图并不总是写入同一个缓冲区,屏幕会在两个缓冲区之间交替显示。
我在我的手机(Nexus S,Android 2.3.3)和模拟器(Android 2.2)上测试了它。
public class TestCanvas extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new TestView(this));
}
}
class TestView extends SurfaceView implements SurfaceHolder.Callback {
private TestThread mThread;
private int mWidth;
private int mHeight;
private Bitmap mBitmap;
private SurfaceHolder mSurfaceHolder;
public TestView(Context context) {
super(context);
mThread = new TestThread();
mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon);
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
mWidth = width;
mHeight = height;
mThread.start();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {/* Do nothing */}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread != null && mThread.isAlive())
mThread.interrupt();
}
class TestThread extends Thread {
@Override
public void run() {
while (!isInterrupted()) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
c.drawBitmap(mBitmap, (int) (Math.random() * mWidth), (int) (Math.random() * mHeight), null);
}
} finally {
if (c != null)
mSurfaceHolder.unlockCanvasAndPost(c);
}
try {
sleep(1000);
} catch (InterruptedException e) {
interrupt();
}
}
}
}
}
SurfaceHolder.Callback
,而不仅仅是 Callback
。
Canvas
。这使得我关于“以前的图纸”的说法是正确的。 Canvas
保留以前的绘图。
Canvas
会保留以前的图纸。但这完全没用,问题是当你使用 lockCanvas()
时,你不知道那些“先前的绘图”是什么,并且无法假设它们。也许如果有两个具有相同大小的 SurfaceView 的活动,它们将共享相同的 Canvas。也许您会得到一大块未初始化的 RAM,其中包含随机字节。也许总有写在画布上的谷歌标志。你无法知道。 在 lockCanvas(null)
之后未绘制每个像素的任何应用程序都会损坏。
对我来说,调用 Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
或类似的东西只有在我触摸屏幕后才会起作用。所以我会调用上面的代码行,但只有在我触摸屏幕后屏幕才会清除。所以对我有用的是调用 invalidate()
,然后调用 init()
,它在创建时调用以初始化视图。
private void init() {
setFocusable(true);
setFocusableInTouchMode(true);
setOnTouchListener(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(6);
mCanvas = new Canvas();
mPaths = new LinkedList<>();
addNewPath();
}
在 java android 中擦除 Canvas 类似于通过 javascript 使用 globalCompositeOperation 擦除 HTML Canvas。逻辑是相似的。
U 将选择 DST_OUT(目的地输出)逻辑。
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
注意:DST_OUT 更有用,因为如果油漆颜色有 50% 的 alpha,它可以擦除 50%。因此,要完全清除到透明,颜色的 alpha 必须为 100%。建议应用paint.setColor(Color.WHITE)。并确保画布图像格式为 RGBA_8888。
擦除后,使用 SRC_OVER (Source Over) 回到正常绘图。
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
从字面上更新小区域显示将需要访问图形硬件,并且可能不受支持。
最接近最高性能的是使用多图像层。
使用以下方法,您可以清除整个画布或只是其中的一部分。
请不要忘记禁用硬件加速,因为 PorterDuff.Mode.CLEAR 不适用于硬件加速和最后调用 setWillNotDraw(false)
,因为我们覆盖了 onDraw
方法。
//view's constructor
setWillNotDraw(false);
setLayerType(LAYER_TYPE_SOFTWARE, null);
//view's onDraw
Paint TransparentPaint = new Paint();
TransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, TransparentPaint);
LAYER_TYPE_HARDWARE
更改为 LAYER_TYPE_SOFTWARE
不要忘记调用 invalidate();
canvas.drawColor(backgroundColor);
invalidate();
path.reset();
invalidate
?
您的第一个要求,如何清除或重绘整个画布 - 答案 - 使用 canvas.drawColor(color.Black) 方法以黑色或您指定的任何颜色清除屏幕。
您的第二个要求,如何更新屏幕的一部分 - 回答 - 例如,如果您想在屏幕上保持所有其他内容不变,但在屏幕的一小块区域中显示一个整数(比如计数器),该整数每五秒增加一次。然后使用 canvas.drawrect 方法通过指定左上右下并绘制来绘制那个小区域。然后计算您的计数器值(使用 postdalayed 5 秒等,就像 Handler.postDelayed(Runnable_Object, 5000);) ,将其转换为文本字符串,计算这个小矩形中的 x 和 y 坐标并使用文本视图显示变化计数器值。
尝试在活动的 onPause() 删除视图并添加 onRestart()
LayoutYouAddedYourView.addView(YourCustomView); LayoutYouAddedYourView.removeView(YourCustomView);
添加视图的那一刻,将调用 onDraw() 方法。
YourCustomView,是一个扩展 View 类的类。
就我而言,我将画布绘制成线性布局。要再次清理和重绘:
LinearLayout linearLayout = findViewById(R.id.myCanvas);
linearLayout.removeAllViews();
然后,我用新值调用该类:
Lienzo fondo = new Lienzo(this,items);
linearLayout.addView(fondo);
这是 Lienzo 类:
class Lienzo extends View {
Paint paint;
RectF contenedor;
Path path;
ArrayList<Items>elementos;
public Lienzo(Context context,ArrayList<Items> elementos) {
super(context);
this.elementos=elementos;
init();
}
private void init() {
path=new Path();
paint = new Paint();
contenedor = new RectF();
paint.setStyle(Paint.Style.FILL);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
contenedor.left = oneValue;
contenedor.top = anotherValue;
contenedor.right = anotherValue;
contenedor.bottom = anotherValue;
float angulo = -90; //starts drawing at 12 o'clock
//total= sum of all element values
for (int i=0;i<elementos.size();i++){
if (elementos.get(i).angulo!=0 && elementos.get(i).visible){
paint.setColor(elementos.get(i).backColor);
canvas.drawArc(contenedor,angulo,(float)(elementos.get(i).value*360)/total,true,paint);
angulo+=(float)(elementos.get(i).value*360)/total;
}
} //for example
}
}
就我而言,每次创建画布都对我有用,即使它对内存不友好
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.image);
imageBitmap = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), bm.getConfig());
canvas = new Canvas(imageBitmap);
canvas.drawBitmap(bm, 0, 0, null);
以下对我有用:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SCREEN);
我找到了我的解决方案。
油漆视图类:
public void clear() {
mPath.reset();
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
paths.clear();
}
和 MainActivity:
clear_canvas_.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
paintView.clear();
}
});
我不得不使用单独的绘图通道来清除画布(锁定、绘制和解锁):
Canvas canvas = null;
try {
canvas = holder.lockCanvas();
if (canvas == null) {
// exit drawing thread
break;
}
canvas.drawColor(colorToClearFromCanvas, PorterDuff.Mode.CLEAR);
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
打电话
canvas.drawColor(颜色。透明)
Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
。如果我没记错的话,颜色实际上可以是任何东西(不必是透明的),因为 PorterDuff.Mode.CLEAR
只会清除当前剪辑(就像在画布上打一个洞)。
不定期副业成功案例分享
Bitmap#eraseColor(Color.TRANSPARENT)
,如下面的 HeMac 回答中所示。Color.TRANSPARENT
是不必要的。PorterDuff.Mode.CLEAR
对于ARGB_8888
位图来说已经足够了,这意味着将 alpha 和颜色设置为 [0, 0]。另一种方法是将Color.TRANSPARENT
与PorterDuff.Mode.SRC
一起使用。