SurfaceView类的使用 surfaceview 透明

文章转自东方尚智沈大海博客:

在android中开发游戏,一般来说,或想写一个复杂一点的游戏,是必须用到SurfaceView来开发的。
经过这一阵子对android的学习,我找到了自已在android中游戏开发的误区,不要老想着用Layout和view去实现,不要将某个游戏
中的对象做成一个组件来处理。应该尽量想着在Canvas(画布)中画出游戏戏中的背景、人物、动画等...
SurfaceView提供直接访问一个可画图的界面,可以控制在界面顶部的子视图层。SurfaceView是提供给需要直接画像素而不是使用
窗体部件的应用使用的。Android图形系统中一个重要的概念和线索是surface。View及其子类(如TextView,Button)
要画在surface上。每个surface创建一个Canvas对象(但属性时常改变),用来管理view在surface上的绘图操作,如画点画线。
还要注意的是,使用它的时候,一般都是出现在最顶层的:The view hierarchy will take care ofcorrectly compositing
with the Surface any siblings of the SurfaceView that wouldnormally appear on top of it.

使用的SurfaceView的时候,一般情况下还要对其进行创建,销毁,改变时的情况进行监视,这就要用到SurfaceHolder.Callback.
class BBatt extends SurfaceView implements SurfaceHolder.Callback{
public void surfaceChanged(SurfaceHolder holder,int format,intwidth,int height){}
//看其名知其义,在surface的大小发生改变时激发
public void surfaceCreated(SurfaceHolder holder){}
//同上,在创建时激发,一般在这里调用画图的线程。
public void surfaceDestroyed(SurfaceHolder holder) {}
//同上,销毁时激发,一般在这里将画图的线程停止、释放。
}


例子:

public class BBatt extends SurfaceView implementsSurfaceHolder.Callback, OnKeyListener {
privateBFairy bFairy;
privateDrawThread drawThread;
publicBBatt(Context context) {
super(context);
this.setLayoutParams(newwGroup.LayoutParams(Global.battlefieldWidth,Global.battlefieldHeight));
this.getHolder().addCallback( this );
this.setFocusable( true );
this.setOnKeyListener( this );
bFairy = new BFairy(this.getContext());
}
publicvoid surfaceChanged(SurfaceHolder holder,
intformat,int width,int height) {
drawThread = new DrawThread(holder);
drawThread.start();
}
public voidsurfaceD estroyed(SurfaceHolder holder) {
if( drawThread != null ) {
drawThread.doStop();
while(true) try {
drawThread.join();
break ;
} catch(Exception ex) {}
}
}
public boolean onKey(View view, int keyCode, KeyEvent event){}
}

实例2:用线程画一个蓝色的长方形。

package com.g3.test;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class Test extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
}

//内部类
class MyView extends SurfaceView implementsSurfaceHolder.Callback{

SurfaceHolder holder;
public MyView(Context context) {
super(context);
holder = this.getHolder();//获取holder
holder.addCallback(this);
//setFocusable(true);

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, intwidth,
int height) {

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
new Thread(new MyThread()).start();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}

//内部类的内部类
class MyThread implements Runnable{

@Override
public void run() {
Canvas canvas = holder.lockCanvas(null);//获取画布
Paint mPaint = new Paint();
mPaint.setColor(Color.BLUE);

canvas.drawRect(new RectF(40,60,80,80), mPaint);
holder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
}
}
}
}


访问SurfaceView的底层图形是通过SurfaceHolder接口来实现的,通过getHolder()方法可以得到这个SurfaceHolder对象。你应该实现surfaceCreated(SurfaceHolder)和surfaceDestroyed(SurfaceHolder)方法来知道在这个Surface在窗口的显示和隐藏过程中是什么时候创建和销毁的。
SurfaceView可以在多线程中被访问。
注意:一个SurfaceView只在SurfaceHolder.Callback.surfaceCreated() 和SurfaceHolder.Callback.surfaceDestroyed()调用之间是可用的,其他时间是得不到它的Canvas对象的(null)。

