现在的位置: 首页 > 综合 > 正文

关于SurfaceView的使用

2013年08月19日 ⁄ 综合 ⁄ 共 3296字 ⁄ 字号 评论关闭

    最近在使用SurfaceView进行自定义控件开发,众所周知,仅仅是利用android配给我们的控件已经无法满足我们日益强烈的开发需要,比如开发游戏的相关界面,为了界面的美观以及简洁,我们不得不使用自定义控件。于是我们必须掌握自定义控件的变成原理。但是在网上,全部都是千篇一律的例子,想要学到点什么都是很难的,为此我走了很多弯路,下下这篇文章,希望后面的人能看到,不要和我一样在百度上找垃圾文章,耽误了自己的学习时间,当然聪明的人还可以使用github进行实例查找,但是英文功底要过关。

    SurfaceView其实是View的子类,但是SurfaceView拥有比View更加完善以及更加便利的操作,因为SurfaceView嵌入了Surface(说白了就是一个SurfaceHolder,在使用的时候,用的都是SurfaceHolder以及Canvas)并且由于是View的子类所以拥有View的所有功能,并且在这里SurfaceView较之View来说更加灵活,虽说View也可以不用extends成一个自定义的新类,但是使用的时候很麻烦,因为在使用他的函数进行大小,颜色设置上有些函数残缺,甚至有些找不到,这就定了他的使用自由度,因此使用View生成子类是并非好的选择。虽然SurfaceView是从他继承来的,故而事实上View更加小型化,但是View的子类就只能在主UI线程中实现,而SurfaceView没有这方面的限制,他可以在在新的线程上运行也可以在主线程运行。若是在新线程运行,则在实现SurfdaceView时,无论是利用生成子类的方法,还是直接使用时添加子类的各种需要进行直接地生成(这个方法关键在于对生成的SurfaceView对象使用addCallback(),getholder(),unlockCanvas()等方法进行必要的方法动态加载,应该注意的点就是只要有用到SurfaceHolder那么就会用到创建新线程,增加回调,这是不可避免的,必须添加的,因为程序规定了这么做,不这么做就会出错),若是在主线程中运行,也就是说,没用到创建一个新线程,那么我们也就没有必要增加回调,创建线程,这个时候也就没用到SurfaceHolder了,这个时候我们就可以直接对创建了的SurfaceView对象进行图片加载,但是其实用的方法是从View继承来的,就是使用setBackgroundDrawable(new
BitmapDrawable(Bitmap))(括号里的BitmapDrawable是Drawable的子类,Drawable本身加载不了Bitmap但是setBackgroundDrawable方法括号里面的对象是Drawable类型的,旨在方便加载各种Drawable对象)这种方法本质上和使用View没区别,可以作为一种技巧使用,但是若真要用这种方法,可以直接使用View子类,现实中的使用因人而异。

    最后由于网上对于SuefaceView进行继承后当做子类使用的例子太多了,这里我举一个小型的例子,对SurfaceView的使用进行简洁地介绍,加深对SurfaceView的理解。下面是程序代码

package com.example.mysurafce;

import java.io.IOException;

import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.RelativeLayout;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;

public class MainActivity extends Activity {
//程序运行出来是黑底的,因为图片大小没限制,所以图片大小就覆盖了整个屏面

private SurfaceHolder h;
private Bitmap bm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout l=new RelativeLayout (this);
setContentView(l);

try{
//这个加载图片的方式基本上是最好的了,getAssets().open耗内存最少,产生的是InputStream
bm=BitmapFactory.decodeStream(getAssets().open("y.png"));

}catch(IOException e){
e.printStackTrace();
}

SurfaceView sur=new SurfaceView(this);
h=sur.getHolder();
h.addCallback(new SurfaceHolder.Callback(){
@Override
public void surfaceCreated(SurfaceHolder arg0) {
new Thread(new MyRunnable()).start();
}
@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
int arg3) {
}
@Override
public void surfaceDestroyed(SurfaceHolder arg0) {

}

class MyRunnable implements Runnable{

public void run(){
Canvas canvas=h.lockCanvas();
canvas.drawBitmap(bm, 0,0,null);
h.unlockCanvasAndPost(canvas);

}

}

});
l.addView(sur);


}

}

在代码中,加入了新的线程,因为SurfaceView本身必须要使用另外的线程进行加载界面的,所以新线程必不可少,否则报错,另外,新线程必须经由另外的出发点来触发,不然,报错,就像直接将新线程放在onCreated中,那么就会出错,因为主线程跑到新线程去了。另外sur.getHolder().setFixedSize(50,50);是用于对SurfaceView大小进行加载的,这点View就没有,可能有人就说了,measure()不就可以了,但实际上measure函数会调用onMeasure(),而onMeasure()本身最主要的部分在于setMeasuredDimension(),但是却有默认的参数,因此不容易改变自身默认参数(我试过很多方法都没能通过measure改变大小,正是由于这样才没用View,因为Measure是从View那里继承来的)。sur.setZOrderOnTop(true);sur.getHolder().setFormat(PixelFormat.TRANSLUCENT);是用于设置背景透明的。canvas=sur.getHolder().lockCanvas();得到的canvas必须放在新线程中加载。另外如果有需要对于SurfaceView的变化进行控制,可加载回调,使用SurfaceView的addCallback()即可加载,当然SAurfaceHolder.Callback需要重写三个主函数,分别是surfaceCreated(),surfaceChanged(),surfaceDestroyed(),但可以不加载,如果没用到。

     其实最正规的做法是像View那样extends一个新类,这样就能解决所有的问题而不用去每次使用都重新加载,但是视情况而定,选自己最适合的进行。

抱歉!评论已关闭.