SurfaceView

SurfaceView:执行效率高。SurfaceView可以直接访 问一个画布(Canvas),SurfaceView是提供给需要直接画像素而不是使用窗体部件的应用使用的。Android图形系统中的一个重要概念是 Surface,View及其子类都要画在Surface上。每个Surface创建一个Canvas对象,用来管理View在Surface上的绘图操 作。

在使用SurfaceView开发时需要注意,使用它绘图时,一般都是出现在顶层。使用时还需要对其进行创建、销毁,情况改变时进行监视,这 就是实现SurfaceHolder.Callback接口,如果要对被绘制的画布进行剪裁。控制其大小时都需要使用SurfaceHolder来完成处 理。在程序中,SurfaceHolder对象需要使用getHolder方法来获得,同时还需要addCallback方法来添加“回调函数”。
1)surfaceChanged:在Surface的大小发生改变时激发。
2)surfaceCreated:创建Surface时激发。
3)SurfaceDestroyed:销毁Surface时激发。
4)addCallback:给SurfaceView添加一个回调函数
5)lockCanvas:锁定画布,绘图之前必须锁定画布才能得到当前的画布对象
6)unlockCanvasAndPost:
7)removeCallback:从SurfaceView中移除回调函数。

SurfaceView和View的明显不同之处在于,SurfaceView不需要通过线程来更新视图。

public class GameSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable

SurfaceHolder mSurfaceHolder=this.getHolder();

mSurfaceHolder.addCallback(this);

public void surfaceCreated(SurfaceHolder holder)
{
 new Thread(this).start();
}

public void run()
{
 while (mbLoop)
 {
  try {
   Thread.sleep(200);
  }
  catch (Exception e) {}
  synchronized(mSurfaceHolder)
 {
   Draw();  
  }
 }
}

public void Draw()
{
 Canvas canvas=mSurfaceHolder.lockCanvas();
 ...
}

 

//=====================================

SurfaceView是View的子类,他和View最本质的区别在于,使用SurfaceView可以在一个新起的单独线程中重新绘制画面而View则必须在UI的主线程中更新画面:

根据游戏特点,View和SurfaceView分布应用于以下情况:

1,被动更新画面的:这类游戏往往都是由用户来触发页面更新的,是一种简单的交互,比如棋类游戏,这种游戏用View就可以了。

2,主动更新画面的:比如背景的持续更新,或者一个人在一直跑到等。这些动作需要一个单独的线程不停的绘制人的状态,为了避免阻塞UI主线程,所以需要SurfaceView来控制。

在实际使用SurfaceView时,应注意以下几点:

1,使用后台线程进行绘图的代价是额外的内存消耗,所以使用SurfaceView的时候仍然要保持谨慎。

2,SurfaceView 是提供给需要直接画像素而不是使用窗体部件的应用使用的。也就是说,我们无法在SurfaceView中直接使用TextView等页面组件。但是可以利 用Surface的纵深排序的特性,使其他页面组件看来其就像是显示在SurfaceView之上。

 

SurfaceView提供了一个专用的绘画Surface,并且他可以将Surface放到屏幕的准确位置,我们则可以控制Surface的格式和大小。

Surface 是纵深排序的(Z-ordered),这表明他总在自己所在窗口的后面。SurfaceView提供了一个可见区域,只有在这个区域内的Surface可 可见,可见区域外的部分不可见。Surface的排版显示受到视图层级关系的影响,他的兄弟视图结点会在顶端显示。这意味着Surface的内容会被他的 兄弟视图遮挡。利用这一特性,我们可以用来在其上放置遮盖物,例如,文本和按钮等控件。但是,如果Surface上面有透明控件,那么他的每次变化都会引 起框架重新计算他和顶层控件的透明效果,这会影响性能。

每个Surface都会创建一个Canvas对象,用来管理View在Surface上的绘图操作,他支持使用所有标准Canvas方法进行绘图,同时也支持万全的OpenGLES库。

Surface会在SurfaceView显示之后被创建,在SurfaceView隐藏之前被销毁。

 

要访问Surface必须通过SurfaceHolder接口,使用SurfaceView.getHolder方法可以SurfaceHolder接口对象。

SurfaceHolder接口的几个常用方法如下:

1,addCallback(SurfaceHolder.Callback callback):为当前的SurfaceView设置一个回调对象,SurfaceView将会在Surface改变时调用该回调。

2,getSurface() 获得一个可直接访问的Surface对象

3,lockCanvas() 锁定Surface并返回一个Canvas对象,其大小为整个View

4,unlockCanvasAndPost(Canvas canvas):释放指定Surface对象上的锁,并把Canvas对象发送到SurfaceView进行画面更新。

5,lockCanvas(Rect dirty):锁定Surface并返回一个Canvas对象,其大小为指定矩形,只对失效区域进行重绘,可以提高速度。