我的访问过程:
创建一个SurfaceView的子类,实现SurfaceHolder.Callback接口。
得到这个SurfaceView的SurfaceHolder对象holder。
holder.addCallback(callback),也就是实现SurfaceHolder.Callback接口的类对象。
在SurfaceHolder.Callback.surfaceCreated()调用过后holder.lockCanvas()对象就可以得到SurfaceView对象对应的Canvas对象canvas了。
用canvas对象画图。
画图结束后调用holder.unlockCanvasAndPost()就把图画在窗口中了。
SurfaceView可以多线程访问,在多线程中画图。


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements
SurfaceHolder.Callback {


private Context mContext;
private SurfaceHolder mHolder;

public TouchScreenAdjusterSurfaceView(Context context,) {
super(context);

mContext = context;

mHolder = TouchScreenAdjusterSurfaceView.this.getHolder();
mHolder.addCallback(TouchScreenAdjusterSurfaceView.this);

this.setFocusableInTouchMode(true); // to make sure that we canget
// touch events and key events,and
// "setFocusable()" to make sure we
// can get key events
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, intwidth,
int height) {
// TODO Auto-generated method stub

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
//now you can get the Canvas and draw something here
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub

}


public void drawMyShape(PointPostion ps) {

mCanvas = mHolder.lockCanvas();

// draw anything you like

mHolder.unlockCanvasAndPost(mCanvas);
}

}**************************************************************************************************当我们需要开发一个复杂的游戏的时候,而且对程序的执行效率要求很高时,View类就不能满足需求了,这时必须用SurfaceView类进行开发。例如,对速度要求很高的游戏时,View类就不能满足需求了,这时必须使用SurfaceView类进行开发。例如,对速度要求很高的游戏,可以使用双缓冲来显示。游戏中的背景、人物、动画等都需要绘制在一个画布(Canvas)上,而SurfaceView可以直接访问一个画布,SurfaceView是提供给需要直接画像素而不是使用窗体部件的应用使用的。每个Surface创建一个Canvas对象(但属性时常改变),用来管理View和Surface上的绘图操作。

在使用SurfaceView开发时需要注意的是,使用它绘图时,一般都是出现在最顶层。使用时还需要对其进行创建、销毁、情况改变时进行监视,这就要实现SurfaceHolder.Callback接口,如果要对被绘制的画布进行裁剪,控制其大小都需要使用SurfaceHolder来完成处理。在程序中,SurfaceHolder对象需要通过getHolder方法来获得,同时还需要addCallback方法来添加 "回调函数"

在一般的情况下,应用程序的View都是在相同的GUI线程中绘制的。这个主应用程序线程同时也用来处理所有的用户交互(例如,按钮单击或者文本输入)。
当需要快速地更新View的UI,或者当渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的操作,或者那些要求快速更新或者高速帧率的地方,例如,使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

1. 何时应该使用SurfaceView?

SurfaceView使用的方式与任何View所派生的类都是完全相同的。可以像其他View那样应用动画,并把它们放到布局中。

SurfaceView封装的Surface支持使用本章前面所描述的所有标准Canvas方法进行绘图,同时也支持完全的OpenGLES库。

使用OpenGL,你可以再Surface上绘制任何支持的2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依靠硬件加速(可用的时候)来极大地提高性能。

对于显示动态的3D图像来说,例如,那些使用GoogleEarth功能的应用程序,或者那些提供沉浸体验的交互式游戏,SurfaceView特别有用。它还是实时显示摄像头预览的最佳选择。

2. 创建一个新的SurfaceView控件

要创建一个新的SurfaceView,需要创建一个新的扩展了SurfaceView的类,并实现SurfaceHolder.Callback。

SurfaceHolder回调可以在底层的Surface被创建和销毁的时候通知View,并传递给它对SurfaceHolder对象的引用,其中包含了当前有效的Surface。

