您的位置:首页 > 移动开发 > Android开发

Android学习之界面篇(九)SurfaceView简单学习

2016-05-08 23:33 537 查看
知识点:

surfaceview介绍
surfaceview与View的区别,surface与surfaceview的区别
实例创建与使用
SurfaceHolder.Callback的使用,surfaceHolder可以看作是surfaceview的控制器,控制图形的大小,像素等.
在主函数中回调函数的使用getHolder.addCallback(this)
canvas的锁定与解锁,绘制图形之前锁定画布,绘制结束之后解锁画布。
canvas的save与restore,
canvas画布的各种绘制操作
Timer 的使用与操作

一、SurfaceView简介:
在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面。由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行绘制。又由于不会占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应。
SurfaceView是View类的继承类,这个View里内嵌了一个专门用于绘制的Surface,这个可以类似的理解成为一个在View里的Canvas。你可以控制这个Surface的格式和尺寸。Surfaceview类则控制这个Surface在屏幕上的正确位置。
java.lang.Object
android.view.View
android.view.SurfaceView
二、SurfaceView与view的区别:

Android游戏当中主要的除了控制类外就是显示类View。SurfaceView是从View基类中派生出来的显示类。android游戏开发中常用的三种视图是:view、SurfaceView和GLSurfaceView。

  View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;必须在UI主线程内更新画面,速度较慢。

  SurfaceView:基于view视图进行拓展的视图类,更适合2D游戏的开发;是view的子类,类似使用双缓机制,在新的线程中更新画面所以刷新界面速度比view快。

  GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3D游戏开发的视图;是SurfaceView的子类,openGL专用。

在2D游戏开发中,大致可以分为两种游戏框架,View和SurfaceView。

  View:必须在UI的主线程中更新画面,用于被动更新画面。

  surfaceView:UI线程和子线程中都可以。在一个新启动的线程中重新绘制画面,主动更新画面。

UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步,涉及到线程同步。

所以基于以上,根据游戏特点,一般分成两类。

1 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。

2 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

三、SurfaceView与Surface的联系

简单来说,SurfaceView与Surface的联系就是,Surface是管理显示内容的数据(implementsParcelable),包括存储于数据的交换。而SurfaceView就是把这些数据显示出来到屏幕上面。

两者联系如图所示:



四、下面是使用SurfaceView的简单实例:

public class MyView extends SurfaceView implements SurfaceHolder.Callback{

//声明一个画笔
private Paint paint=null;

/**
* 构造函数
* @param context
*/
public MyView(Context context) {
super(context);
//实例化画笔
paint=new Paint();
//给画笔设置颜色
paint.setColor(Color.RED);
getHolder().addCallback(this);//添加回调
}

/**
* 自定义画图函数,此函数须在surface创建之后运行
*/
public void draw(){
//通过getHolder来锁定画布
Canvas canvas= getHolder().lockCanvas();
//初始化画布,设置颜色为白色
canvas.drawColor(Color.WHITE);

//绘制一个正方形
//canvas.drawRect(0,0,500,500,paint);

//绘制两条平行线,getWidth是获取屏幕的宽度,getHeight是获取屏幕的高度
//保存画布状态,之后可以对图形进行更改
canvas.save();
//顺时针旋转画布,并设置旋转的中心点的x和y坐标
canvas.rotate(90,getWidth()/2,getHeight()/2);
canvas.drawLine(0,getHeight()/2,getWidth(),getHeight(),paint);
//更改完成之后要复原画布状态,save和restore必须成对出现。不执行restore的话以后绘制的图形都会随着画布的改变而改变
canvas.restore();
canvas.drawLine(0,getHeight()/2+100,getWidth(),getHeight()+100,paint);

//在绘制结束后一定要解锁画布
getHolder().unlockCanvasAndPost(canvas);
}
/**
*surface创建之后执行的操作
* @param holder
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
draw();
}

/**
*surface改变之后执行的操作
* @param holder
* @param format
* @param width
* @param height
*/
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

/**
*surface销毁之后执行的操作
* @param holder
*/
@Override
public void surfaceDestroyed(SurfaceHolder holder) {

}
}

具体显示效果如下:













五、SurfaceHoldery与SurfaceHolder.Callbback的介绍

SurfaceHolder

android.view.SurfaceHolder

Class Overview

Abstract interface to someone holding a display surface. Allows you to control the surface size and format, edit the pixels in the surface, and monitor changes to the surface. This interface is typically available through the
SurfaceView
class.

When using this interface from a thread other than the one running its
SurfaceView
,
you will want to carefully read the methods
lockCanvas()
and
Callback.surfaceCreated()
.