需要注意的是,以上方法(addCallbacl除外)必须在Surface对象被创建之后和销毁之前执行,否则将不能获取正确的Surface和Canvas对象,返回值为null。

 

为了保证可以正确的操作Canvas对象,我们对Canvas的任何操作都必须要在Surface被创建之后和销毁之前执行。此时,就需要用到SurfaceHolder.Callback接口,Surface会在自己状态发生变化时通知该接口。

SurfaceHolder.Callback接口的3个方法如下:

1)surfaceChanged(SurfaceHolder holder,int format,int width,int height) 当Surface的状态(大小和格式)发生变化的时候调用该函数,例如竖屏改为横屏时。在surface创建后该函数至少会被调用一次。

2)surfaceCreated(SurfaceHolder holder):当Surface第一次创建后会立即调用该函数。程序可以在函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface。

3)surfaceDestroyed(SurfaceHolder holder):当Surface被销毁前调用该函数,该函数被调用后就不能继续使用Surface了,一班在该函数中来清理使用的资源。

 

构建SurfaceView应用的一般步骤:

1)创建一个新类继承子SurfaceView,并实现SurfaceHolder.Callback接口。

2)创建一个可以启动新线程的类或函数,例如一个继承自Thread的子类或者实现Runnable接口的类,该类做为完成绘图任务的线程类。

3)在surfaceCreated回调函数中启动绘图线程,开始绘制画面

4)在surfaceDestroyed中,终止绘图线程,释放资源

5)为了增加绘图线程的可控性,往往还会添加启动线程和终止线程的方法,供外部调用。

 

1)创建一个继承SurfaceView的类---MySurfaceView,并实现SurfaceHolder.Callback接口

在 创建SurfaceView子类时需要注意,如果该子类不会被应用于layout.xml配置文件中,则子类只需要实现 SurfaceView(Context context)构造即可;如果该子类需要被应用于layout.xml配置文件中,则还必须实现SurfaceView(Context context,AttributeSet attrs)构造函数,并且该函数必须具有public访问修饰符。

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

 private SurfaceHolder holder;

 private boolean hasSurface;

 public MySurfaceView(Context context) {

 super(context);

 init();

}

public MySurfaceView(Context context,AttributeSet attrs) {

 super(context,attrs);

 init();

}

public MySurfaceView(Context context,AttributeSet attrs,int DefStyle){

 super(context,attrs,defStyle);

 init();

}

private void init(){

 holder=getHolder();

 holder.addCallback(this);

 hasSurface=false;

}

public void surfaceCreated(SurfaceHolder holder){

 hasSurface=true;

}

public void surfaceDestroyed(SurfaceHolder holder){

 hasSurface=false;

}

public void surfaceChanged(SurfaceHolder holder,int format,int w,int h){

}

}

}

 

2)创建一个可以启动新线程的类或函数,例如一个继承自Thread的子类或者实现Runnable接口的类,该类做为完成绘图任务的线程类。

 

class MySurfaceViewThread extends Thread{

 private boolean done;

 MySurfaceViewThread(){

 super();

 done=false;

}

@Override

public void run(){

 //重复绘图,直到线程停止

  while(!done) {

   Canvas canvas=holder.lockCanvas();

  holder.unlockCanvasAndPost(canvas);

 }

}

public void requestExit(){

//把这个线程标记为完成,并合并到主线程中

done=true;

try {

 join();

}catch (InterruptedException ex) {

}

}

public void onWindowResize(int w,int h){

//超过护理可用的屏幕尺寸的改变

}

}

 

3,在surfaceCreated回调函数中启动绘图 线程,开始绘制画面

首先在MySurfaceView类中添加一个私有变量,保存对于线程的引用。

private MySurfaceViewThread mySurfaceViewThread;

然后在surfaceCreated回调函数中启动线程

public void surfaceCreated(SurfaceHolder holder){

 hasSurface=true;

 if(mySurfaceViewThread!=null) {

 mySurfaceViewThread.start();

}

}

 

4),在surfaceDestroyed中,停止绘图线程,释放资源

public void surfaceDestroyed(SurfaceHolder holder){

hasSurface=false;

if (mySurfaceViewThread!=null){

 mySurfaceViewThread.requestExit();

 mySurfaceViewThread=null;

}

 

5)为了增加绘图线程的可控性,往往还会添加启动线程和终止线程的方法,供外部调用。

大多数情况下,绘图线程还需要外部干预,比如人为的停止绘画和重新开始等,所以还需要把线程的控制方法提供给外部调用。因此,还需要增加以下两个方法:

public void start() {

 if (mySurfaceViewThread==null) {

 mySurfaceViewThread=new MySurfaceViewThread();

 if (hasSurface==true){

  mySurfaceViewThread.start();

}

}

}

