View 看需要重写几个函数,分别是绘图函数onDraw ,按键按下函数 onKeyDown,按键抬起函数 onKeyUp,触屏函数onTouchEvent
其中绘图函数含有画笔paint和画布canvas
@SuppressLint("DrawAllocation") public class MyView extends View { private int tX=10 , tY=10; public MyView (Context context){ super(context); //给当前View设置焦点,其实就是告示系统,现在这个视图需要与用户交互,让系统来监听此视图 setFocusable(true); } protected void onDraw(Canvas canvas){ Paint paint =new Paint(); paint.setColor(Color.WHITE); canvas.drawText("HI View", tX, tY, paint); super.onDraw(canvas); } public boolean onKeyDown (int keyCode, KeyEvent event){ //KeyEvent 指的是按键的动作事件队列,此类还定义了很多静态常量键值 if(keyCode == KeyEvent.KEYCODE_DPAD_UP){ tY-=2; }else if(keyCode == KeyEvent.KEYCODE_DPAD_DOWN){ tY+=2; }else if(keyCode == KeyEvent.KEYCODE_DPAD_LEFT){ tX-=2; }else if(keyCode == KeyEvent.KEYCODE_DPAD_RIGHT){ tX+=2; } //invalidate方法不能再当前线程中 循环 调用执行,而postInvalidate()函数可以在子线程中循环调用执行 //如果不在当前View创建线程循环重绘画布的话,则两种方式没有区别 invalidate(); //postInvalidate(); return super.onKeyDown(keyCode, event); } public boolean onKeyUp(int keyCode,KeyEvent event){ return super.onKeyUp(keyCode, event); } public boolean onToucnEvent(MotionEvent event){ int x=(int) event.getX(); int y=(int) event.getY(); //玩家点击屏幕的动作 if(event.getAction()==MotionEvent.ACTION_DOWN){ tX=x; tY=y; //玩家手指抬起离开屏幕的动作 }else if(event.getAction()==MotionEvent.ACTION_MOVE){ tX=x; tY=y; //玩家手指在屏幕上移动的动作 }else if(event.getAction()==MotionEvent.ACTION_UP){ tX=x; tY=y; } invalidate(); return super.onTouchEvent(event); } }
在MainActivity让屏幕显示MyView类(在这里去掉标题栏和状态栏)
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); this.requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(new MyView(this)); } }
(0)SurfaceView是View的子类
(1)SurfaceHolder类提供控制SurfaceView的大小、格式等,并且主要用于监听SurfaceView的状态。
(2)SurfaceView只是保存当前视图的像素数据
(3)使用SurfaceHolder的lockCanvas()函数不仅是获取canvas,同时还对获取的 Canvas画布进行加锁,防止绘制的过程中被修改和摧毁
(4)SurfaceHolder对SurfaceView的状态进行监听需要使用android.view.SurfaceHolder.Callback接口:此接口需要重写三个函数
当SurfaceView被创建完成后响应的函数 public void surfaceCreated(SurfaceHolder holder){}
当SurfaceView状态发生改变时响应的函数 pubic void surfaceChanged (SurfaceHolder ,int format , int width ,int height){}
当SurfaceView状态摧毁时响应的函数 public void surfaceDestroyed(SurfaceHolder ){}
public class MySurfaceView extends SurfaceView implements Callback{ private SurfaceHolder sfh; private Paint paint; public MySurfaceView (Context context){ super(context); sfh = this.getHolder(); //为SurfaceView添加状态监听 sfh.addCallback(this); paint = new Paint(); paint.setColor(Color.WHITE); } public void surfaceCreated(SurfaceHolder holder){ myDraw(); } public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){ } public void surfaceDestroyed(SurfaceHolder holder){ } //自定义绘图函数 public void myDraw(){ Canvas canvas = sfh.lockCanvas(); canvas.drawText("HI SurfaceView", 10, 10, paint); sfh.unlockCanvasAndPost(canvas); } }刷屏的方式有几种:
(1)每次绘图之前,绘制一个等同于屏幕大小的图形或者背景图片覆盖在画布之上
(2)每次绘图之前,在画布上填充一种颜色(肯定不是修改画笔颜色嘛),可以指定颜色也可以用RGB表示
修改后的Draw函数如下:
public void myDrawAfterModify1(){ Canvas canvas =sfh.lockCanvas(); canvas.drawRect(0, 0, this.getHeight() ,this.getWidth(), paint); canvas.drawText("HI SurfaceView", tX, tY, paint); sfh.unlockCanvasAndPost(canvas); } public void myDrawAfterModify2(){ Canvas canvas =sfh.lockCanvas(); canvas.drawColor(Color.BLACK); canvas.drawText("HI SurfaceView", tX, tY, paint); sfh.unlockCanvasAndPost(canvas); }
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.activity_main); requestWindowFeature(Window.FEATURE_NO_TITLE); this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); setContentView(new MySurfaceView(this)); } }
(1)线程的初始化和线程的启动都写在视图的SurfaceCreated创建函数当中,并且将线程标识位在视图摧毁时将其值改变为false
(2)Bcak键之后:
surfaceDestroyed->构造函数->surfaceCreated->surfaceChanged
Home键之后
surfaceDestroyed->surfaceCreated->surfaceChanged
也就是说按键Back之后,surfaceView会被重新加载
(3)获取视图的宽和高
一定要在视图创建之后才可获取到
(4)绘图函数try一下
因为当SurfaceView不可编辑或者尚未创建时,调用lockCanvas()函数会返回NULL
(5)提交画布必须放在finally下
绘图的时候可能会出现不可预知的BUG,在try下不会崩溃,但是在提交画布之前出错的话,那么解锁提交画布函数则无法被执行到,这样会导致下次通过lockCanvas来获取
Canvas时抛出异常
(6)刷帧时间尽可能保持一致
public class MySurfaceView extends SurfaceView implements Callback ,Runnable{ private SurfaceHolder sfh; private Paint paint; private int tX=10, tY=10; //生命一个线程 private Thread th; //线程消亡的标志位 private boolean flag; private Canvas canvas; //声明屏幕的宽高 private int screenW ,screenH; public MySurfaceView (Context context){ super(context); sfh = this.getHolder(); //为SurfaceView添加状态监听 sfh.addCallback(this); paint = new Paint(); paint.setColor(Color.WHITE); setFocusable(true); } public void surfaceCreated(SurfaceHolder holder){ screenW =this.getWidth(); screenH =this.getHeight(); flag =true; th = new Thread(this); th.start(); } public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){ } public void surfaceDestroyed(SurfaceHolder holder){ flag=false; } //自定义绘图函数 public void myDraw(){ try{ canvas = sfh.lockCanvas(); if(canvas!=null){ //canvas.drawRect(0, 0, this.getWidth(), this.getHeight(), paint); canvas.drawRGB(0, 0, 0); canvas.drawText("HI GameSurfaceView", tX, tY, paint); } }catch(Exception e){ }finally{ if(canvas!=null) sfh.unlockCanvasAndPost(canvas); } } public boolean onTouchEvent(MotionEvent event){ tX=(int) event.getX(); tY=(int) event.getY(); myDraw(); return true; } public boolean onKeyDown(int keyCode,KeyEvent event){ return super.onKeyDown(keyCode, event); } //游戏逻辑 private void logic(){} public void run(){ while(flag){ long start=System.currentTimeMillis(); myDraw(); logic(); long end= System.currentTimeMillis(); try{ if(end -start <50){ Thread.sleep(50-(end-start)); } }catch(InterruptedException e){ e.printStackTrace(); } } } }