一个典型的SurfaceView设计模型包括一个由Thread所派生的类,它可以接收对当前的SurfaceHolder的引用,并独立地更新它。

3. SurfaceHolder.Callback 回调接口的方法介绍

surfaceChanged: 在Surface的大小发生改变时激发。
surfaceCreated: 在创建Surface时激发
surfaceDestroyed: 在销毁Surface时激发
addCallback: 给SurfaceView添加一个回调函数
lockCanvas: 锁定画布,绘图之前必须锁定画布才能得到当前画布对象。
unlockCanvasAndPost: 开始绘图时锁定了画布,绘制完成之后解锁画布。
removeCallback: 从SurfaceView 中移除回调函数。


SurfaceView 和 View的明显不同之处在于,继承SurfaceView 的视图可以另起一个线程或者 说在子线程中 更新视图 这与我上一篇的另外一个示例android 自定义View类的简单使用 示例 (这个示例请点这里http://www.eoeandroid.com/thread-72273-1-1.html)明显的一个区别就是SurfaceView 的画图方法 是在 子线程中执行的 而 View类的那个示例 的画图方法 是在UI线程中执行的。其实SurfaceView按道理应该违背了 android的 单线程模型 因为它在子线称中 画图 刷新界面 来更新UI 至于它为什么可以这么做 我也不知道 有时间研究一下吧 可能它是一种特殊的视图吧。 还有要注意的一点就是 我们在绘图之前必须使用lockCanvas 方法锁定画布,并得到画布,然后再画布上绘制;当绘制完成后,使用unlockCanvasAndPost方法解锁画布,然后就显示到屏幕上。SurfaceView 类的事件处理规则和View一样。
下面是这样一个例子 这个例子实现了一个不断变换颜色的圆形 并且实现了SurfaceView的事件处理。 我们可以通过模拟器的 上下键来调节 这个圆 在屏幕中的位置。下面先看一下运行效果吧。

效果图:




Java代码:

  1. package eoe.Demo;

  2. import android.content.Context;
  3. import android.graphics.Canvas;
  4. import android.graphics.Color;
  5. import android.graphics.Paint;
  6. import android.view.SurfaceHolder;
  7. import android.view.SurfaceView;

  8. public class GameSurfaceView extends SurfaceView implementsSurfaceHolder.Callback,Runnable{

  9. //控制循环
  10. boolean mbLoop = false;

  11. //定义SurfaceHolder对象
  12. SurfaceHolder mSurfaceHolder = null;
  13. int miCount = 0;
  14. int y =50;

  15. public GameSurfaceView(Context context) {
  16. super(context);
  17. //实例化SurfaceHolder
  18. mSurfaceHolder = this.getHolder();

  19. SurfaceView类的使用 surfaceview 透明
  20. //添加回调 函数
  21. //注意这里这句 mSurfaceHolder.addCallback(this)这句执行完了之后
  22. //马上就会回调 surfaceCreated方法了 然后开启线程 执行绘图方法这里这个执行顺序要搞清楚
  23. mSurfaceHolder.addCallback(this);
  24. this.setFocusable(true);
  25. mbLoop = true;
  26. }

  27. //在surface的大小发生改变时激发
  28. @Override
  29. public void surfaceChanged(SurfaceHolder holder, int format,int width,
  30. int height) {
  31. }

  32. //surface创建时激发 此方法在主线程总执行
  33. @Override
  34. public void surfaceCreated(SurfaceHolder holder) {
  35. //开启绘图线程
  36. new Thread(this).start();
  37. }

  38. //在surface销毁时激发
  39. @Override
  40. public void surfaceDestroyed(SurfaceHolder holder) {
  41. //停止循环
  42. mbLoop = false;
  43. }

  44. //绘图循环
  45. @Override
  46. public void run() {
  47. while (mbLoop){
  48. try {
  49. Thread.sleep(200);
  50. } catch (Exception e) {

  51. }
  52. //至于这里为什么同步这就像一块画布 你不能让两个人同时往上边画画
  53. synchronized( mSurfaceHolder ){
  54. Draw();
  55. }
  56. }
  57. }
  58. //绘图方法 注意这里是另起一个线程来执行绘图方法了不是在UI 线程了
  59. public void Draw(){
  60. //锁定画布,得到canvas 用SurfaceHolder对象的lockCanvas方法
  61. Canvas canvas = mSurfaceHolder.lockCanvas();
  62. if (mSurfaceHolder==null || canvas == null) {
  63. return;
  64. }
  65. if (miCount < 100) {
  66. miCount++;
  67. }else {
  68. miCount = 0;
  69. }
  70. //绘图
  71. Paint mPaint = new Paint();
  72. //给Paint对象加上抗锯齿标志
  73. //http://tech.techweb.com.cn/thread-459611-1-1.html 详细说明可以看看这里--->大家直接复制链接到 浏览器打开吧。
  74. mPaint.setAntiAlias(true);
  75. mPaint.setColor(Color.BLACK);
  76. //绘制矩形---清屏作用
  77. canvas.drawRect(0, 0, 320, 480, mPaint);
  78. switch (miCount % 4) {
  79. case 0:
  80. mPaint.setColor(Color.BLUE);
  81. break;
  82. case 1:
  83. mPaint.setColor(Color.GREEN);
  84. break;
  85. case 2:
  86. mPaint.setColor(Color.RED);
  87. case 3:
  88. mPaint.setColor(Color.YELLOW);
  89. default:
  90. mPaint.setColor(Color.WHITE);
  91. break;
  92. }
  93. //绘制矩形
  94. canvas.drawCircle((320 - 25) / 2, y, 50, mPaint);
  95. //绘制后解锁,绘制后必须解锁才能显示
  96. mSurfaceHolder.unlockCanvasAndPost(canvas);
  97. }
  98. }

*************************************************************************************************

我们还是接上一篇的代码来讲解:

Java代码:

  1. package eoe.Demo;

  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.KeyEvent;
  5. import android.view.MotionEvent;

  6. public class Activity01 extends Activity {
  7. GameSurfaceView mGameSurfaceView;

  8. @Override
  9. public void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);

  11. //创建GameSurfaceView对象
  12. mGameSurfaceView = new GameSurfaceView(this);

  13. //设置显示GameSurfaceView视图
  14. setContentView(mGameSurfaceView);
  15. }

  16. //触笔事件 返回值为true 父视图不做处理以下返回值为true的都是不做处理的
  17. public boolean onTouchEvent(MotionEvent event){
  18. return true;
  19. }
  20. //按键按下事件
  21. public boolean onKeyDown(int keyCode, KeyEventevent){
  22. return true;
  23. }
  24. //按键弹起事件
  25. public boolean onKeyUp(int keyCode, KeyEvent event){
  26. switch (keyCode) {
  27. //上方向键
  28. case KeyEvent.KEYCODE_DPAD_UP:
  29. mGameSurfaceView.y-=3;
  30. break;
  31. //下方向键
  32. case KeyEvent.KEYCODE_DPAD_DOWN:
  33. mGameSurfaceView.y+=3;
  34. break;
  35. }
  36. return false;
  37. }
  38. public boolean onKeyMultiple(int keyCode, int repeatCount,KeyEvent event){
  39. return true;
  40. }
  41. }
