Android:移动 bmp 留下痕迹(表面视图和 Canvas)
Android : moving bmp leaving a trail ( Surface View and Canvas )
我正在尝试为 android 应用制作精灵动画,但遇到了一个小问题。我在我添加到现有布局之上的 surfaceView 上绘图。在这个 surfaceView 上,我想为几个精灵设置动画,以便它们沿着路径行走。
这就是我现在面临的结果:
行走的精灵正在留下痕迹。所以我决定 google 这个问题,显然我必须先清除 canvas 才能使用它。
这是我之前的 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);
}
这是我在
之后的 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);
}
所以我现在有了这个结果
这很棒,因为它没有留下更多痕迹,但我看不到下面的布局。无论如何我可以两全其美吗?我认为清除 canvas 会起作用,但它显然没有按预期起作用。
我会post下面的代码
表面视图:
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);
}
线程:
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;
}
雪碧:
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);
}
所以在绘制精灵之前,您必须先将背景位图绘制到表面上。现在您正在绘制覆盖背景布局的黑色。
或者,您可以使用具有 paint.setColor 到 Color.Transparent 的 Paint canvas.drawRect。
有很多方法可以处理这个问题,但一般来说,方法是维护一个背景层(这只是另一个图像)和精灵层。不是清除,而是在每一帧将你的背景 blit(复制)到表面上(这会擦除所有内容),然后你 blit 你的精灵。
您正在使用 setZOrderOnTop()
,它将 SurfaceView 的 Surface 部分放在其他所有内容之上的层上。 (默认情况下,它是其他所有内容下面的单独图层。)当您使用 canvas.drawRGB(0, 0, 0)
清除它时,您将整个图层设置为不透明的黑色,这将掩盖它下面的视图图层。
如果您使用 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
将其清除为透明黑色,您应该会得到想要的结果。
FWIW,如果您只是在 Surface 上绘图,则不应覆盖 onDraw()
。 onDraw()
方法由 View 层级调用,用于在 SurfaceView 的 View 部分上绘制。如果某事设法使 SurfaceView 的视图无效,您的 onDraw()
将被调用,并且您最终会在视图层上得到一个角色精灵。 (根据您的布局,这可能不可见。)只需为该方法指定一个不同的名称即可。
我正在尝试为 android 应用制作精灵动画,但遇到了一个小问题。我在我添加到现有布局之上的 surfaceView 上绘图。在这个 surfaceView 上,我想为几个精灵设置动画,以便它们沿着路径行走。
这就是我现在面临的结果:
行走的精灵正在留下痕迹。所以我决定 google 这个问题,显然我必须先清除 canvas 才能使用它。
这是我之前的 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);
}
这是我在
之后的 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);
}
所以我现在有了这个结果
这很棒,因为它没有留下更多痕迹,但我看不到下面的布局。无论如何我可以两全其美吗?我认为清除 canvas 会起作用,但它显然没有按预期起作用。
我会post下面的代码
表面视图:
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);
}
线程:
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;
}
雪碧:
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);
}
所以在绘制精灵之前,您必须先将背景位图绘制到表面上。现在您正在绘制覆盖背景布局的黑色。
或者,您可以使用具有 paint.setColor 到 Color.Transparent 的 Paint canvas.drawRect。
有很多方法可以处理这个问题,但一般来说,方法是维护一个背景层(这只是另一个图像)和精灵层。不是清除,而是在每一帧将你的背景 blit(复制)到表面上(这会擦除所有内容),然后你 blit 你的精灵。
您正在使用 setZOrderOnTop()
,它将 SurfaceView 的 Surface 部分放在其他所有内容之上的层上。 (默认情况下,它是其他所有内容下面的单独图层。)当您使用 canvas.drawRGB(0, 0, 0)
清除它时,您将整个图层设置为不透明的黑色,这将掩盖它下面的视图图层。
如果您使用 canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
将其清除为透明黑色,您应该会得到想要的结果。
FWIW,如果您只是在 Surface 上绘图,则不应覆盖 onDraw()
。 onDraw()
方法由 View 层级调用,用于在 SurfaceView 的 View 部分上绘制。如果某事设法使 SurfaceView 的视图无效,您的 onDraw()
将被调用,并且您最终会在视图层上得到一个角色精灵。 (根据您的布局,这可能不可见。)只需为该方法指定一个不同的名称即可。