Android SurfaceView 使用双缓冲机制_双缓冲优化之只改变动态精灵_ Handler线程外通信_弹出对话框与提示信息

本文介绍SurfaceView中双缓冲机制的实现与优化,通过分离静止元素减少重绘,提高游戏性能。同时探讨Handler在多线程间的通信,实现屏幕提示与对话框的动态更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SurfaceView是游戏中最常用的组件,但是SurfaceView的动态绘制会造成闪屏、黑屏的现象,利用双缓冲可以缓解这一现象,并且这里对双缓冲就行了二次优化处理,并且Handler与其他线程的通信方式

1、普通双缓冲机制的实现
这里仅截取实现的一部分

// 游戏界面
public class GameView extends SurfaceView implements SurfaceHolder.Callback,Runnable{
    private Canvas canvas; //画布
    private Paint paint; //画笔
    private SurfaceHolder surfaceHolder;//  控制容器

    public SurfaceHolder getSurfaceHolder() {
        return surfaceHolder;
    }

    public boolean gameRunFlag;  // 子线程标志位

    private Bitmap bitmapbuffer;// '假屏幕'的'截图',缓冲下屏幕 用来画在假屏幕上,利用双缓冲,减少绘制的时候屏幕闪烁现象
    private Canvas canvasbuffer;//假的屏幕(画布) ,利用双缓冲,减少绘制的时候屏幕闪烁现象

    private Context context;    // 绘制到的目标容器

    private static GameView gameView; //动画绘制'管家',总管所有的屏幕绘制动画,可通过管家向页面提交控制的事件及结果
	//与其他线程通信
    private Handler handler = new Handler();

    //将背景的静止元素只绘画一次,下次直接拿去用即可
    private Canvas canvasback;
    private Bitmap bitmapback;

	//覆盖默认构造方法,在指定容器运行
    public GameView(Context context) {
        super(context);
        this.context=context;

        paint=new Paint();
        surfaceHolder=getHolder();

        gameRunFlag=true;
        gameView=this;

        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
         *                      屏幕缓冲                                   *
         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
        //假屏幕的图片(类似屏幕截图了),缓冲下屏幕,用来画在假屏幕上
        bitmapbuffer= Bitmap.createBitmap(Config.deviceWidth,Config.deviceHeight,Bitmap.Config.ARGB_8888);
        //假的屏幕(就是在一个画布上画了假屏幕的图片),并且把假屏幕的内容缓冲到 假屏幕图片上
        canvasbuffer=new Canvas(bitmapbuffer);

        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
         *                      游戏背景                                   *
         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
        bitmapback= Bitmap.createBitmap(Config.deviceWidth,Config.deviceHeight,Bitmap.Config.ARGB_8888);
        canvasback=new Canvas(bitmapback);

        //surfaceview 进行而外的操作用的线程
        handler= new Handler();

        //getHolder().addCallback(this);
        surfaceHolder.addCallback(this);
        //surfaceCreated(surfaceHolder);
        //Log.i("游戏界面","标志位="+gameRunFlag);
    }

双缓冲实现及优化
在普通的双缓冲中我们是将屏幕全部重新绘制一遍的,但是其实我们改变的只是一部分,而对于一些相对来说静止的如背景动画、工具栏等其实是不会改变的,我们可以把这一部分剥离出来,只绘制一次,这样就减少了绘制量,提高绘制的效率,实现如下:

//线程
    @Override
    public void run() {
        //Log.i("游戏界面","run="+gameRunFlag);

        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
         *                      游戏背景  只画一次静止的背景元素               *
         * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
        //画背景
        canvasback.drawBitmap(Config.gameImg_back,0,0,paint);
        //画工具栏
        canvasback.drawBitmap(Config.border_back,0,(Config.deviceHeight-(Config.deviceHeight/7)),paint);
        //画杀敌图标
        canvasback.drawBitmap(Config.bate_logo,Config.deviceWidth-(Config.deviceWidth-Config.NenegLiang_Context_X)*2+Config.deviceWidth/192, (float) (Config.deviceHeight/8*6.9),paint);
        //画退出按钮
        canvasback.drawBitmap(Config.gema_over,Config.game_over_X ,Config.game_over_Y,paint);

        /*
            动态元素绘制
         */
        while(gameRunFlag){
            synchronized (surfaceHolder){//线程同步锁
                try{
                    //锁住画布
                    canvas=surfaceHolder.lockCanvas();
                    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
                     *                     屏幕缓冲,把内容先缓冲到假屏幕里                 *
                     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
                    //清空屏幕  画背景
                    canvasbuffer.drawBitmap(bitmapback,0,0,null);
                    
                    //画能量图标 和 能量值
                    paint.setColor(Color.rgb(0 ,255 ,255));
                    paint.setTextSize(Config.deviceHeight/18);
                    canvasbuffer.drawText("+"+Config.L_number,Config.NenegLiang_Context_X+Config.neng_logo[0].getWidth(),Config.NenegLiang_Context_Y+Config.deviceHeight/108,paint);
                    canvasbuffer.drawBitmap(Config.neng_logo[this.neng_index],Config.NenegLiang_Context_X-Config.deviceWidth/192,(float) (Config.deviceHeight/8*6.9),paint);
                    if(System.currentTimeMillis()-this.neng_time>400){
                        this.neng_index=(this.neng_index+1)%Config.neng_logo.length;
                        this.neng_time=System.currentTimeMillis();
                    }

                    //画杀敌图标 和 击杀数
                    paint.setColor(Color.rgb(255 ,0 ,0));
                    canvasbuffer.drawText("+"+Config.SCORE,Config.deviceWidth-(Config.deviceWidth-Config.NenegLiang_Context_X)*2+Config.bate_logo.getWidth()+Config.deviceWidth/192,Config.NenegLiang_Context_Y+Config.deviceHeight/108,paint);

                    //画笔还原
                    paint.setColor(color);

                    updateData();//调用更新数据
                    ondraw(canvasbuffer);//更新数据后,再绘制容器的全部内容
//                    canvasbuffer.setBitmap(bitmapbuffer);
                    /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
                     *                      把缓冲的假屏幕,画到真正的屏幕里面                                 *
                     *                          (就行照镜子一样的照上去)                                   *
                     * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
                    canvas.drawBitmap(bitmapbuffer,0,0,null);

                }catch(Exception e){
                    e.getMessage();
                }finally{
                    //开锁,画布可以被再次更新了
                    if(canvas!=null) {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                }
//                gameRunFlag=false;
            }
            //锁外休眠 给其他线程留运行空间
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

2、Handler线程外通信
SurfaceView中是一个自线程,依赖于另外的实现线程来启动,如果我们需要在屏幕上显示提示信息,绘制弹出对话框等,那么就需要使用Handler来实现与外部的通信,实现如下:
这里仅截取一部分代码

//游戏结束 对话框
    public void gameOver(){
        handler.post(new Runnable() {
            @Override
            public void run() {
                try{
                    Thread.sleep(50);
                    //调用弹出对话框的依赖界面自生管理者
                    RunYXActivity.getRunActivity().gameOver();
//                    System.exit(0);
                    //surfaceDestroyed(surfaceHolder);
                }catch(Exception e){
                    e.printStackTrace();
                }

            }
        });
    }
//显示提示信息
 handler.post(new Runnable(){
                @Override
                public void run() {
                    Toast.makeText(context,name+"被杀!",Toast.LENGTH_LONG).show();
                }
            });

其实我们很容易发现Handler也是启动来一个子线程来实现的通信

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值