使用SurfaceView和Thread在Android上绘制图形

10

我正在尝试使用3个类将一个球绘制到屏幕上。我已经了解了一些关于这方面的知识,并找到了一个代码片段,可以在一个页面上使用这3个类,在Android中使用图形

我修改了代码,以便我有一个移动的球,并且当撞击墙壁时会改变方向,就像下面的图片一样(这是使用链接中的代码)

moving ball screenshot

现在,我想将这3个类分别放在3个不同的页面中,以免让所有内容都太拥挤,而所有设置都是相同的。

这是我所拥有的3个类。

  1. BallActivity.java
  2. Ball.java
  3. BallThread.java

package com.brick.breaker;
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;


public class BallActivity extends Activity {

private Ball ball;

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

    ball = new Ball(this);
    setContentView(ball);
}

@Override
protected void onPause() {

    super.onPause();

    setContentView(null);
    ball = null;

    finish();
}

}

package com.brick.breaker;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class Ball extends SurfaceView implements SurfaceHolder.Callback {

private BallThread ballThread = null;

private Bitmap bitmap;

private float x, y;
private float vx, vy;

public Ball(Context context) {
    super(context);

    bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ball);

    x = 50.0f;
    y = 50.0f;

    vx = 10.0f;
    vy = 10.0f;

    getHolder().addCallback(this);
    ballThread = new BallThread(getHolder(), this);
}

protected void onDraw(Canvas canvas) {

    update(canvas);

    canvas.drawBitmap(bitmap, x, y, null);
}

public void update(Canvas canvas) {

    checkCollisions(canvas);

    x += vx;
    y += vy;
}

public void checkCollisions(Canvas canvas) {

    if(x - vx < 0) {

        vx = Math.abs(vx);

    } else if(x + vx > canvas.getWidth() - getBitmapWidth()) {

        vx = -Math.abs(vx);
    }

    if(y - vy < 0) {

        vy = Math.abs(vy);

    } else if(y + vy > canvas.getHeight() - getBitmapHeight()) {

        vy = -Math.abs(vy);
    }
}

public int getBitmapWidth() {

    if(bitmap != null) {

        return bitmap.getWidth();

    } else {

        return 0;
    }
}

public int getBitmapHeight() {

    if(bitmap != null) {

        return bitmap.getHeight();

    } else {

        return 0;
    }
}

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

}

public void surfaceCreated(SurfaceHolder holder) {

    ballThread.setRunnable(true);
    ballThread.start();

}

public void surfaceDestroyed(SurfaceHolder holder) {

    boolean retry = true;
    ballThread.setRunnable(false);

    while(retry) {

        try {

            ballThread.join();
            retry = false;

        } catch(InterruptedException ie) {

            //Try again and again and again
        }

        break;
    }

    ballThread = null;

}

}

package com.brick.breaker;

import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class BallThread extends Thread {

private SurfaceHolder sh;
private Ball ball;

private Canvas canvas;

private boolean run = false;

public BallThread(SurfaceHolder _holder,Ball _ball) {

    sh = _holder;
    ball = _ball;
}

public void setRunnable(boolean _run) {

    run = _run;
}

public void run() {

    while(run) {

        canvas = null;

        try {

            canvas = sh.lockCanvas(null);

            synchronized(sh) {

                ball.onDraw(canvas);
            }

        } finally {

            if(canvas != null) {

                sh.unlockCanvasAndPost(canvas);
            }

        }

    }
}

public Canvas getCanvas() {

    if(canvas != null) {

        return canvas;

    } else {

        return null;
    }
}
}

这里有一张展示这些类的结果的图片。

enter image description here

我尝试着找出其中的问题,但由于我对Android开发还比较新,因此想要请求帮助。

有人知道是什么导致小球绘制成那样吗?代码跟链接中的基本相同,我已经尝试过进行实验以找到解决方案但都没有成功。

5个回答

15

你只画了球,在每次画球之前,你需要重新绘制一个黑色背景(或者任何你想要的颜色)。

另外,你也可以仅在上一个位置绘制黑色区域,但是当你使用更多对象时,可能会出现问题。

这里有一个不错的示例,与你所做的类似。


是的,这解决了我的问题,在onDraw方法中,我添加了一行canvas.drawColor(Color.BLACK);在绘制球之前,这样屏幕就会被黑色填充,消除了不必要的绿色球闪烁。非常感谢=) - Morten Høgseth
顺便说一句,为了实现更加流畅和快速的动画效果,我建议使用OpenGL。Canvas仅适用于简单的操作,特别是现在大多数设备仍然没有GPU来协助图形处理,因为它们大多数仍然运行在“旧”的Android版本(姜饼)。 - android developer
非常好的建议=)我会尝试上面的代码,然后创建一个新项目并使用OpenGL执行相同的逻辑。谢谢提示:) - Morten Høgseth
有些人喜欢使用第三方库来进行动画和游戏开发,比如libgdx(它也可以在桌面上运行!)和andengine,这些库都使用OpenGL。 - android developer
另外,从蜂巢开始,替代OpenGL的另一个选择是RenderScript:http://android-developers.blogspot.co.il/2011/02/introducing-renderscript.html http://developer.android.com/guide/topics/renderscript/index.html - android developer

1
一个快速的检查,我必须说你只是在同一表面上绘制,从未请求你的SurfaceView重新绘制。在finally块的末尾,在IF语句中使用:postInvalidate(); 这应该会导致SurfaceView重新绘制自己。

1

放置这个

public void onDraw(Canvas canvas){
   canvas.drawColor(Color.BLACK);

.....

}

0

-1

[编辑]答案是错误的,但评论很有帮助,所以我会保留这个答案:

虽然这不是你问的问题,但你的代码存在一个问题。在Android中,你只能在UI线程中写入屏幕。这是运行所有Activity回调等的线程。通过从BallThread向屏幕写入,你可能会在程序中遇到许多奇怪的故障。


2
SurfaceView 可以从另一个线程更新 - http://developer.android.com/reference/android/view/SurfaceView.html - Joseph Earl
谢谢。学到了新东西。 - Dale Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接