Android知识Android开发经验谈Android精选

SurfaceView简析

2017-06-17  本文已影响122人  一只好奇的茂

SurfaceView和View最本质的区别?

surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞,将无法响应按键,触屏等消息。当使用surfaceView,由于是在新的线程中更新画面,所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,按照预先设计,画面需要被改变,那么你就需要在surfaceView的thread中改变画面,这就需要一个event queue来保存touch event,并及时处理这些touch event,这会稍稍复杂一点,因为涉及到线程同步。

何时该使用SurfaceView?

当需要快速地更新View的UI,或者当渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。例如,使用3D图形,创建游戏,或者实时预览摄像头。

实际案例:

使用方法:

步骤

  1. 继承SurfaceView并实现SurfaceHolder.Callback接口
  2. SurfaceView.getHolder()获得SurfaceHolder对象
  3. SurfaceHolder.addCallback(callback)添加回调函数
  4. SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布
  5. Canvas绘画
  6. SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示

ClockSurfaceView

public class ClockSurfaceView extends SurfaceView {

    private static final String TAG = "ClockView";
    private Paint mPaint;
    private int widhth = 200;//控件的宽度
    private int height = 200;//控件的高度
    private int padding = 5;
    private Calendar mCalendar;
    private int mHour;//小时
    private int mMinuate;//分钟
    private int mSecond;//秒
    private float mDegrees;//因为圆是360度 我们有12个刻度 所以就是360/12
    private int mHourLineLen;//时指针 线
    private int mMinuateLine;//分钟 线
    private int mSecondLine;//表钟线
    SurfaceHolder holder;
    private boolean loop = true;

    public ClockSurfaceView(Context context) {
        this(context, null);
    }

    public ClockSurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ClockSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        //开启硬件加速
//        this.setLayerType(View.LAYER_TYPE_HARDWARE, null);
        holder = getHolder();
        setBackTransfluent();    //设置背景色为透明色
        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                new Thread(new ClockThread()).start();
            }

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

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                loop = false;
            }
        });
    }

    class ClockThread implements Runnable {//内部定义异步线程来完成绘画操作...

        @Override
        public void run() {
            // TODO Auto-generated method stub
            while (loop) {
                Canvas canvas = holder.lockCanvas(null);//锁定画布...
                clear(canvas);
                drawCircle(canvas);
                drawScale(canvas);
                canvasCenterCircle(canvas);
                drawPointer(canvas);
                drawStr(canvas);
                holder.unlockCanvasAndPost(canvas);//解除锁定
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(widhth, height);
        mHourLineLen = (int) (widhth / 2 * 0.6);
        mMinuateLine = (int) (widhth / 2 * 0.7);
        mSecondLine = (int) (widhth / 2 * 0.8);
    }


    private void setBackTransfluent() {
        this.setZOrderOnTop(true);
        //this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        holder.setFormat(PixelFormat.TRANSLUCENT);
    }


    private void initPaint() {
        mPaint = new Paint();
        mPaint.setStrokeWidth(3);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.parseColor("#FF4081"));
        mPaint.setAntiAlias(true);
    }

    private void clear(Canvas canvas) {
        Paint mPaint = new Paint();
        mPaint.setAntiAlias(true);//抗锯齿
        // canvas 清除画布内容。
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        canvas.drawPaint(mPaint);
    }

    /**
     * 绘制文字
     *
     * @param canvas
     */
    private void drawStr(Canvas canvas) {
        mPaint.setTextSize(24);
        StringBuffer sb = new StringBuffer();
        if (mHour < 10) {
            sb.append("0").append(String.valueOf(mHour)).append(":");
        } else {
            sb.append(String.valueOf(mHour)).append(":");
        }
        if (mMinuate < 10) {
            sb.append("0").append(String.valueOf(mMinuate)).append(":");
        } else {
            sb.append(String.valueOf(mMinuate)).append(":");
        }
        if (mSecond < 10) {
            sb.append("0").append(String.valueOf(mSecond));
        } else {
            sb.append(String.valueOf(mSecond));
        }
        String str = sb.toString();
        int strW = (int) mPaint.measureText(str);
        canvas.drawText(str, widhth / 2 - strW / 2, widhth / 2 + 30, mPaint);
    }

    /**
     * 在 圆中心绘制一个点
     *
     * @param canvas
     */
    private void canvasCenterCircle(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(widhth / 2, height / 2, 5, mPaint);
    }


    /**
     * 绘制圆
     *
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(widhth / 2, height / 2, widhth / 2 - padding, mPaint);
    }

    /**
     * 绘制刻度
     *
     * @param canvas
     */
    private void drawScale(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        for (int i = 0; i < 12; i++) {
            if (i % 3 == 0) {//  12  3  6  9对应的线长点
                canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 15, mPaint);
            } else {
                canvas.drawLine(widhth / 2 - padding, padding, widhth / 2 - padding, padding + 4 + 8, mPaint);
            }
            canvas.rotate(30, widhth / 2, widhth / 2);
        }
    }

    /**
     * 绘制时  分 表 指针
     *
     * @param canvas
     */
    private void drawPointer(Canvas canvas) {
        mCalendar = Calendar.getInstance();
        mHour = mCalendar.get(Calendar.HOUR);
        mMinuate = mCalendar.get(Calendar.MINUTE);
        mSecond = mCalendar.get(Calendar.SECOND);
        //小时的旋转度
        mDegrees = mHour * 30 + mMinuate / 2;
        mPaint.setColor(Color.BLACK);
        canvas.save();
        canvas.rotate(mDegrees, widhth / 2, widhth / 2);
        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mHourLineLen, mPaint);
        canvas.restore();
        //分钟
        mPaint.setColor(Color.parseColor("#666666"));
        mPaint.setStrokeWidth(5);
        mDegrees = mMinuate * 6 + mSecond / 10;
        canvas.save();
        canvas.rotate(mDegrees, widhth / 2, widhth / 2);
        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mMinuateLine, mPaint);
        canvas.restore();
        //绘制表针
        mPaint.setStrokeWidth(2);
        mPaint.setColor(Color.parseColor("#666666"));
        mDegrees = mSecond * 6;
        canvas.save();
        canvas.rotate(mDegrees, widhth / 2, widhth / 2);
        canvas.drawLine(widhth / 2, height / 2, widhth / 2, widhth / 2 - mSecondLine, mPaint);
        canvas.restore();
    }
}

在布局文件xml中引用即可。

效果:

参考

surfaceView和View最本质的区别
给surfaceview设置默认背景
SurfaceView设置背景透明

上一篇下一篇

猜你喜欢

热点阅读