欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android-使用SurfaceView多线程绘制动画

程序员文章站 2022-03-09 19:46:26
...
使用SurfaceView中的Surface对象进行绘图,其本质就是利用SurfaceHolder的lockCanvas获取到Canvas对象进行绘制的,对于绘制动画来说,必须使用双缓冲,或者采用双线程,一个线程负责专门的预处理,比如图片数据读取,另外一个线程负责进行专绘制图形。因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高动画播放的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。

下在给出一个例子,讲解一下如何利用双线程提高绘图速度:

以下可以看到的动画是一张解码后的图片从最屏幕最左边快速移到右边,重新开始则清屏进行显示

package com.test.surfaceview;

import java.lang.reflect.Field;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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;

public class TestsurfaceviewActivity extends Activity {
	private final static String TAG = "TestsurfaceviewActivity";

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// setContentView(new MySurfaceView(this)); // 这里以MySurfaceView作为显示View
		onTestInit();
	}

	private SurfaceView drawSV = null;
	private SurfaceHolder drawSH = null;
	ArrayList<Integer> imgList = new ArrayList<Integer>(); 
	private int mWidth= 0, mHeight = 0;
	private Bitmap bitmap = null;
	private LoadImage loadImg = new LoadImage();
	private DrawImage drawImg = null;

	private void onTestInit() {
		drawSV = (SurfaceView) this.findViewById(R.id.SurfaceDrawView);
		drawSH = drawSV.getHolder();
		drawSH.addCallback(new MySVCallback());
	}
	
	private int onTestStart(){
		if(loadImg != null){
			loadImg.start();			
		}
		drawImg = new DrawImage(0,mHeight);
		drawImg.start();
		return 0;
	}
	
	private void onTestStop(){
		if(loadImg != null){
			loadImg.stop();
		}
		
		if(drawImg != null){
			drawImg.stop();
		}
	}

	private class MySVCallback implements SurfaceHolder.Callback {
		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width,
				int height) {
			// TODO Auto-generated method stub
			Log.i(TAG, "surfaceChanged is called");
		}

		@Override
		public void surfaceCreated(SurfaceHolder holder) {
			// TODO Auto-generated method stub
			Log.i(TAG, "surfaceCreated is called");
			// 用反射机制来获取资源中的图片ID和尺寸
			Field[] fields = R.drawable.class.getDeclaredFields();
			for (Field field : fields) {
				// 除了icon及launcher之外的图片
				if (!"icon".equals(field.getName())
						&& !"ic_launcher".equals(field.getName())) {
					int index = 0;
					try {
						index = field.getInt(R.drawable.class);
					} catch (IllegalArgumentException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					// 保存图片ID
					imgList.add(index);
				}
			}
			
			Log.i(TAG,"imglist size = "+imgList.size());
            // 取得图像大小  
            Bitmap bmImg = BitmapFactory.decodeResource(getResources(),  
                    imgList.get(0));

            mWidth  = bmImg.getWidth(); 
            mHeight = bmImg.getHeight();
            
            onTestStart();
		}

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
			// TODO Auto-generated method stub
			Log.i(TAG, "surfaceDestroyed is called");
		}		
	}

	private class LoadImage extends Thread {
		private int imgIndex = 0;

		public void run() {
			while (true) {
				bitmap = BitmapFactory.decodeResource(getResources(),
						imgList.get(imgIndex));
				++imgIndex;
				if (imgIndex == imgList.size()) { // 循环取图片数据
					imgIndex = 0;
				}
			}
		}
	}
	
	private class DrawImage extends Thread{
		private int x,y;
		public DrawImage(int x,int y){
			this.x = x;
			this.y = y;
		}
		
		private void ClearScreen(){
			Canvas canvas = drawSH.lockCanvas(null);
			canvas.drawColor(Color.BLACK);// 清除画布
			drawSH.unlockCanvasAndPost(canvas);
		}
		
		public void run() {
			while (true) {
				if (bitmap != null) {
					/**
					 * 以下两个有明显的效率差异,lockCanvas()指定Rect内减少循环画线的次数,
					 * 可以提高绘图效率,全屏刷新时会很闪烁
					 */
					Canvas c = drawSH.lockCanvas(new Rect(this.x, this.y,
							this.x + mWidth, this.y + mHeight));
//					Canvas c = drawSH.lockCanvas();
					c.drawBitmap(bitmap, this.x, this.y, new Paint());
					drawSH.unlockCanvasAndPost(c);// 将显示内容上屏
					
					this.x += 10;
					//如果到了终点,则清屏重来
					if(this.x > 1280 - mWidth){
						this.x = 0;
						ClearScreen();
					}
				}
			}
		}
	}
}