Android中SurfaceView学习

               SurfaceView和View的明显不同在于Surface不需要通过线程来更新视图,但在绘制之前必须使用lockCanvas方法锁定画布,并得 到画布,然后绘制,完成后用unlockCanvasAndPost方法解锁画布。SurfaceView类的事件处理和View一样。

             首先来看一个简单的框架。

绘制界面类:

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是 带双缓冲的,但实际运行起来,图像还是有闪烁。

尝试写一个新的进程来处理图像的绘制,未果。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值