Android,canvas:如何清除(删除内容)画布(= bitmaps),生活在surfaceView中?

时间:2011-04-20 11:19:35

标签: android android-canvas surfaceview

为了制作一个简单的游戏,我使用了一个模板来绘制带有这样位图的画布:

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

19 个答案:

答案 0 :(得分:265)

使用PorterDuff清晰模式绘制透明色可以实现我想要的技巧。

Canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)

答案 1 :(得分:71)

  

如何为新布局清除(或重绘)WHOLE画布(=在游戏中尝试)?

只需拨打Canvas.drawColor(Color.BLACK)或您想要清除Canvas的任何颜色。

  

而且:我怎样才能更新屏幕的一部分?

由于Android OS在更新屏幕时重新绘制每个像素,因此没有这样的方法只更新“屏幕的一部分”。但是,当你没有清除Canvas上的旧图纸时,旧图纸仍然在表面上,这可能只是“更新屏幕的一部分”的一种方式。

因此,如果您想“更新屏幕的一部分”,请避免调用Canvas.drawColor()方法。

答案 2 :(得分:28)

在谷歌小组中找到了这个,这对我有用..

Paint clearPaint = new Paint();
clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, clearPaint); 

这样可以在保持设置位图的同时删除图形矩形等。

答案 3 :(得分:17)

我尝试了@mobistry的答案:

canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);

但它对我没用。

对我而言,解决方案是:

canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);

也许有人有同样的问题。

答案 4 :(得分:12)

mBitmap.eraseColor(Color.TRANSPARENT);

canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);

答案 5 :(得分:12)

使用Path类的重置方法

Path.reset();

答案 6 :(得分:4)

请在surfaceview extend class constructor上粘贴下面的代码.............

构造函数编码

    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" />

尝试以上代码......

答案 7 :(得分:3)

以下是最小示例的代码,显示您必须在每帧重绘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();
                }
            }
        }   
    }
}

答案 8 :(得分:3)

对我来说,呼叫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();
}

答案 9 :(得分:2)

canvas.drawColor(Color.TRANSPARENT, Mode.MULTIPLY);

答案 10 :(得分:1)

在Java android中的Canvas上擦除与通过globalCompositeOperation通过javascript擦除HTML Canvas相似。逻辑相似。

U将选择DST_OUT(目标输出)逻辑。

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

注意:DST_OUT更加有用,因为如果绘画颜色的alpha值为50%,则DST_OUT可以擦除50%。因此,要完全清除为透明,颜色的Alpha必须为100%。建议应用paint.setColor(Color.WHITE)。并确保画布图像格式为RGBA_8888。

擦除后,使用SRC_OVER(源结束)返回正常绘图。

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));

实际上,更新小区域显示将需要访问图形硬件,并且可能不支持。

最接近以获得最佳性能的是使用多图像层。

答案 11 :(得分:0)

别忘了调用invalidate();

canvas.drawColor(backgroundColor);
invalidate();
path.reset();

答案 12 :(得分:0)

使用以下方法,您可以清除整个画布或其中的一部分。
请不要忘记禁用硬件加速,因为 PorterDuff.Mode.CLEAR 不适用于硬件加速,并最终调用setWillNotDraw(false),因为我们重写了onDraw方法。

//view's constructor
setWillNotDraw(false);
setLayerType(LAYER_TYPE_HARDWARE, null);

//view's onDraw
Paint TransparentPaint = new Paint();
TransparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRect(0, 0, width, height, TransparentPaint); 

答案 13 :(得分:0)

尝试删除活动的onPause()视图并添加onRestart()

LayoutYouAddedYourView.addView(YourCustomView); LayoutYouAddedYourView.removeView(YourCustomView);

添加视图后,onDraw()方法将被调用。

YourCustomView,是扩展View类的类。

答案 14 :(得分:0)

就我而言,我将画布绘制为linearlayout。 要清洁并重新绘制:

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

答案 15 :(得分:0)

就我而言,每次创建画布都对我有用,即使它对内存不友好

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

答案 16 :(得分:-1)

您的第一个要求,如何清除或重绘整个画布 - 答案 - 使用canvas.drawColor(color.Black)方法清除屏幕上的颜色为黑色或您指定的任何内容。

你的第二个要求,如何更新部分屏幕 - 答案 - 例如,如果你想在屏幕上保持所有其他内容不变,但在屏幕的一小块区域显示一个整数(比如计数器),每个都会增加五秒钟然后使用canvas.drawrect方法通过指定左上角的底部和绘制来绘制该小区域。然后计算你的计数器值(使用postdalayed 5秒等,llike Handler.postDelayed(Runnable_Object,5000);),将其转换为文本字符串,计算这个小矩形中的x和y坐标,并使用文本视图显示更改反价值。

答案 17 :(得分:-1)

我必须使用单独的绘图通道来清除画布(锁定,绘制和解锁):

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

答案 18 :(得分:-3)

致电

canvas.drawColor(Color.TRANSPARENT)