复制代码


写完这个例子 我有些疑问

可不可以 在 类似这种 SurfaceView 上边放一些控件呢?
前边已经说了Surface是提供给需要直接画像素而不是使用窗体部件的应用使用的。 但是 我还是想知道 为什么不能再Surface上边放 一个控件 比如 一个 button 或者 一个 textview 这里我们来拿button 举例子比如我们想在 Surface上边放一个 button 这时 我们就需要 new一个 button 按钮了呗 在Acvity01类中我们可以这样做 Button button = new Button(this);
但是 你new 完这个 button 你怎么把它 放倒 SurfaceView 这个视图上呢?因为在Activity01这个类中我们是这样设置布局的 setContentView(mGameSurfaceView);它的布局就是一个 SurfaceView 说白了也是一个View 这就相当于在一个视图上在放一个视图
这是放不了的。
这时候有人就想了 我们可以把Button 放在 GameSurfaceView 类中 这个想法真是聪明 太聪明了。 然后我们就到GameSurfaceView类中 这样写 Button button = new Button(this); 你会惊奇的发现他报错了。 为什么呢? 下面我把 android 中 Button的源码贴上来大家看看

Java代码:

  1. @RemoteView
  2. public class Button extends TextView {
  3. public Button(Context context) {
  4. this(context, null);
  5. }

  6. public Button(Context context, AttributeSet attrs) {
  7. this(context, attrs,com.android.internal.R.attr.buttonStyle);
  8. }

  9. public Button(Context context, AttributeSet attrs, intdefStyle) {
  10. super(context, attrs, defStyle);
  11. }
  12. }
