简体   繁体   English

Android:移动bmp留下痕迹(Surface View和Canvas)

[英]Android : moving bmp leaving a trail ( Surface View and Canvas )

I was trying to animate a sprite for an android app and i've encounter a small problem. 我正在尝试为Android应用程序的精灵设置动画,但是遇到一个小问题。 I'm drawing on a surfaceView that i add on top of already existing layouts. 我正在一个SurfaceView上绘制,该视图添加到已经存在的布局之上。 On this surfaceView, i wanted to animate few sprites so they would walk along a path. 在这个surfaceView上,我想为几个精灵设置动画,以便它们沿着路径行走。

So this is the result i'm facing right now : 所以这就是我现在面临的结果: 在此处输入图片说明

The walking sprite is leaving a trail. 行走的精灵正在留下痕迹。 So i decided to google this problem and apparently i had to clear the canvas first before drawing on it. 所以我决定用谷歌搜索这个问题,显然我必须在画上画布之前先清除它。
This was my onDraw method before : 这是我之前的onDraw方法:

public void onDraw(Canvas canvas) {
    update();
    int srcX = currentFrame * width;
    int srcY = directionX * height;
    Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
    Rect dst = new Rect(x, y, x + width, y + height);
    canvas.drawBitmap(bmp, src, dst, null);

}

And this was my onDraw method after 这是我之后的onDraw方法

public void onDraw(Canvas canvas) {
    canvas.drawRGB(0, 0, 0);
    update();
    int srcX = currentFrame * width;
    int srcY = directionX * height;
    Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
    Rect dst = new Rect(x, y, x + width, y + height);
    canvas.drawBitmap(bmp, src, dst, null);

}

So i now have this as a result 所以我现在有了这个

在此处输入图片说明

which is great because it leaves no more trail BUT i cant see the layouts underneath. 这很棒,因为它不再留下任何痕迹,但我看不到下面的布局。 Is there anyway i can get the best of both world ? 无论如何,我能同时获得两全其美吗? I thought clearing the canvas would work but it obviously doesnt not work as expected. 我认为清理画布会起作用,但显然不能按预期工作。

I'll post my code below 我将在下面发布我的代码

SurfaceView : SurfaceView:

public class MonsterView extends SurfaceView {

private Bitmap monsterImg;
private SurfaceHolder holder;
private MonsterThread mainThread;
private Sprite monsterSprite;
private int x;
private int xSpeed = 1;

public MonsterView(Context context) {
    super(context);
    this.mainThread = new MonsterThread(this);
    this.x = 0;


    holder = getHolder();
    holder.setFormat(PixelFormat.TRANSPARENT);

    holder.addCallback(new SurfaceHolder.Callback() {

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            mainThread.setRunning(true);
            mainThread.start();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            boolean retry = true;
            mainThread.setRunning(false);
            while (retry) {
                try {
                    mainThread.join();
                    retry = false;
                } catch (InterruptedException e) {
                }
            }

        }
    });
    setZOrderOnTop(true);
    monsterImg = BitmapFactory.decodeResource(getResources(), R.drawable.fire);
    monsterSprite = new Sprite(this,monsterImg);
}

@Override
public void onDraw(Canvas canvas) {
    if (x == getWidth() - monsterImg.getWidth()) {
        xSpeed = -1;
    }
    if (x == 0) {
        xSpeed = 1;
    }
    x = x + xSpeed;
    monsterSprite.onDraw(canvas);
}

Thread : 线程:

