Android中SurfaceView学习
2012-09-27 11:42
387 查看
SurfaceView和View的明显不同在于Surface不需要通过线程来更新视图,但在绘制之前必须使用lockCanvas方法锁定画布,并得 到画布,然后绘制,完成后用unlockCanvasAndPost方法解锁画布。SurfaceView类的事件处理和View一样。
首先来看一个简单的框架。
绘制界面类:
解释几个 概念 :
callback接口:
只要继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView 了,SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知 View,SurfaceHolder.Callback具有如下的接口:
surfaceCreated(SurfaceHolder
holder):当Surface第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface。
surfaceChanged(SurfaceHolder
holder, int format, int width,int height):当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次。
SurfaceHolder 类:
它是一个用于控制surface的接口,它提供了控制surface 的大小,格式,上面的像素,即监视其改变的。
SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface
就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或 则Canvas lockCanvas()函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑或则尚未 创建调用该函数会返回null,在 unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用
lockCanvas(Rect rect)函数来指定一个rect区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获 取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)。
最后来看一个复杂一些的例子 ,结合了之前的手势操作。
实现的效果是:在屏幕上进行Fling操作,在手滑动的方向上会产生一个运动的小球 ,速度 就是手势滑动的速度。
球的大小随机 ,颜色随机 ,碰到边界会反弹。
运行结果:
代码清单:
小球类,主要是设置几个属性,还有几个set,get方法。
主控类:
主要负责触控时间的监听。
画板类:
负责图形的绘制。
package com.example.bonusball;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class CanvasView extends SurfaceView implements SurfaceHolder.Callback
{
private SurfaceHolder myHolder;
private Paint ballPaint; // Paint used to draw the cannonball
private int screenWidth; // width of the screen
private int screenHeight; // height of the screen
private int maxBallRadius;
private CanvasThread myThread;
private List<Ball> ballList;
private Paint backgroundPaint;
private Random mRandom;
//控制循环
private boolean isLoop;
public CanvasView(Context context) {
super(context);
// TODO Auto-generated constructor stub
myHolder=this.getHolder();
myHolder.addCallback(this);
ballPaint=new Paint();
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.BLACK);
isLoop = true;
ballList=new CopyOnWriteArrayList<Ball>();
mRandom=new Random();
}
public void fireBall(float startX,float startY,float velocityX,float velocityY)
{
int ranColor = 0xff000000 | mRandom.nextInt(0x00ffffff);
float randomRadius=mRandom.nextInt(maxBallRadius);
float tmpRadius=maxBallRadius/5.0>randomRadius?maxBallRadius:randomRadius;
ballList.add(new Ball(ranColor,tmpRadius,startX,startY,velocityX,velocityY));
System.out.println("Fire");
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
screenWidth = w; // store the width
screenHeight = h; // store the height
maxBallRadius=w/10;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
myThread = new CanvasThread();
System.out.println("SurfaceCreated!");
myThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
// 停止循环
isLoop = false;
}
public void drawGameElements(Canvas canvas)
{
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), backgroundPaint);
for(Ball b:ballList)
{
ballPaint.setColor(b.getColor());
canvas.drawCircle(b.getX(),b.getY(),b.getRadius(),ballPaint);
}
}
private void updatePositions(double elapsedTimeMS) {
// TODO Auto-generated method stub
float interval = (float) (elapsedTimeMS / 1000.0);
for(Ball b:ballList)
{
b.setPosX(b.getX()+b.getVX()*interval);
b.setPosY(b.getY()+b.getVY()*interval);
if (b.getX() + b.getRadius()> screenWidth )
{
b.setVX(-1*b.getVX());
//边界修复
b.setPosX(screenWidth-b.getRadius());
}
if(b.getX() - b.getRadius() < 0)
{
b.setVX(-1*b.getVX());
b.setPosX(b.getRadius());
}
if (b.getY() + b.getRadius()> screenHeight)
{
b.setVY(-1*b.getVY());
b.setPosY(screenHeight-b.getRadius());
}
if(b.getY() - b.getRadius() < 0)
{
b.setVY(-1*b.getVY());
b.setPosY(b.getRadius());
}
}
}
private class CanvasThread extends Thread
{
@Override
public void run()
{
Canvas canvas=null;
long previousFrameTime = System.currentTimeMillis();
while(isLoop)
{
try{
canvas = myHolder.lockCanvas(null);//获取画布
synchronized( myHolder )
{
canvas.drawColor(Color.BLACK);
long currentTime = System.currentTimeMillis();
double elapsedTimeMS = currentTime - previousFrameTime;
updatePositions(elapsedTimeMS); // update game state
drawGameElements(canvas);
previousFrameTime = currentTime; // update previous time
//System.out.println("run");
}
}
finally
{
if (canvas != null)
myHolder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
} // end finally
}
}
}
}
未解决问题一枚
理论上surfaceview是 带双缓冲的,但实际运行起来,图像还是有闪烁。
尝试写一个新的进程来处理图像的绘制,未果。
首先来看一个简单的框架。
绘制界面类:
package com.example.bonusball; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; public class CanvasView extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder myHolder; private Paint ballPaint; // Paint used to draw the cannonball private int screenWidth; // width of the screen private int screenHeight; // height of the screen private int ballRadius; private CanvasThread myThread; //控制循环 private boolean isLoop; public CanvasView(Context context) { super(context); // TODO Auto-generated constructor stub myHolder=this.getHolder(); myHolder.addCallback(this); ballPaint=new Paint(); ballPaint.setColor(Color.BLUE); isLoop = true; } public void fireBall(float startX,float startY) { System.out.println("Fire"); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); screenWidth = w; // store the width screenHeight = h; // store the height ballRadius=w/10; } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub myThread = new CanvasThread(); System.out.println("SurfaceCreated!"); myThread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub // 停止循环 isLoop = false; } public void drawGameElements(Canvas canvas) { canvas.drawCircle(100, 100,ballRadius,ballPaint); } private class CanvasThread extends Thread { @Override public void run() { while(true) { synchronized( myHolder ) { Canvas canvas = myHolder.lockCanvas(null);//获取画布 drawGameElements(canvas); myHolder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像 //System.out.println("run"); } } } } }事件处理 类:
package com.example.bonusball; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.widget.Toast; public class BallActivity extends Activity { private GestureDetector myGestureDetector;//监听手势 private CanvasView myCanvas; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); myCanvas=new CanvasView(this); setContentView(myCanvas); myGestureDetector = new GestureDetector(this, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event) { return myGestureDetector.onTouchEvent(event); } private class MyGestureListener extends SimpleOnGestureListener { public boolean onDown(MotionEvent e1) { Toast.makeText(getApplicationContext(), "onDown", Toast.LENGTH_SHORT).show(); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { System.out.println("Fling"); return true; } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_ball, menu); return true; } }
解释几个 概念 :
callback接口:
只要继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView 了,SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知 View,SurfaceHolder.Callback具有如下的接口:
surfaceCreated(SurfaceHolder
holder):当Surface第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface。
surfaceChanged(SurfaceHolder
holder, int format, int width,int height):当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次。
SurfaceHolder 类:
它是一个用于控制surface的接口,它提供了控制surface 的大小,格式,上面的像素,即监视其改变的。
SurfaceView的getHolder()函数可以获取SurfaceHolder对象,Surface
就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或 则Canvas lockCanvas()函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑或则尚未 创建调用该函数会返回null,在 unlockCanvas() 和 lockCanvas()中Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用
lockCanvas(Rect rect)函数来指定一个rect区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获 取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)。
最后来看一个复杂一些的例子 ,结合了之前的手势操作。
实现的效果是:在屏幕上进行Fling操作,在手滑动的方向上会产生一个运动的小球 ,速度 就是手势滑动的速度。
球的大小随机 ,颜色随机 ,碰到边界会反弹。
运行结果:
代码清单:
小球类,主要是设置几个属性,还有几个set,get方法。
package com.example.bonusball; public class Ball { private float posX; private float posY; private float velocityX; private float velocityY; private float radius; private int color; public Ball(int rgb,float r,float pX,float pY,float vX,float vY) { this.color=rgb; this.radius=r; this.posX=pX; this.posY=pY; this.velocityX=vX; this.velocityY=vY; } public float getRadius() { return radius; } public int getColor() { return color; } public float getX() { return posX; } public float getY() { return posY; } public float getVX() { return velocityX; } public float getVY() { return velocityY; } public void setPosX(float newX) { this.posX=newX; } public void setPosY(float newY) { this.posY=newY; } public void setVX(float newVX) { this.velocityX=newVX; } public void setVY(float newVY) { this.velocityY=newVY; } }
主控类:
主要负责触控时间的监听。
package com.example.bonusball; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.widget.Toast; public class BallActivity extends Activity { private GestureDetector myGestureDetector;//监听手势 private CanvasView myCanvas; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); myCanvas=new CanvasView(this); setContentView(myCanvas); myGestureDetector = new GestureDetector(this, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event) { return myGestureDetector.onTouchEvent(event); } private class MyGestureListener extends SimpleOnGestureListener { public boolean onDown(MotionEvent e1) { // Toast.makeText(getApplicationContext(), "onDown", Toast.LENGTH_SHORT).show(); //myCanvas.fireBall(e1.getRawX(),e1.getRawY(),0,0); return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { System.out.println("Fling"); final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200; if (Math.abs(e1.getX() - e2.getX()) > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY) myCanvas.fireBall(e1.getRawX(),e1.getRawY(),velocityX/4,velocityY/4); return true; } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_ball, menu); return true; } }
画板类:
负责图形的绘制。
package com.example.bonusball;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class CanvasView extends SurfaceView implements SurfaceHolder.Callback
{
private SurfaceHolder myHolder;
private Paint ballPaint; // Paint used to draw the cannonball
private int screenWidth; // width of the screen
private int screenHeight; // height of the screen
private int maxBallRadius;
private CanvasThread myThread;
private List<Ball> ballList;
private Paint backgroundPaint;
private Random mRandom;
//控制循环
private boolean isLoop;
public CanvasView(Context context) {
super(context);
// TODO Auto-generated constructor stub
myHolder=this.getHolder();
myHolder.addCallback(this);
ballPaint=new Paint();
backgroundPaint = new Paint();
backgroundPaint.setColor(Color.BLACK);
isLoop = true;
ballList=new CopyOnWriteArrayList<Ball>();
mRandom=new Random();
}
public void fireBall(float startX,float startY,float velocityX,float velocityY)
{
int ranColor = 0xff000000 | mRandom.nextInt(0x00ffffff);
float randomRadius=mRandom.nextInt(maxBallRadius);
float tmpRadius=maxBallRadius/5.0>randomRadius?maxBallRadius:randomRadius;
ballList.add(new Ball(ranColor,tmpRadius,startX,startY,velocityX,velocityY));
System.out.println("Fire");
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// TODO Auto-generated method stub
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
screenWidth = w; // store the width
screenHeight = h; // store the height
maxBallRadius=w/10;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
myThread = new CanvasThread();
System.out.println("SurfaceCreated!");
myThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
// 停止循环
isLoop = false;
}
public void drawGameElements(Canvas canvas)
{
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), backgroundPaint);
for(Ball b:ballList)
{
ballPaint.setColor(b.getColor());
canvas.drawCircle(b.getX(),b.getY(),b.getRadius(),ballPaint);
}
}
private void updatePositions(double elapsedTimeMS) {
// TODO Auto-generated method stub
float interval = (float) (elapsedTimeMS / 1000.0);
for(Ball b:ballList)
{
b.setPosX(b.getX()+b.getVX()*interval);
b.setPosY(b.getY()+b.getVY()*interval);
if (b.getX() + b.getRadius()> screenWidth )
{
b.setVX(-1*b.getVX());
//边界修复
b.setPosX(screenWidth-b.getRadius());
}
if(b.getX() - b.getRadius() < 0)
{
b.setVX(-1*b.getVX());
b.setPosX(b.getRadius());
}
if (b.getY() + b.getRadius()> screenHeight)
{
b.setVY(-1*b.getVY());
b.setPosY(screenHeight-b.getRadius());
}
if(b.getY() - b.getRadius() < 0)
{
b.setVY(-1*b.getVY());
b.setPosY(b.getRadius());
}
}
}
private class CanvasThread extends Thread
{
@Override
public void run()
{
Canvas canvas=null;
long previousFrameTime = System.currentTimeMillis();
while(isLoop)
{
try{
canvas = myHolder.lockCanvas(null);//获取画布
synchronized( myHolder )
{
canvas.drawColor(Color.BLACK);
long currentTime = System.currentTimeMillis();
double elapsedTimeMS = currentTime - previousFrameTime;
updatePositions(elapsedTimeMS); // update game state
drawGameElements(canvas);
previousFrameTime = currentTime; // update previous time
//System.out.println("run");
}
}
finally
{
if (canvas != null)
myHolder.unlockCanvasAndPost(canvas);//解锁画布,提交画好的图像
} // end finally
}
}
}
}
未解决问题一枚
理论上surfaceview是 带双缓冲的,但实际运行起来,图像还是有闪烁。
尝试写一个新的进程来处理图像的绘制,未果。
相关文章推荐
- Android之SurfaceView学习
- Android:SurfaceView学习
- Android SurfaceView 学习笔记(二)
- Android之SurfaceView学习(一)
- Android之SurfaceView学习(一)
- android 之surfaceView 学习
- Android之SurfaceView学习(一)
- [学习笔记]用户界面优化之Android SurfaceView的使用
- Android之SurfaceView学习(一)
- Android之SurfaceView学习
- Android之SurfaceView学习(一)
- Android游戏开发之SurfaceView的使用-android学习之旅(五)
- Android之SurfaceView学习(一)
- Android之SurfaceView学习(一)
- Android的View, SurfaceView学习
- [转]Android之SurfaceView学习
- Android之SurfaceView学习
- Android 学习笔记之SurfaceView的使用+如何实现视频播放...
- Android SurfaceView学习
- Android游戏开发之SurfaceView的使用-android学习之旅(五)