当我们对ui 显示要求较高的时候,或者对程序执行要求较高的时候,view 类并不能满足需求,这个时候就要求用使用 SurfaceView.
比如 在一个运行速度要求很高的游戏中,可以使用双缓存来显示。游戏中的图片动画等绘制在一个画布(Canvas) 上,而SurfaceView 可以直接访问画布,SurfaceView 是提供给需要直接画像素而不是使用窗体部件的应用使用的。
每一个 Surface 创建一个 Canvas 对象,用来管理View 和 Surface 上的操作。
一般 view 都是在相同的 GUI线程中绘制的,这个主应用程序线程同时也用来处理所有的用户交互(例如,按钮单击或者文本输入)。 当需要快速的更新View的 Ui,或者渲染代码阻塞GUI线程时间过长的时候,SurfaceView就是解决上述问题的最佳选择。 SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的操作,或者那些要求快速更新或者高速帧率的地方,例如,使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。
SurfaceView使用的方式与任何View所派生的类都是完全相同的。可以像其他View那样应用动画,并把它们放到布局中。
SurfaceView封装的Surface支持使用所有标准Canvas方法进行绘图,同时也支持完全的OpenGL ES库。
使用OpenGL,可以在Surface上绘制任何支持的2D或者3D对象,与在2D画布上模拟相同的效果相比,这种方法可以依靠硬件加速(可用的时候)来极大地提高性能。
对于显示动态的3D图像来说,例如,那些使用Google Earth功能的应用程序,或者那些提供沉浸体验的交互式游戏,SurfaceView特别有用。它还是实时显示摄像头预览的最佳选择。
我们依照实现一个 小圆刷新更改颜色,然后可以点击改变位置 通过 自定义SurfaceView 来时实现,详解看代码注释。
package myapplication.lum.com.myanimation;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private boolean mbLoop = false; //控制线程ui刷新
private SurfaceHolder mSurfaceHolder = null; //定义 SurfaceHolder 对象
private int count = 0;
public float x = 50, y = 50; //圆球的显示位置
private int screenWidth = getWidth(), screenHeight = getHeight(); //屏幕大小
public MySurfaceView(Context context) {
super(context);
mbLoop = true;
mSurfaceHolder = this.getHolder(); //初始化 mSurfaceHolder 对象
/*
* 添加回调 函数
* 注意这里这句 mSurfaceHolder.addCallback(this)这句执行完了之后
* 马上就会回调 surfaceCreated方法了 然后开启线程 执行绘图方法
* */
mSurfaceHolder.addCallback(this);
this.setFocusable(true);
}
//surface创建时激发 此方法在主线程总执行
@Override
public void surfaceCreated(SurfaceHolder holder) {
new Thread(this).start(); // 开启绘图线程
}
//在surface的大小发生改变时调用
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
screenWidth = width; // reset width when screen orientation is changed
screenHeight = height; // reset height when screen orientation is changed
}
//在surface销毁时 调用
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mbLoop = false;
}
@Override
public void run() {
while (mbLoop) {
synchronized (mSurfaceHolder) { //同步对象,保证画布一时间只有一个操作
onDrawCanvas();
}
try {
Thread.sleep(200);
} catch (Exception e) {
}
}
}
///绘图方法 注意这里是另起一个线程来执行绘图方法了不是在UI 线程了
public void onDrawCanvas() {
//锁定画布,得到canvas 用SurfaceHolder对象的lockCanvas方法
Canvas canvas = mSurfaceHolder.lockCanvas();
if (mSurfaceHolder == null || canvas == null) {
return;
}
if (count < 100) {
count++;
} else {
count = 0;
}
//绘图
Paint mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.CYAN);
//绘制矩形---清屏作用
canvas.drawRect(0, 0, screenWidth, screenHeight, mPaint); // repaint background color
switch (count % 4) {
case 0:
mPaint.setColor(Color.BLUE);
break;
case 1:
mPaint.setColor(Color.GREEN);
break;
case 2:
mPaint.setColor(Color.RED);
break;
case 3:
mPaint.setColor(Color.YELLOW);
break;
default:
mPaint.setColor(Color.WHITE);
break;
}
//绘制小圆
canvas.drawCircle(x, y, 50, mPaint);
//绘制后解锁,绘制后必须解锁才能显示
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
package myapplication.lum.com.myanimation;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
public class MainActivity extends Activity {
MySurfaceView mySurfaceView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
//鼠标点击 改变小球的现实的坐标位置
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
mySurfaceView.x = event.getX();
mySurfaceView.y = event.getY();
}
return true;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
this.finish();
}
return true;
}
}
最后了解一下SurfaceView的基本使流程:
1.获取到 SurfaceView 对应的 SurfaceHolder,并且要给 SurfaceHolder 添加一个 SurfaceHolder.callback 对象。
2.创建一个线程用于渲染视图;
3.在SurfaceHolder.callback的surfaceCreated方法中开始渲染视图,绘制结束后,SurfaceHolder.callback的surfaceDestroyed方法中使用 unlockCanvasAndPost方法解锁 Canvas。
package myapplication.lum.com.myanimation;
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 MainActivity extends Activity {
/** Called when the activity is first created. */
Button btnSimpleDraw, btnTimerDraw;
SurfaceView sfv;
SurfaceHolder sfh;
int Y_axis[],//保存正弦波的Y轴上的点
centerY,//中心线
oldX,oldY;//上一个XY点
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnSimpleDraw = (Button) this.findViewById(R.id.Button01);
btnSimpleDraw.setOnClickListener(new ClickEvent());
sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);
sfh = sfv.getHolder();
// 初始化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);//直接绘制正弦波
}
}
}
/**
* 绘制指定区域
*/
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));
canvas.drawColor(Color.BLACK);
Paint mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setColor(Color.RED);// 画笔为红色
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);
}
}
文件参考:
[Android开发常见问题-24] Android 的 SurfaceView 双缓冲应用
https://blog.csdn.net/pi9nc/article/details/19418695