android 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 of correctly compositing
with the Surface any siblings of the SurfaceView that would normally appear on top of it.

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


例子:

public class BBatt extends SurfaceView implements
SurfaceHolder.Callback, OnKeyListener {
private BFairy bFairy;
private DrawThread drawThread;
public BBatt(Context context) {
super(context);
this.setLayoutParams(
new ViewGroup.LayoutParams(
Global.battlefieldWidth, Global.battlefieldHeight));
this.getHolder().addCallback( this );
this.setFocusable( true );
this.setOnKeyListener( this );
bFairy = new BFairy(this.getContext());
}
public void surfaceChanged(SurfaceHolder holder,
int format,int width,int height) {
drawThread = new DrawThread(holder);
drawThread.start();
}
public void surfaceDestroyed(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;
/*
* SurfaceView的示例程序
* 演示其流程
*/
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 implements SurfaceHolder.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, int width,
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 can get
// touch events and key events,and
// "setFocusable()" to make sure we
// can get key events
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
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);
}

}

 

转帖:http://hi.baidu.com/cq_yajun/blog/item/c989e45651c904133a293559.html/cmtid/472f45f7e9562d2bbc3109db

======================================================================================

SurfaceView由于可以直接从内存或者DMA等硬件接口取得图像数据,因此是个非常重要的绘图容器,这次我就用两篇文章来介绍SurfaceView的用法。网上介绍SurfaceView的用法有很多,写法也层出不同,例如继承SurfaceView类,或者继承SurfaceHolder.Callback类等,这个可以根据功能实际需要自己选择,我这里就直接在普通的用户界面调用SurfaceHolder的lockCanvas和unlockCanvasAndPost。

        先来看看程序运行的截图:

截图1主要演示了直接把正弦波绘画在SurfaceView上

对比上面的左右两图,右图用.lockCanvas(null),而左图用.lockCanvas(new Rect(oldX, 0, oldX + length,
    getWindowManager().getDefaultDisplay().getHeight())),对比一下两个效果,由于左图是按指定Rect绘画,所以效率会比右图的全控件绘画高些,并且在清屏之后(canvas.drawColor(Color.BLACK))不会留有上次绘画的残留。

 

接下来贴出main.xml的源码:

view plaincopy to clipboardprint?
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android
    android:layout_width="fill_parent" android:layout_height="fill_parent" 
    android:orientation="vertical"> 
 
    <LinearLayout android:id="@+id/LinearLayout01" 
        android:layout_width="wrap_content" android:layout_height="wrap_content"> 
        <Button android:id="@+id/Button01" android:layout_width="wrap_content" 
            android:layout_height="wrap_content" android:text="简单绘画"></Button> 
        <Button android:id="@+id/Button02" android:layout_width="wrap_content" 
            android:layout_height="wrap_content" android:text="定时器绘画"></Button> 
    </LinearLayout> 
    <SurfaceView android:id="@+id/SurfaceView01" 
        android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView> 
</LinearLayout> 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent" android:layout_height="fill_parent"
 android:orientation="vertical">

 <LinearLayout android:id="@+id/LinearLayout01"
  android:layout_width="wrap_content" android:layout_height="wrap_content">
  <Button android:id="@+id/Button01" android:layout_width="wrap_content"
   android:layout_height="wrap_content" android:text="简单绘画"></Button>
  <Button android:id="@+id/Button02" android:layout_width="wrap_content"
   android:layout_height="wrap_content" android:text="定时器绘画"></Button>
 </LinearLayout>
 <SurfaceView android:id="@+id/SurfaceView01"
  android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView>
</LinearLayout>
 

接下来贴出程序源码:

view plaincopy to clipboardprint?
package com.testSurfaceView;  
 
import java.util.Timer;  
import java.util.TimerTask;  
 
import android.app.Activity;  
import android.graphics.Canvas;  
import android.graphics.Color;  
import android.graphics.Paint;  
import android.graphics.Rect;  
import android.os.Bundle;  
import android.util.Log;  
import android.view.SurfaceHolder;  
import android.view.SurfaceView;  
import android.view.View;  
import android.widget.Button;  
 
public class testSurfaceView extends Activity {  
    /** Called when the activity is first created. */ 
    Button btnSimpleDraw, btnTimerDraw;  
    SurfaceView sfv;  
    SurfaceHolder sfh;  
 
    private Timer mTimer;  
    private MyTimerTask mTimerTask;  
    int Y_axis[],//保存正弦波的Y轴上的点  
    centerY,//中心线  
    oldX,oldY,//上一个XY点   
    currentX;//当前绘制到的X轴上的点  
 
    @Override 
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
 