简单翻译:

SurfaceHolder是控制surface的一个抽象接口,你可以通过SurfaceHolder来控制surface的尺寸和格式,或者修改surface的像素,监视surface的变化等等,SurfaceHolder是SurfaceView的典型接口。
与直接控制SurfaceView来修改surface不同,使用SurfaceHolder来修改surface时,需要注意
lockCanvas()
Callback.surfaceCreated()
.这两个方法。

SurfaceHolder控制surface的流程所使用的几个方法。

1、abstract void addCallback(SurfaceHolder.Callback callback)
Add a Callback interface for this holder.// 给SurfaceHolder一个回调对象。

2、abstract Canvas lockCanvas(Rect dirty)
Just like lockCanvas() but allows specification of a dirty rectangle.

// 锁定画布中的某一个区域,返回的画布对象Canvas(当更新的内容只有一个区域时,同时要追求高效,可以只更

新一部分的区域,而不必更新全部画布区域)

3、abstract Canvas lockCanvas()
Start editing the pixels in the surface.// 锁定画布,返回的画布对象Canvas

4、abstract void removeCallback(SurfaceHolder.Callback callback)
Removes a previously added Callback interface from this holder.//移除回调对象

5、abstract void unlockCanvasAndPost(Canvas canvas)

Finish editing pixels in the surface.// 结束锁定画图,并提交改变。

SurfaceHolder.Callback

android.view.SurfaceHolder.Callback


Known
Indirect Subclasses
GLSurfaceView,NativeActivity,RSSurfaceView,SurfaceHolder.Callback2

Class Overview

A client may implement this interface to receive information about changes to the surface. When used with a
SurfaceView
,
the Surface being held is only available between calls to
surfaceCreated(SurfaceHolder)
and
surfaceDestroyed(SurfaceHolder)
.
The Callback is set with
SurfaceHolder.addCallback
method.

简单翻译:

SurfaceHolder.Callback是监听surface改变的一个接口

1、public abstract voidsurfaceChanged(SurfaceHolder holder,
int format, int width, int height)

holderThe SurfaceHolder whose surface has changed.
formatThe new PixelFormat of the surface.
widthThe new width of the surface.
heightThe new height of the surfa
//surface发生改变时被调用

2、public abstract voidsurfaceCreated(SurfaceHolder holder)

Parameters
holderThe SurfaceHolder whose surface is being created
//在surface创建时被调用,一般在这个方法里面开启渲染屏幕的线程。

3、public abstract voidsurfaceDestroyed(SurfaceHolder holder)
Parameters
holderThe SurfaceHolder whose surface is being destroyed.
//销毁时被调用,一般在这个方法里将渲染的线程停止。

六、android canvas

首先说一下canvas类:

Class Overview

The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap),
and a paint (to describe the colors and styles for the drawing).

这个类相当于一个画布,你可以在里面画很多东西;

我们可以把这个Canvas理解成系统提供给我们的一块内存区域(但实际上它只是一套画图的API,真正的内存是下面的Bitmap),而且它还提供了一整套对这个内存区域进行操作的方法,所有的这些操作都是画图API。也就是说在这种方式下我们已经能一笔一划或者使用Graphic来画我们所需要的东西了,要画什么要显示什么都由我们自己控制。

这种方式根据环境还分为两种:一种就是使用普通View的canvas画图,还有一种就是使用专门的SurfaceView的canvas来画图。两种的主要是区别就是可以在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性能。前面一种适合处理量比较小,帧率比较小的动画,比如说象棋游戏之类的;而后一种主要用在游戏,高品质动画方面的画图。

下面是Canvas类常用的方法:

drawRect(RectF rect, Paint paint) //绘制区域,参数一为RectF一个区域

drawPath(Path path, Paint paint) //绘制一个路径,参数一为Path路径对象

drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) //贴图,参数一就是我们常规的Bitmap对象,参数二是源区域(这里是bitmap),参数三是目标区域(应该在canvas的位置和大小),参数四是Paint画刷对象,因为用到了缩放和拉伸的可能,当原始Rect不等于目标Rect时性能将会有大幅损失。

drawLine(float startX, float startY, float stopX, float stopY, Paintpaint) //画线,参数一起始点的x轴位置,参数二起始点的y轴位置,参数三终点的x轴水平位置,参数四y轴垂直位置,最后一个参数为Paint
画刷对象。


