SurfaceView简析

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容