        btnSimpleDraw = (Button) this.findViewById(R.id.Button01);  
        btnTimerDraw = (Button) this.findViewById(R.id.Button02);  
        btnSimpleDraw.setOnClickListener(new ClickEvent());  
        btnTimerDraw.setOnClickListener(new ClickEvent());  
        sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);  
        sfh = sfv.getHolder();  
 
        //动态绘制正弦波的定时器  
        mTimer = new Timer();  
        mTimerTask = new MyTimerTask();  
 
        // 初始化y轴数据  
        centerY = (getWindowManager().getDefaultDisplay().getHeight() - sfv  
                .getTop()) / 2;  
        Y_axis = new int[getWindowManager().getDefaultDisplay().getWidth()];  
        for (int i = 1; i < Y_axis.length; i++) {// 计算正弦波  
            Y_axis[i - 1] = centerY  
                    - (int) (100 * Math.sin(i * 2 * Math.PI / 180));  
        }  
    }  
 
    class ClickEvent implements View.OnClickListener {  
 
        @Override 
        public void onClick(View v) {  
 
            if (v == btnSimpleDraw) {  
                SimpleDraw(Y_axis.length-1);//直接绘制正弦波  
              
            } else if (v == btnTimerDraw) {  
                oldY = centerY;  
                mTimer.schedule(mTimerTask, 0, 5);//动态绘制正弦波  
            }  
 
        }  
 
    }  
 
    class MyTimerTask extends TimerTask {  
        @Override 
        public void run() {  
 
            SimpleDraw(currentX);  
            currentX++;//往前进  
            if (currentX == Y_axis.length - 1) {//如果到了终点,则清屏重来  
                ClearDraw();  
                currentX = 0;  
                oldY = centerY;  
            }  
        }  
 
    }  
      
    /* 
     * 绘制指定区域 
     */ 
    void SimpleDraw(int length) {  
        if (length == 0)  
            oldX = 0;  
        Canvas canvas = sfh.lockCanvas(new Rect(oldX, 0, oldX + length,  
                getWindowManager().getDefaultDisplay().getHeight()));// 关键:获取画布  
        Log.i("Canvas:",  
                String.valueOf(oldX) + "," + String.valueOf(oldX + length));  
 
        Paint mPaint = new Paint();  
        mPaint.setColor(Color.GREEN);// 画笔为绿色  
        mPaint.setStrokeWidth(2);// 设置画笔粗细  
 
        int y;  
        for (int i = oldX + 1; i < length; i++) {// 绘画正弦波  
            y = Y_axis[i - 1];  
            canvas.drawLine(oldX, oldY, i, y, mPaint);  
            oldX = i;  
            oldY = y;  
        }  
        sfh.unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像  
    }  
 
    void ClearDraw() {  
        Canvas canvas = sfh.lockCanvas(null);  
        canvas.drawColor(Color.BLACK);// 清除画布  
        sfh.unlockCanvasAndPost(canvas);  
 
    }  

package com.testSurfaceView;

import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

public class testSurfaceView extends Activity {
 /** Called when the activity is first created. */
 Button btnSimpleDraw, btnTimerDraw;
 SurfaceView sfv;
 SurfaceHolder sfh;

 private Timer mTimer;
 private MyTimerTask mTimerTask;
 int Y_axis[],//保存正弦波的Y轴上的点
 centerY,//中心线
 oldX,oldY,//上一个XY点
 currentX;//当前绘制到的X轴上的点

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  btnSimpleDraw = (Button) this.findViewById(R.id.Button01);
  btnTimerDraw = (Button) this.findViewById(R.id.Button02);
  btnSimpleDraw.setOnClickListener(new ClickEvent());
  btnTimerDraw.setOnClickListener(new ClickEvent());
  sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);
  sfh = sfv.getHolder();

  //动态绘制正弦波的定时器
  mTimer = new Timer();
  mTimerTask = new MyTimerTask();

  // 初始化y轴数据
  centerY = (getWindowManager().getDefaultDisplay().getHeight() - sfv
    .getTop()) / 2;
  Y_axis = new int[getWindowManager().getDefaultDisplay().getWidth()];
  for (int i = 1; i < Y_axis.length; i++) {// 计算正弦波
   Y_axis[i - 1] = centerY
     - (int) (100 * Math.sin(i * 2 * Math.PI / 180));
  }
 }

 class ClickEvent implements View.OnClickListener {

  @Override
  public void onClick(View v) {

   if (v == btnSimpleDraw) {
    SimpleDraw(Y_axis.length-1);//直接绘制正弦波
   
   } else if (v == btnTimerDraw) {
    oldY = centerY;
    mTimer.schedule(mTimerTask, 0, 5);//动态绘制正弦波
   }

  }

 }

 class MyTimerTask extends TimerTask {
  @Override
  public void run() {

   SimpleDraw(currentX);
   currentX++;//往前进
   if (currentX == Y_axis.length - 1) {//如果到了终点,则清屏重来
    ClearDraw();
    currentX = 0;
    oldY = centerY;
   }
  }

 }
 
 /*
  * 绘制指定区域
  */
 void SimpleDraw(int length) {
  if (length == 0)
   oldX = 0;
  Canvas canvas = sfh.lockCanvas(new Rect(oldX, 0, oldX + length,
    getWindowManager().getDefaultDisplay().getHeight()));// 关键:获取画布
  Log.i("Canvas:",
    String.valueOf(oldX) + "," + String.valueOf(oldX + length));

  Paint mPaint = new Paint();
  mPaint.setColor(Color.GREEN);// 画笔为绿色
  mPaint.setStrokeWidth(2);// 设置画笔粗细

  int y;
  for (int i = oldX + 1; i < length; i++) {// 绘画正弦波
   y = Y_axis[i - 1];
   canvas.drawLine(oldX, oldY, i, y, mPaint);
   oldX = i;
   oldY = y;
  }
  sfh.unlockCanvasAndPost(canvas);// 解锁画布,提交画好的图像
 }

 void ClearDraw() {
  Canvas canvas = sfh.lockCanvas(null);
  canvas.drawColor(Color.BLACK);// 清除画布
  sfh.unlockCanvasAndPost(canvas);

 }
}

注意一下 for (int i = oldX + 1; i < length; i++) {// 绘画正弦波 这句,在.lockCanvas()指定Rect内减少循环画线的次数,可以提高绘图效率。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hellogv/archive/2010/11/03/5985090.aspx

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值