public class MonsterThread extends Thread {
private MonsterView monsterSurface;
private boolean running;
static final long FPS = 35;

public MonsterThread(MonsterView monsterSurface){
    this.monsterSurface = monsterSurface;
    this.running = false;
}

@Override
public void run() {
    long ticksPS = 1000 / FPS;
    long startTime;
    long sleepTime;

    while(running){
        Canvas c = null;
        startTime = System.currentTimeMillis();
        try{
            c = monsterSurface.getHolder().lockCanvas();
            synchronized (monsterSurface.getHolder()){

                monsterSurface.onDraw(c);
            }
        } finally {
            if( c!= null){
                monsterSurface.getHolder().unlockCanvasAndPost(c);

            }
        }
        sleepTime = ticksPS-(System.currentTimeMillis() - startTime);
        try {
            if(sleepTime > 0)
                sleep(sleepTime);
            else
                sleep(10);
        } catch (Exception e){}
    }
}

public MonsterView getMonsterSurface() {
    return monsterSurface;
}

public void setMonsterSurface(MonsterView monsterSurface) {
    this.monsterSurface = monsterSurface;
}

public boolean isRunning() {
    return running;
}

public void setRunning(boolean running) {
    this.running = running;
}

Sprite : 雪碧

public class Sprite {
private static final int BMP_ROWS = 4;
private static final int BMP_COLUMNS = 3;
private int x = 0;
private int y = 0;
private int xSpeed = 5;
private MonsterView monsterView;
private Bitmap bmp;
private int currentFrame = 0;
private int width;
private int height;

private int directionX;

public Sprite(MonsterView monsterView, Bitmap bmp) {
    this.monsterView = monsterView;
    this.bmp=bmp;
    this.width = bmp.getWidth() / BMP_COLUMNS;
    this.height = bmp.getHeight() / BMP_ROWS;
    this.directionX = 2;
}

private void update() {
    if (x > monsterView.getWidth() - width - xSpeed) {
        xSpeed = -5;
        directionX = 1;
    }
    if (x + xSpeed < 0) {
        xSpeed = 5;
        directionX = 2;
    }
    x = x + xSpeed;
    currentFrame = ++currentFrame % BMP_COLUMNS;
    Log.d("test", ""+currentFrame);
}

public void onDraw(Canvas canvas) {
    update();
    int srcX = currentFrame * width;
    int srcY = directionX * height;
    Rect src = new Rect(srcX, srcY, srcX + width, srcY + height);
    Rect dst = new Rect(x, y, x + width, y + height);
    canvas.drawBitmap(bmp, src, dst, null);

}

You're using setZOrderOnTop() , which is putting the Surface part of the SurfaceView on a layer above everything else. 您正在使用setZOrderOnTop() ,它会将SurfaceView的Surface部分放在其他所有图层之上。 (By default, it's a separate layer below everything else.) When you clear it with canvas.drawRGB(0, 0, 0) you're setting the entire layer to opaque black, which is going to obscure the View layer beneath it. (默认情况下,它是其他所有之下的单独层。)使用canvas.drawRGB(0, 0, 0) )清除它时, canvas.drawRGB(0, 0, 0)整个层设置为不透明黑色,这将使下面的View层模糊。

If instead you clear it to transparent black, with canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) , you should get the results you want. 相反,如果使用canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)其清除为透明黑色,则应该获得所需的结果。

FWIW, you shouldn't override onDraw() if you're only drawing on the Surface. FWIW,如果仅在Surface上绘制,则不应覆盖onDraw() The onDraw() method is called by the View hierarchy to draw on the View part of the SurfaceView. View层次结构调用onDraw()方法以在SurfaceView的View部分上进行绘制。 If something manages to invalidate the SurfaceView's View, your onDraw() will be called, and you'll end up with a character sprite on the View layer. 如果某种方法使SurfaceView的View无效,则将调用onDraw() ,最后在View层上显示一个字符精灵。 (Depending on your layout this may not be visible.) Just give the method a different name. (根据您的布局,这可能不可见。)只需为该方法指定其他名称即可。

There are a number of ways to handle this, but generally, the way to do this is by maintaining a background layer (which is just another image), and sprite layers. 有多种方法可以解决此问题,但是通常,通过维护背景层(仅是另一幅图像)和子画面层来实现此目的。 Instead of clearing, at each frame you blit (copy) your background onto the surface (which erases everything), then you blit your sprites. 无需清除,而是在每一帧上将背景变亮(复制)到表面上(这会擦除所有内容),然后使精灵变亮。

So you're going to have to draw the background bitmap onto the surface first before drawing your sprite. 因此,在绘制精灵之前,您必须先在背景上绘制背景位图。 Right now you're drawing black which is covering your background layouts. 现在,您正在绘制黑色,这覆盖了您的背景布局。

Alternatively you might be able to do canvas.drawRect with a Paint with paint.setColor to Color.Transparent. 或者,您也可以使用Paint.setColor设置为Color.Transparent的Paint来进行canvas.drawRect。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM