Android培训三十二课第十讲

本文深入解析Android游戏开发中View与SurfaceView的区别及应用场景,重点介绍它们的刷新机制、双缓冲技术,并提供具体实现代码。

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

  Anroid是一种以 嵌入式为基础的开放源码操作系统, Android 培训第十课:系统讲解构建游戏框架View与SurFaceView的区别。

1.view
viewapi中的结构
java.lang.Object
view.View
直接子类:
AnalogClock, ImageView, KeyboardView, ProgressBar, SurfaceView, TextVie, ViewGroup, ViewStub
间接子类:
AbsListView, AbsSeekBar, AbsSpinner, AbsoluteLayout, AdapterView<T extends Adapter>, 

AppWidgetHostView, AutoCompleteTextView, Button, CheckBox, CheckedTextView, Chronometer, 

CompoundButton, DatePicker, DialerFilter, DigitalClock,EditView, ExpandableListView, ExtractEditText, 

FrameLayout, GLSurfaceView, Gallery, GestureOverlayView, GridView, HorizontalScrollView, ImageButton, 

ImageSwitcher, LinearLayout, ListView, MediaController, MultiAutoCompleteTextView, QuickContactBadge, 

RadioButton, RadioGroup, RatingBar, RelativeLayout, ScrollView, SeekBar, SlidingDrawer, Spinner, TabHost, 

TabWidget, TableLayout, TableRow, TextSwitcher, TimePicker, ToggleButton, TwoLineListItem, VideoView, 

ViewAnimator, ViewFlipper, ViewSwitcher, WebView, ZoomButton, ZoomControls
    由此可见View类属于Anroid开发绘制中的显示老大,任何与绘制有关系的控件都是它的子类。这篇文章主要讲View SurFaceView 使用线程刷新屏幕绘制方面的知识。开发中如何去选择使用View还是SurFaceView

   下面详细的说明一下Andooid刷新屏幕的几种方法。
    第一种: 在onDraw方法最后调用invalidate()方法,它会通知UI线程重绘 这样 View会重新调用onDraw方法,实现刷新屏幕。 这样写看起来代码非常简洁漂亮,但是它也同时存在一个很大的问题,它和游戏主线程是分开的 它违背了单线程模式,这样操作绘制的话是很不安全的,举个例子 比如程序先进在Activity1中 使用invalidate()方法来重绘, 然后我跳到了Activity2这时候Activity1已经finash()掉 可是Activity1中 的invalidate() 的线程还在程序中,Android的虚拟机不可能主动杀死正在运行中的线程所以这样操作是非常危险的。因为它是在UI线程中被动掉用的所以很不安全。
invalidate()  更新整个屏幕区域
invalidate(Rect rect) 更新Rect区域
invalidate(l, t, r, b) 更新指定矩形区域 

1     public void onDraw(Canvas canvas){  

2             DosomeThing();  

3             invalidate();  

4     }  

       第二种:使用postInvalidate();方法来刷新屏幕 ,调用后它会用handler通知UI线程重绘屏幕,我们可以 new  Thread(this).start(); 开启一个游戏的主线程 然后在主线程中通过调用postInvalidate();方法来刷新屏幕。postInvalidate();方法 调用后 系统会帮我们调用onDraw方法 ,它是在我们自己的线程中调用 通过调用它可以通知UI线程刷新屏幕 。由此可见它是主动调用UI线程的。所以建议使用postInvalidate()方法通知UI线程来刷新整个屏幕。
postInvalidate(left, top, right, bottom) 方法 通过UI线程来刷新规定矩形区域。 

5         @Override

6         public void run() {

7             while (mIsRunning) {

8                 try {

9                     Thread.sleep(100);

10                     postInvalidate();

11                 } catch (InterruptedException e) {

12                     // TODO Auto-generated catch block

13                     e.printStackTrace();

14                 }

15             }

16         }

        View中用到的双缓冲技术
     重绘的原理是 程序根据时间来刷新屏幕 如果有一帧图形还没有完全绘制结束 程序就开始刷新屏幕这样就会造成瞬间屏幕闪烁 画面很不美观,所以双缓冲的技术就诞生了。它存在的目的就是解决屏幕闪烁的问题,下面我说说在自定义View中如何实现双缓冲。
首先我们需要创建一张屏幕大小的缓冲图片,我说一下第三个参数 ARGB 分别代表的是 透明度   红色   绿色     蓝色
Bitmap.Config  ARGB_4444              ARGB  分别占四位  
Bitmap.Config  ARGB_8888              ARGB  分别占八位
Bitmap.Config  RGB_565                没有透明度(A)   R5位   6位   B5位   
       一般情况下我们使用ARGB_8888 因为它的效果是最好了 当然它也是最占内存的。 

