首页 > 代码库 > SurfaceView学习中遇到的问题

SurfaceView学习中遇到的问题

1. 听说游戏开发都用Surfaceview,上网搜了下,说是SurfaceView在更新视图时,采用了双缓存机制,可以提高更新效率,加强用户体验。下面两段粘贴自别人的博客,说的还挺清楚的。

Note: On each pass you retrieve the Canvas from the SurfaceHolder, the previous state of the Canvas will be retained. In order to properly animate your graphics, you must re-paint the entire surface. For example, you can clear the previous state of the Canvas by filling in a color with drawColor() or setting a background image with drawBitmap(). Otherwise, you will see traces of the drawings you previously performed.

在运用时可以理解为:SurfaceView在更新视图时用到了两张Canvas,一张frontCanvas和一张backCanvas,每次实际显示的是frontCanvas,backCanvas存储的是上一次更改前的视图,当使用lockCanvas()获取画布时,得到的实际上是backCanvas而不是正在显示的frontCanvas,之后你在获取到的backCanvas上绘制新视图,再unlockCanvasAndPost(canvas)此视图,那么上传的这张canvas将替换原来的frontCanvas作为新的frontCanvas,原来的frontCanvas将切换到后台作为backCanvas。例如,如果你已经先后两次绘制了视图A和B,那么你再调用lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你讲重绘的C视图上传,那么C将取代B作为新的frontCanvas显示在SurfaceView上,原来的B则转换为backCanvas。

2. 在网上依样画葫芦写了短代码,结果send to background的时候会crash。Debug后发现原因是没检查SurfaceHolder获得的Canvas是不是为空。在surfaceDestroyed之后,Canvas肯定没有了,所以要检查。

public class DemoSurfaceView extends SurfaceView implements Callback{        LoopThread thread;             public DemoSurfaceView(Context context) {        super(context);        init();    }         private void init(){        SurfaceHolder holder = getHolder();        thread = new LoopThread(holder, getContext());        holder.addCallback(this);    }         @Override      public void surfaceChanged(SurfaceHolder holder, int format, int width,              int height) {      }         @Override      public void surfaceCreated(SurfaceHolder holder) {          thread.isRunning = true;          thread.start();      }         @Override      public void surfaceDestroyed(SurfaceHolder holder) {          Log.i("Hailin", "surfaceDestroyed");        thread.isRunning = false;          try {              thread.join();          } catch (InterruptedException e) {              e.printStackTrace();          }     }      class LoopThread extends Thread{             SurfaceHolder surfaceHolder;          Context context;          boolean isRunning;          float radius = 10f;          Paint paint;             public LoopThread(SurfaceHolder surfaceHolder,Context context){                 this.surfaceHolder = surfaceHolder;              this.context = context;              isRunning = false;                 paint = new Paint();              paint.setColor(Color.YELLOW);              paint.setStyle(Paint.Style.STROKE);          }             @Override          public void run() {                 Canvas c = null;                 while(isRunning){                     try{                      synchronized (surfaceHolder) {                             if(surfaceHolder == null) {                            Log.i("Hailin", "surfaceHolder == null");                        }                                                c = surfaceHolder.lockCanvas(null);                          if(c == null) {                            Log.i("Hailin", "c == null");                            return;                        }                        doDraw(c);                          Thread.sleep(50);                         surfaceHolder.unlockCanvasAndPost(c);                     }                  } catch (InterruptedException e) {                      e.printStackTrace();                  } finally {                                       }                 }             }             public void doDraw(Canvas c){                 c.drawColor(Color.BLACK);              c.translate(200, 200);              c.drawCircle(0,0, radius++, paint);                 if(radius > 100){                  radius = 10f;              }             }         }  }
View Code

3. 照着《疯狂Android讲义》敲了断代码,发现在点击屏幕后在surfaceCreated里面画的背景图老是丢失。如果直接用Surfaceview.setBackground来设置背景,点击屏幕画的图会被背景图非黑色部分给遮住(背景图本来是白色背景,但是画上去都变黑色背景了)。最后只能在onTouch里面重新画一遍背景图。

public class SVTest extends Activity {    private SurfaceHolder holder;    private Paint paint;    private SurfaceView surface;    private Canvas canvas;    private boolean isTouched;    private Bitmap mBitmap;        @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        this.setContentView(R.layout.svtest);                paint = new Paint();        surface = (SurfaceView) this.findViewById(R.id.svTest);                final Bitmap back = BitmapFactory.decodeResource(SVTest.this.getResources(), R.drawable.ic_launcher);        DisplayMetrics dm = getResources().getDisplayMetrics();          int mScreenWidth = dm.widthPixels;        int mScreenHeight = dm.heightPixels;        mBitmap = Bitmap.createScaledBitmap(back, mScreenWidth, mScreenHeight, true);                 // actually it set a Drawable with pure color as background//        surface.setBackgroundColor(Color.WHITE);        // set background Drawable//        surface.setBackgroundResource(R.drawable.ic_launcher);                holder = surface.getHolder();        holder.addCallback(new Callback() {            @Override            public void surfaceCreated(SurfaceHolder holder) {                // TODO Auto-generated method stub                Log.i("Hailin", "surfaceCreated");                //                set background                                canvas = holder.lockCanvas();                canvas.drawBitmap(mBitmap, 0, 0, null);                holder.unlockCanvasAndPost(canvas);            }            @Override            public void surfaceChanged(SurfaceHolder holder, int format,                    int width, int height) {                // TODO Auto-generated method stub                Log.i("Hailin", "surfaceChanged");            }            @Override            public void surfaceDestroyed(SurfaceHolder holder) {                // TODO Auto-generated method stub                Log.i("Hailin", "surfaceDestroyed");            }                    });                surface.setOnTouchListener(new OnTouchListener() {            @Override            public boolean onTouch(View v, MotionEvent event) {                // TODO Auto-generated method stub                if(event.getAction() == MotionEvent.ACTION_DOWN) {                    if(!isTouched) {                            // set background again                        canvas = holder.lockCanvas();                        canvas.drawBitmap(mBitmap, 0, 0, null);                        holder.unlockCanvasAndPost(canvas);                                                isTouched = true;                    }                    int cx = (int) event.getX();                    int cy = (int) event.getY();                    Canvas canvas = holder.lockCanvas(new Rect(cx - 50, cy - 50, cx + 50, cy + 50));                    canvas.save();                    canvas.rotate(30, cx, cy);                    paint.setColor(Color.RED);                    canvas.drawRect(cx - 50, cy - 50, cx, cy, paint);                    canvas.restore();                    paint.setColor(Color.GREEN);                    canvas.drawRect(cx, cy, cx + 50, cy + 50, paint);                    holder.unlockCanvasAndPost(canvas);                }                return false;            }                    });    }        @Override    public void onResume() {        super.onResume();        Log.i("Hailin", "onResume");    }        @Override    public void onPause() {        super.onPause();        Log.i("Hailin", "onPause");    }        @Override    public void onDestroy() {        super.onDestroy();        Log.i("Hailin", "onDestroy");    }    }
View Code

 

SurfaceView学习中遇到的问题