SurfaceView

前言

终于告一段落了,由于在之前没有用到SurfaceView,而这次项目中偏偏用到了SurfaceView,导致我在这上面花了太多的时间,现在终于有时间来整理下了;

为什么会有它?

通常情况下View的绘制和用户响应都是在同一个线程中处理的,这就是为什么处理长时间事件(例如访问网络)需要放到另外的线程中去(防止阻塞当前UI线程的操作和绘制)。但是在其他线程中却不能修改UI元素,例如用后台线程更新自定义View(调用View的在自定义View中的onDraw函数)是不允许的,如果需要在另外的线程绘制界面、需要迅速的更新界面或则渲染UI界面需要较长的时间,这种情况就要使用SurfaceView了;

那它有什么特点呢?

SurfaceView中包含一个Surface对象而Surface是可以在后台线程中绘制的。Surface属于

OPhone底层显示系统。SurfaceView的性质决定了其比较适合一些场景:需要界面迅速更新、对帧率要求较高的情况。使用SurfaceView需要注意以下几点情况:

SurfaceViewSurfaceHolder.Callback函数都从当前SurfaceView窗口线程中调用(一般而言就是程序的主线程)。有关资源状态要注意和绘制线程之间的同步。 在绘制线程中必须先合法的获取Surface才能开始绘制内容,在SurfaceHolder.Callback.surfaceCreated()SurfaceHolder.Callback.surfaceDestroyed()之间的状态为合法的,另外在Surface类型为SURFACE_TYPE_PUSH_BUFFERS时候是不合法的。 额外的绘制线程会消耗系统的资源,在使用SurfaceView的时候要注意这点;

那么如何使用呢?

由于SurfaceView需要开发者来绘制图像,因此,开发者想要使用SurfaceView则必须继承SurfaceView

使用SurfaceView只要继承SurfaceView类并实现SurfaceHolder.Callback接口就可以实现一个自定义的SurfaceView了,SurfaceHolder.Callback在底层的Surface状态发生变化的时候通知ViewSurfaceHolder.Callback具有如下的接口:

surfaceCreated(SurfaceHolder holder):当Surface第一次创建后会立即调用该函数。程序可以在该函数中做些和绘制界面相关的初始化工作,一般情况下都是在另外的线程来绘制界面,所以不要在这个函数中绘制Surface

surfaceChanged(SurfaceHolder holder, int format, int width,int height):当Surface的状态(大小和格式)发生变化的时候会调用该函数,在surfaceCreated调用后该函数至少会被调用一次。

surfaceDestroyed(SurfaceHolder holder):当Surface被摧毁前会调用该函数,该函数被调用后就不能继续使用Surface了,一般在该函数中来清理使用的资源

既然是子线程进行绘制,那需要同步吗?

答案是:需要控制同步,那怎么控制呢?

通过SurfaceViewgetHolder()函数可以获取SurfaceHolder对象,Surface就在SurfaceHolder对象内。虽然Surface保存了当前窗口的像素数据,但是在使用过程中是不直接和Surface打交道的,由SurfaceHolderCanvas lockCanvas()Canvas lockCanvas(Rect dirty)函数来获取Canvas对象,通过在Canvas上绘制内容来修改Surface中的数据。如果Surface不可编辑或则尚未创建调用该函数会返回null,在unlockCanvas() lockCanvas()Surface的内容是不缓存的,所以需要完全重绘Surface的内容,为了提高效率只重绘变化的部分则可以调用lockCanvas(Rect dirty)函数来指定一个dirty区域,这样该区域外的内容会缓存起来。在调用lockCanvas函数获取Canvas后,SurfaceView会获取Surface的一个同步锁直到调用unlockCanvasAndPost(Canvas canvas)函数才释放该锁,这里的同步机制保证在Surface绘制过程中不会被改变(被摧毁、修改)

当在Canvas中绘制完成后,调用函数unlockCanvasAndPost(Canvas canvas)来通知系统Surface已经绘制完成,这样系统会把绘制完的内容显示出来。为了充分利用不同平台的资源,发挥平台的最优效果可以通过SurfaceHoldersetType函数来设置绘制的类型,目前接收如下的参数:

SURFACE_TYPE_NORMAL:用RAM缓存原生数据的普通Surface

SURFACE_TYPE_HARDWARE:适用于DMA(Direct memory access )引擎和硬件加速的Surface

SURFACE_TYPE_GPU:适用于GPU加速的Surface

SURFACE_TYPE_PUSH_BUFFERS:表明该Surface不包含原生数据,Surface用到的数据由其他对象提供,在Camera图像预览中就使用该类型的Surface,有Camera负责提供给预览Surface数据,这样图像预览会比较流畅。如果设置这种类型则就不能调用lockCanvas来获取Canvas对象了。

注意事项

一个SurfaceView只在SurfaceHolder.Callback.surfaceCreated()SurfaceHolder.Callback.surfaceDestroyed()调用之间是可用的,其他时间是得不到它的Canvas对象的(null)。

 

参考链接

 

http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1201/656.html

个人代码

package XXXXXXXXXXX;

 

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.PixelFormat;

import android.graphics.PorterDuff;

import android.graphics.PorterDuffXfermode;

import android.util.AttributeSet;

import android.view.SurfaceHolder;

import android.view.SurfaceView;

import cn.gov.pbc.identityauthentication.client.mobile.android.logger.MKLogger;

 

 

public class PreviewBorderView extends SurfaceView implements SurfaceHolder.Callback,