17    mBufferBitmap = Bitmap.createBitmap(mScreenWidth,mScreenHeight,Config.ARGB_8888);

     创建一个缓冲的画布,将内容绘制在缓冲区mBufferBitmap中 

18 Canvas mCanvas = new Canvas();

19 mCanvas.setBitmap(mBufferBitmap);

      最后一次性的把缓冲区mBufferBitmap绘制在屏幕上,怎么样 简单吧 呵呵。 

20         @Override

21         protected void onDraw(Canvas canvas) {

22             /**这里先把所有须要绘制的资源绘制到mBufferBitmap**/

23             /**绘制地图**/

24             DrawMap(mCanvas,mPaint,mBitmap);

25             /**绘制动画**/

26             RenderAnimation(mCanvas);

27             /**更新动画**/

28             UpdateAnimation();

29             

30             

31             if(isBorderCollision) {

32                 DrawCollision(mCanvas,"与边界发生碰撞");

33             }

34             

35             if(isAcotrCollision) {

36                 DrawCollision(mCanvas,"与实体层发生碰撞");

37             }

38             if(isPersonCollision) {

39                 DrawCollision(mCanvas,"NPC发生碰撞");

40             }

41             

42             /**最后通过canvas一次性的把mBufferBitmap绘制到屏幕上**/

43             canvas.drawBitmap(mBufferBitmap, 0,0, mPaint);

44             super.onDraw(canvas);

45         }

        由此可见view属于被动刷新, 因为我们做的任何刷新的操作实际上都是通知UI线程去刷新。所以在做一些只有通过玩家操作以后才会刷新屏幕的游戏 并非自动刷新的游戏 可以使用view来操作。
2.SurfaceView
     从API中可以看出SurfaceView属于View的子类 它是专门为制作游戏而产生的,它的功能非常强大,最重要的是它支持OpenGL ES库,2D3D的效果都可以实现。创建SurfaceView的时候需要实现SurfaceHolder.Callback接口,它可以用来监听SurfaceView的状态,SurfaceView的改变 SurfaceView的创建 SurfaceView 销毁  我们可以在相应的方法中做一些比如初始化的操作 或者 清空的操作等等。
       使用SurfaceView构建游戏框架它的绘制原理是绘制前先锁定画布 然后等都绘制结束以后 在对画布进行解锁 最后在把画布内容显示到屏幕上。
       代码中是如何实现SurfaceView
       首先需要实现 Callback 接口 与Runnable接口 

46 public class AnimView extends SurfaceView implements Callback,Runnable

      获取当前mSurfaceHolder 并且把它加到CallBack回调函数中 

47             SurfaceHolder  mSurfaceHolder = getHolder();

48             mSurfaceHolder.addCallback(this);

        通过callBack接口监听SurfaceView的状态, 在它被创建的时候开启游戏的主线程,结束的时候销毁。这里说一下在View的构造函数中是拿不到view有关的任何信息的,因为它还没有构建好。 所以通过这个监听我们可以在surfaceCreated()中拿到当前view的属性 比如view的宽高 等等,所以callBack接口还是非常有用处的。 

49         @Override

50         public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,

51                 int arg3) {

52             // surfaceView的大小发生改变的时候

53             

54         }

55 

56         @Override

57         public void surfaceCreated(SurfaceHolder arg0) {

58             /**启动游戏主线程**/

59             mIsRunning = true;

60             mThread = new Thread(this);

61             mThread.start();

62         }

63 

64         @Override

65         public void surfaceDestroyed(SurfaceHolder arg0) {

66          // surfaceView销毁的时候

67             mIsRunning = false;

68         }

       在游戏主线程循环中在绘制开始 先拿到画布canvas 并使用mSurfaceHolder.lockCanvas()锁定画布,等绘制结束以后 使用mSurfaceHolder.unlockCanvasAndPost(mCanvas)解锁画布,  解锁画布以后画布上的内容才会显示到屏幕上。 

69         @Override

70         public void run() {

71             while (mIsRunning) {

72                 try {

73                     Thread.sleep(100);

74                 } catch (InterruptedException e) {

75                     // TODO Auto-generated catch block

76                     e.printStackTrace();

77                 }

78                

79                 //在这里加上线程安全锁

80                 synchronized (mSurfaceHolder) {

81                     /**拿到当前画布 然后锁定**/

82                     mCanvas =mSurfaceHolder.lockCanvas();  

83             &nbs

 

查看原文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值