复制代码


Button 这个类比较简单 就三个构造方法。 但是 每个 构造方法 都需要一个 Context 对象。
这一点大家显然都已经注意到了。 那为什么 我可以再 在继承Activity的类里边 new一个Button 而在 继承SurfaceView的类中确不行呢。
我们分别来看一下Activity类和GameSurfaceView类的继承关系

2011-5-3 11:31 上传下载附件(25.84KB)

我想大家已经知道 为什么 SurfaceView上边不可以放一些我们常用的控件了 也知道为什么我们可以再Activity类 里边可以 new Button 而在SurfaceView 类里边却不行 因为我们的Activity类本身就是一个 Context对象。 而我们的SurfaceView本身 不是 一个Context对象。 而我们要new 一个Button怎么都得需要一个Context对象。从这个角度其实就可以解释为什么 我们不能再SurfaceView上边放一些我们常用的控件了Button TextView之类的。

  

爱华网本文地址 » http://www.413yy.cn/a/25101015/270490.html

更多阅读

痄腮散的使用说明 宝宝炸腮怎么办

腮腺炎,中医学称“痄腮”, 民间也称“猪头肥”。是儿童和青少年中常见的呼吸道传染病,由腮腺炎病毒所引起。冬春季节发病较多,病人是传染源,飞沫的吸入是主要传播途径,接触病人后2-3周发病。腮腺炎主要表现为一侧或两侧耳垂下肿大,肿大的腮

详解暖宝宝的使用方法 暖宝宝使用方法

暖宝宝能够快速热敷、消肿、止痛、活血化瘀,广泛适用于各种畏寒症。并能快速缓解并消除各种畏寒疾病引起的疼痛,是关节炎、肩周炎、腰腿痛、风湿及类风湿、四肢发凉、患处遇寒疼痛等疾病患者迅速止痛的即开即用型产品。详解暖宝宝的使

卷发器怎么用?图解卷发器的使用方法 卷发器的使用方法图解

卷发器怎么用?图解卷发器的使用方法——简介很多MM都有卷发情结,漂亮的卷发成了众多爱美女士的追求,对于爱美的MM们来说,卷发器已经成为了可以自己动手打造百变造型的重要家电产品。不用去发廊就可以自己做出漂亮的卷发,下面就跟随小编一

MFC多线程CWinThread的使用方法转 cwinthread run

CWinThread的使用方法CWinThread类成员数据成员 m_bAutoDelete 指定线程结束时是否要销毁对象 m_hThread 当前线程的句柄 m_nThreadID 当前线程的ID m_pMainWnd 保存指向应用程序的主窗口的指针 m_pActiveWnd 指向容器应用程序的主

声明:《SurfaceView类的使用 surfaceview 透明》为网友一个人的生计分享!如侵犯到您的合法权益请联系我们删除