public void stop(){

if (mySurfaceViewThread!=null){

 mySurfaceViewThread.requestExit();

 mySurfaceViewThread=null;

}

}

以上代码描述了使用SurfaceView的大致框架和步骤,在实际应用时还要根据实际情况作调整,例如可以使用Runnable或TimerTask代替线程类;如果该绘图过程没有其他因素参与,则可以省略最后一步。

 

2,使用SurfaceView绘制正弦曲线

1)创建新的Android项目,名称为SinCurve,并选择创建SinCurveActivity,包名为com.example。该类同时实现了onClickListener接口。

2)修改布局文件main.xml,代码如下:

<LinearLayout ... orientation="vertical" layout_width="match_parent" layout_height="match_parent">

<RelativeLayout layout_width="match_parent" layout_height="match_parent">

<com.example.SinCurveSurfaceView android:id="@+id/mySurfaceView" layout_width="match_parent" layout_height="match_parent" />

<Button android:id="@+id/btnStart" layout_height="wrap_content" layout_width="wrap_content" layout_alignParentBottom="true" />

<Button android:id="@+id/btnStop" layout_toRightOf="@id/btnStart" />

</RelativeLayout>

</LinearLayout>

注意:在main.xml中,两个按钮与SurfaceView处于同级的节点,但是利用Surface的纵深排序的特性,他们看起来就像是处于SurfaceView之上。

 

3)新建名为SinCurveSurfaceView的类,包名为com.example。参照前面的代码,代码如下:

public class SinCurveSurfaceView extends SurfaceView implements SurfaceHolder.Callback{

private SurfaceHolder holder;

private SinCurveThread myThread;

private boolean hasSurface;

 

private int Y_axis[];//保存正弦波的Y轴上的点

private int currentX;//当前绘制到的X轴上的点

private int centerY;//Y轴中心线

private int oldX;//上一个X点

private int oldY;//上一个Y点

 

public SinCurveSurfaceView(Context context) {

super(context);

init();

}

public SinCurveSurfaceView(Context context,AttributeSet attrs){

super(context,attrs);

init();

}

public SinCurveSurfaceView(Context context,AttributeSet attrs,int defStyle){

super(context,attrs,defStyle);

init();

}

private void init(){

holder=getHolder;

holder.addCallback(this);

hasSurface=false;

}

public void start(){

if(myThread==null){

myThread=new SinCurveThread();

if (hasSurface==true) myThread.start();

}

}

public void stop(){

if (myThread!=null){

myThread.requestExit();

myThread=null;

}

}

public void surfaceCreated(SurfaceHolder holder){

 centerY=(getHeight()-getTop())/2;

Y_axis= new int[getWidth()];

for (int i=1;i<Y_axis.length;i++){

Y_axis[i-1]=centerY-(int)(100*Math.sin(i*2*Math.PI/180));

}

oldY=centerY;

hasSurface=true;

if(myThread!=null) myThread.start();

}

public void surfaceDestroyed(SurfaceHolder holder){

hasSurface=false;

stop();

}

public void surfaceChanged(SurfaceHolder holder,int format,int w,int h){

if (myThread!=null) myThread.onWindowsResize(w,h);

}

class SinCurveThread extends Thread {

 private boolean done;

 SinCurveThread() {

 super();

 done=false;

}

@Override

public void run(){

while (!done) {

Canvas canvas=holder.lockCanvas(new Rect(oldX,0,oldX+currentX,getHeight()));

DrawSin(canvas);

holder.unlockCanvasAndPost(canvas);

clearDraw();

}

}

private void DrawSin(Canvas canvas){

if (currentX==0) oldX=0;

Paint mPaint=new Paint();

mPaint.setColor(Color.GREEN);

mPaint.setStrokenWdith(2);

 

int y;

for (int i=oldX+1;i<currentX;i++){

y=Y_axis[i-1];

canvas.drawLine(oldX,oldY,i,y,mPaint);

oldX=i;

oldY=y;

}

}

 

private void ClearDraw() {

currentX++;

if (currentX==Y_axis.length-1){

 Canvas canvas=holder.lockCanvas(null);

canvas.drawColor(Color.BLACK);

holder.unlockCanvasAndPost(canvas);

currentX=0;

oldY=centerY;

}

 

}

public void requestExit(){

 done=true;

 try {

  join();

}catch(InterruptedException ex){

}

}

public void onWindowResize(int w,int h){

//处理可用的屏幕尺寸的改变

}

}

}

 

5)在Activity创建时,为开始和停止按钮编写Click事件代码

@Override

public void onClick(View v) {

 switch (v.getId()) {

 case R.id.btnStart:

  sinCurveView.start();

  break;

case R.id.btnStop:

 sinCurveView.stop();

 break;

default:

 break;

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值