android SurfaceView 使用示例 why SurfaceView上 不可以用控件的轻微解释_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > android SurfaceView 使用示例 why SurfaceView上 不可以用控件的轻微解释

android SurfaceView 使用示例 why SurfaceView上 不可以用控件的轻微解释

 2010/11/26 8:07:18  byandby  http://byandby.javaeye.com  我要评论(0)
  • 摘要:当我们需要开发一个复杂的游戏的时候,而且对程序的执行效率要求很高时,View类就不能满足需求了,这时必须用SurfaceView类进行开发。例如,对速度要求很高的游戏时,View类就不能满足需求了,这时必须使用SurfaceView类进行开发。例如,对速度要求很高的游戏,可以使用双缓冲来显示。游戏中的背景、人物、动画等都需要绘制在一个画布(Canvas)上,而SurfaceView可以直接访问一个画布,SurfaceView是提供给需要直接画像素而不是使用窗体部件的应用使用的
  • 标签:SurfaceView使用示例
    当我们需要开发一个复杂的游戏的时候,而且对程序的执行效率要求很高时,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方法进行绘图,同时也支持完全的OpenGL ES库。

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

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

2. 创建一个新的SurfaceView控件

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

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

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

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


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



    GameSurfaceView 类
package xiaohang.zhimeng;

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

public class GameSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{

	//控制循环
	boolean mbLoop = false;

	//定义SurfaceHolder对象
	SurfaceHolder mSurfaceHolder = null;
	int miCount = 0;
	int y =50;
	
	public GameSurfaceView(Context context) {
		super(context);
		//实例化SurfaceHolder
		mSurfaceHolder = this.getHolder();
		
		//添加回调 函数
		//注意这里这句 mSurfaceHolder.addCallback(this)这句执行完了之后 
		//马上就会回调 surfaceCreated方法了 然后开启线程 执行绘图方法这里这个执行顺序要搞清楚
		mSurfaceHolder.addCallback(this);
		this.setFocusable(true);
		mbLoop = true;
	}
	
	//在surface的大小发生改变时激发
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
	}
	
	//surface创建时激发 此方法在主线程总执行
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		//开启绘图线程
		new Thread(this).start();
	}
	
	//在surface销毁时激发
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		//停止循环
		mbLoop = false;
	}
	
	//绘图循环
	@Override
	public void run() {
		while (mbLoop){
			try {
				Thread.sleep(200);
			} catch (Exception e) {
				
			}
			//至于这里为什么同步这就像一块画布 你不能让两个人同时往上边画画
			synchronized( mSurfaceHolder ){
				Draw();
			}
		}
	}
	//绘图方法  注意这里是另起一个线程来执行绘图方法了不是在UI 线程了
	public void Draw(){
		//锁定画布,得到canvas 用SurfaceHolder对象的lockCanvas方法
		Canvas canvas = mSurfaceHolder.lockCanvas();
		if (mSurfaceHolder==null || canvas == null) {
			return;
		}
		if (miCount < 100) {
			miCount++;
		}else {
			miCount = 0;
		}
		//绘图
		Paint mPaint = new Paint();
		//给Paint对象加上抗锯齿标志
		//[img]http://tech.techweb.com.cn/thread-459611-1-1.html[/img] 详细说明可以看看这里 --->大家直接复制链接到 浏览器打开吧。
		mPaint.setAntiAlias(true);
		mPaint.setColor(Color.BLACK);
		//绘制矩形---清屏作用
		canvas.drawRect(0, 0, 320, 480, mPaint);
		switch (miCount % 4) {
		case 0:
			mPaint.setColor(Color.BLUE);
			break;
		case 1:
			mPaint.setColor(Color.GREEN);
			break;
		case 2:
			mPaint.setColor(Color.RED);
		case 3:
			mPaint.setColor(Color.YELLOW);
		default:
			mPaint.setColor(Color.WHITE);
			break;
		}
		//绘制矩形
		canvas.drawCircle((320 - 25) / 2, y, 50, mPaint);
		//绘制后解锁,绘制后必须解锁才能显示
		mSurfaceHolder.unlockCanvasAndPost(canvas);
	}
}


Activity01 类
package xiaohang.zhimeng;

import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;

public class Activity01 extends Activity {
	GameSurfaceView mGameSurfaceView;
	
	
	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //创建GameSurfaceView对象
        mGameSurfaceView = new GameSurfaceView(this);
        
        //设置显示GameSurfaceView视图
        setContentView(mGameSurfaceView);
    }
	
	//触笔事件 返回值为true 父视图不做处理以下返回值为true的都是不做处理的
	public boolean onTouchEvent(MotionEvent event){
		return true;
	}
	//按键按下事件  
	public boolean onKeyDown(int keyCode, KeyEvent event){
		return true;
	}
	//按键弹起事件
	public boolean onKeyUp(int keyCode, KeyEvent event){
		switch (keyCode) {
		//上方向键
		case KeyEvent.KEYCODE_DPAD_UP:
			mGameSurfaceView.y-=3;
			break;
		//下方向键
		case KeyEvent.KEYCODE_DPAD_DOWN:
			mGameSurfaceView.y+=3;
			break;
		}
		return false;
	}
	public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event){
		return true;
	}
}


  写完这个例子 我有些疑问
可不可以 在 类似这种 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的源码贴上来 大家 看看。。
@RemoteView
public class Button extends TextView {
    public Button(Context context) {
        this(context, null);
    }

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

    public Button(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
}

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


我想大家已经知道 为什么 SurfaceView 上边不可以放一些我们常用的控件了 也知道为什么我们可以再Activity类 里边可以 new Button 而在 SurfaceView 类里边却不行 因为我们的Activity类本身就是一个 Context对象。 而我们的 SurfaceView本身 不是 一个Context对象。 而我们要new 一个Button 怎么都得需要一个Context对象。 从这个角度其实就可以解释为什么 我们不能再SurfaceView上边放一些我们常用的控件了 Button TextView之类的。
  就写到这里。
源代码 附件。 测试平台 android 2.0
  • 相关文章
发表评论
用户名: 匿名