drawPoint(float x, float y, Paint paint) //画点,参数一水平x轴,参数二垂直y轴,第三个参数为Paint对象。
drawText(String text, float x, floaty, Paint paint) //渲染文本,Canvas类除了上面的还可以描绘文字,参数一是String类型的文本,参数二x轴,参数三y轴,参数四是Paint对象。
drawCircle(float cx,float cy,float radius,Paint paint)// 绘制圆,参数一是中心点的x轴,参数二是中心点的y轴,参数三是半径,参数四是paint对象;

还要理解一个paint类:

Class Overview

The Paint class holds the style and color information about how to draw geometries, text and bitmaps.

paint类拥有风格和颜色信息如何绘制几何学,文本和位图。

Paint 代表了Canvas上的画笔、画刷、颜料等等;

Paint类常用方法:

setARGB(int a, int r, int g, int b) // 设置 Paint对象颜色,参数一为alpha透明值

setAlpha(int a) // 设置alpha不透明度,范围为0~255

setAntiAlias(boolean aa) // 是否抗锯齿

setColor(int color) // 设置颜色,这里Android内部定义的有Color类包含了一些常见颜色定义

setTextScaleX(float scaleX) // 设置文本缩放倍数,1.0f为原始

setTextSize(float textSize) // 设置字体大小
setUnderlineText(booleanunderlineText) // 设置下划线

七、Java Timer和TimerTask简介

Timer是一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行,可以看成一个定时器,可以调度TimerTask。TimerTask是一个抽象类,实现了Runnable接口,所以具备了多线程的能力。

import java.util.TimerTask;
public class MyTask extends TimerTask
{
private int id;
public MyTask(int id)
{
this.id = id;
}
public void run()
{
System.out.println("线程" + id + ":正在执行");
//System.gc();
}
}

然后主程序代码为:

import java.util.Date;
import java.util.Timer;
public class Test
{
public static void main(String[] args)
{
Timer timer = new Timer();
timer.schedule(new MyTask(1), 5000);// 5秒后启动任务
MyTask secondTask = new MyTask(2);
timer.schedule(secondTask, 1000, 3000);
// 1秒后启动任务,以后每隔3秒执行一次线程
Date date = new Date();
timer.schedule(new MyTask(3), new Date(date.getTime() + 1000));
// 以date为参数,指定某个时间点执行线程
// timer.cancel();
// secondTask.cancel();
System.out.println("main thread 结束!");
}
}

Timer里面有4个schedule重载函数,而且还有两个scheduleAtFixedRate:
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
安排指定的任务在指定的时间开始进行重复的固定速率执行。

void scheduleAtFixedRate(TimerTask task, long delay, long period)

安排指定的任务在指定的延迟后开始进行重复的固定速率执行。

使用scheduleAtFixedRate的话,Timer会尽量的让任务在一个固定的频率下运行。例如:在上面的例子中,让secondTask在1秒钟后,每3秒钟执行一次,但是因为java不是实时的,所以,我们在上个程序中表达的原义并不能够严格执行,例如有时可能资源调度紧张4秒以后才执行下一次,有时候又3.5秒执行。如果我们调用的是scheduleAtFixedRate,那么Timer会尽量让你的secondTask执行的频率保持在3秒一次。运行上面的程序,假设使用的是scheduleAtFixedRate,那么下面的场景就是可能的:1秒钟后,secondTask执行一次,因为系统繁忙,之后的3.5秒后secondTask才得以执行第二次,然后Timer记下了这个延迟,并尝试在下一个任务的时候弥补这个延迟,那么2.5秒后,secondTask将执行的三次。“以固定的频率而不是固定的延迟时间去执行一个任务”就是这个意思。

Timer终止的问题:

默认情况下,只要一个程序的timer线程在运行,那么这个程序就会保持运行。可以通过以下3种方法终止一个timer线程:

(1)调用timer的cancle方法。你可以从程序的任何地方调用此方法,甚至在一个timer task的run方法里;

(2)让timer线程成为一个daemon线程(可以在创建timer时使用new Timer(true)达到这个目地),这样当程序只有daemon线程的时候,它就会自动终止运行;

(3)调用System.exit方法,使整个程序(所有线程)终止。

TimerTask也有cancel方法。

上面所说的“只要一个程序的timer线程在运行,那么这个程序就会保持运行”。那么反过来,如果Timer里的所有TimerTask都执行完了,整个程序会退出吗,经测试答案是否定的,例如上面的测试代码,如果只加第一个TimerTask在Timer中执行:

timer.schedule(new MyTask(1), 5000);// 5秒后启动任务
那么5秒以后,其实整个程序还是没有退出,Timer会等待垃圾回收的时候被回收掉然后程序会得以退出,但是多长时间呢?在TimerTask的run函数执行完以后加上System.gc();就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: