Android的SurfaceView使用
Android的大多数控件都是继承自View的,因此在自定义控件时一般也是继承View类,但是对于高效的,游戏级别的绘图,或者是播放器等要求比较高的地方,普通的View类就有点吃不开了,这个时候就要用到SurfaceView类。因为比较高级,所以就该装B,单纯一个继承自SurfaceView类是不行的,必须实现一个SurfaceHolder.Callback接口来指明SurfaceView创建、改变、删除时的回调方法,并且在SurfaceView中通过一个SurfaceHolder对象来控制SurfaceView。
如果将该SurfaceView作为某个Activity的全屏View,则直接调用setContentView(new MyView());就好了;但若是作为屏幕View的一部份,就应该修改对应的layout XML文件了,添加类似的布局代码:
<com.android.sv.MyView
android:id="@+id/sv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
其中com.example.fq.MyView为对应自定义类的全名。
由于默认的XML文件解析方法是调用View的View(Context , AttributeSet )构造函数构造View,因此你的自定义SurfaceView中也应该有一个参数为(Context , AttributeSet )的构造函数,并且在构造函数中执行父类的对应函数super( Context , AttributeSet )。在绘图时,必须首先用Canvas c=holder.lockCanvas();锁定并获得画布,随后进行绘制,再调用holder.unlockCanvasAndPost(c);将绘制内容进行呈现
下面是个例子运行图:
主界面:
- public class MainActivity extends Activity {
- private MyView mSV=null;
- private Button mBtnOk;
- private Button mBtnCancle;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mSV=(MyView)findViewById(R.id.sv);
- mSV.setTag(true);
- mBtnOk=(Button)findViewById(R.id.btnok);
- mBtnOk.setOnClickListener(new OnClickListener() {
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- mSV.setTag(true);
- }
- });
- mBtnCancle=(Button)findViewById(R.id.btnCancel);
- mBtnCancle.setOnClickListener(new OnClickListener() {
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- mSV.setTag(false);
- }
- });
- }
- }
MyTest界面:
- public class MyView extends SurfaceView implements SurfaceHolder.Callback{
- private SurfaceHolder holder=null; //控制对象
- private Vector<Float> xs=new Vector<Float>();
- private Vector<Float> ys=new Vector<Float>();
- public MyView(Context context, AttributeSet attrs) {
- super(context, attrs);
- holder=getHolder();
- holder.addCallback(this);
- }
- public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
- // TODO Auto-generated method stub
- }
- public void surfaceCreated(SurfaceHolder arg0) {
- // TODO Auto-generated method stub
- new Thread(new MyLoop()).start();//在 View 中系统不允许主线程外的线程控制 UI .但是 SurfaceView 却可以
- }
- public void surfaceDestroyed(SurfaceHolder arg0) {
- // TODO Auto-generated method stub
- }
- public void doDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- super.onDraw(canvas);
- canvas.drawColor(Color.WHITE);//这里是绘制背景
- Paint p=new Paint(); //笔触
- p.setAntiAlias(true); //反锯齿
- p.setColor(Color.BLACK);
- p.setStyle(Style.STROKE);
- int a=xs.size();
- for(int i=0;i<xs.size();i++)
- canvas.drawCircle(xs.elementAt(i),ys.elementAt(i),10, p);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // TODO Auto-generated method stub
- if(event.getAction()==MotionEvent.ACTION_DOWN){
- String a=this.getTag().toString();
- if(a.equals("true")){
- xs.add(event.getX());
- ys.add(event.getY());
- }
- }
- return true;
- }
- class MyLoop implements Runnable{
- //熟悉游戏编程的应该很面熟吧,主循环
- public void run() {
- // TODO Auto-generated method stub
- while(true){
- try{
- Canvas c=holder.lockCanvas();
- doDraw(c);
- holder.unlockCanvasAndPost(c);
- Thread.sleep(20);
- }catch(Exception e){
- }
- }
- }
- }
- }
main.xml文件:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <com.android.sv.MyView
- android:id="@+id/sv"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- />
- <Button android:id="@+id/btnok"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="开启绘图"/>
- <Button android:id="@+id/btnCancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/btnok"
- android:text="取消绘图"/>
- </RelativeLayout>