Runnable {

private final String TAG = "PreviewBorderView";

private int mScreenH;

    private int mScreenW;

    private Canvas mCanvas;

    private Paint mPaint;

    private Paint mPaintLine;

    private SurfaceHolder mHolder;

    private Thread mThread;

    private String color = "#1298DC";

    private String DEFAULT_TIPS_TEXT = "请将方框对准证件拍摄";

    private int textSize = 32;

 

private Paint mBackgroundPaint;

 

    public PreviewBorderView(Context context) {

        this(context, null);

    }

 

    public PreviewBorderView(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

    }

 

    public PreviewBorderView(Context context, AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);

        init();

    }

 

    /**

     * 初始化绘图变量

     * @param mBackgroundPaint

     */

    private void init() {

        mHolder = getHolder();

        mHolder.addCallback(this);

        // 窗口支持透明度

        mHolder.setFormat(PixelFormat.TRANSPARENT);

        

        // 使SerfaceView位于最顶层

        setZOrderOnTop(true);

        

        mPaint = new Paint();

        mPaint.setAntiAlias(true);

        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);

        mPaint.setColor(Color.WHITE);

        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

        

        mBackgroundPaint = new Paint();

        mBackgroundPaint.setColor(Color.BLACK);

        

        mPaintLine = new Paint();

        mPaintLine.setColor(Color.parseColor(color));

        mPaintLine.setStrokeWidth(10.0F);

        

        setKeepScreenOn(true);

    }

 

    /**

     * 绘制取景框

     * 对于小屏幕手机而言,铺满还可以接受

     * 对于大屏幕手机而言,铺满就显得太差了

     */

    private void draw() {

        try {

            mCanvas = mHolder.lockCanvas();

            // 先设置成白色、透明

            mCanvas.drawARGB(0, 255, 255, 255);

            

            mCanvas.drawRect(0, 0, mScreenW, mScreenH, mBackgroundPaint);

            

            MKLogger.i(TAG, "自定义控件宽度:"+mScreenW);

            MKLogger.i(TAG, "自定义控件高度:"+mScreenH);

            

            // 垂直方向,留白的高度

            int spaceHeight = 100;

            

            // 矩形的高度,

            int RectHeight = mScreenH - spaceHeight * 2;

            

            // 矩形的宽度,真实身份证中的宽度是高度的1.58

            int RectWidth = (int) (RectHeight * 1.58);

            

            // 水平方向,留白的宽度

            int spaceWidth = (mScreenW - RectWidth) / 2;

            

            // 线长度

            int line = 100;

            

            // 1、画出矩形区域

            mCanvas.drawRect(

             spaceWidth, //左边距

             spaceHeight, //上边距

             (mScreenW - spaceWidth), //右边距

             (mScreenH - spaceHeight), //下边距

             mPaint);

            // 2、画出左上角的直角绿色边框 -

            mCanvas.drawLine(

             spaceWidth,

             spaceHeight,

             (spaceWidth + line),

             spaceHeight,

             mPaintLine);

            mCanvas.drawLine(

             spaceWidth,

             spaceHeight,

             spaceWidth,

             (spaceHeight + line),

             mPaintLine);

            // 3、画出右上角的直角绿色边框

            mCanvas.drawLine(

             (mScreenW - spaceWidth - line),

             spaceHeight,

             (mScreenW - spaceWidth),

             spaceHeight,

             mPaintLine);

            mCanvas.drawLine(

             (mScreenW - spaceWidth),

             (spaceHeight),

             (mScreenW - spaceWidth),

             (spaceHeight + line),

             mPaintLine);

            // 4、画出左下角的直角绿色边框

            mCanvas.drawLine(

             spaceWidth,

             (mScreenH - spaceHeight - line),

             spaceWidth,

             (mScreenH - spaceHeight),

             mPaintLine);

            mCanvas.drawLine(

             spaceWidth,

             (mScreenH - spaceHeight),

             (spaceWidth + line),

             (mScreenH - spaceHeight),

             mPaintLine);

            // 5、画出右下角的直角绿色边框

            mCanvas.drawLine(

             (mScreenW - spaceWidth - line),

             (mScreenH - spaceHeight),

             (mScreenW - spaceWidth),

             (mScreenH - spaceHeight),

             mPaintLine);

            mCanvas.drawLine(

             (mScreenW - spaceWidth),

             (mScreenH - spaceHeight - line),

             (mScreenW - spaceWidth),

             (mScreenH - spaceHeight),

             mPaintLine);

            

            // 写字

            mPaintLine.setTextSize(textSize);

            mPaintLine.setAntiAlias(true);

            mPaintLine.setDither(true);

            mPaintLine.setColor(Color.parseColor(color));

            float length = mPaintLine.measureText(DEFAULT_TIPS_TEXT);

            this.mCanvas.drawText(

             DEFAULT_TIPS_TEXT,

             mScreenW / 2 - length / 2,

             spaceHeight / 2,

             mPaintLine);

            

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            if (mCanvas != null) {

                mHolder.unlockCanvasAndPost(mCanvas);

            }

        }

    }

 

 

    @Override

    public void surfaceCreated(SurfaceHolder holder) {

        //获得宽高,开启子线程绘图

        mScreenW = getWidth();

        mScreenH = getHeight();

        mThread = new Thread(this);

        mThread.start();

    }

 

    @Override

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

 

    }

 

    @Override

    public void surfaceDestroyed(SurfaceHolder holder) {

        //停止线程

        try {

            mThread.interrupt();

            mThread = null;

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

 

    @Override

    public void run() {

        //子线程绘图

        draw();

    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值