SurfaceView简析

作者: 一只好奇的茂 | 来源:发表于2017-06-17 16:00 被阅读122次

    SurfaceView和View最本质的区别?

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

    何时该使用SurfaceView?

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

    实际案例:

    • 被动更新画面的。比如棋类,这种用view就好了。因为画面的更新是依赖于 onTouch 来更新,可以直接使用 invalidate。 因为这种情况下,这一次Touch和下一次的Touch需要的时间比较长些,不会产生影响。
    • 主动更新。比如一个人在一直跑动。这就需要一个单独的thread不停的重绘人的状态,避免阻塞main UI thread。所以显然view不合适,需要surfaceView来控制。

    使用方法:

    步骤

    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设置背景透明

    相关文章

      网友评论

        本文标题:SurfaceView简析

        本文链接:https://www.haomeiwen.com/subject/yptatxtx.html