哇!!SurfaceView 4 android~

前言:虽然在上一篇博客中, Android长按图片保存至相册 中我们实现了我们的功能,里面我们自定义了一个叫loaddingview的自定义控件,主要用于显示加载图片的进度。

这里写图片描述

可是当我们有很多图片,并且每个图片都有一个loaddingview作为加载标识的时候,我们的loaddingview就需要性能优化一下了,因为如果这么多loaddingview同时出现,因为我们是在属性动画中不断更新view的,然后view的更新跟绘制都是在主线程中完成的,想想都会很卡~!!!亦或是想想那种很大的3d游戏,很酷炫的界面,如果还是用普通的view的话,那估计画面太美了,都不敢想象。

那有没有可能在子线程中去更新view跟绘制view呢??? 有的!android早早的就给我们提供了一个叫surfaceview的东东~~ 我也没用过几次,准确的来说没有正式的用过这么一个东西。以前在学校玩过一点点,既然如此,那就让我们来看看这个东西。

一、什么是surfaceview?
surface的一些概念性的东西,小伙伴可以看看老罗的这篇博客(大神已经为我们解释的很清楚了):
http://blog.csdn.net/luoshengyang/article/details/8661317

二、surfaceView和View的区别?:

surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面。那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。

三、什么时候需要用surfaceview?

当需要快速地更新View的UI,或者当渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。 SurfaceView封装了一个Surface对象,而不是Canvas。这一点很重要,因为Surface可以使用后台线程绘制。对于那些资源敏感的 操作,或者那些要求快速更新或者高速帧率的地方,例如,使用3D图形,创建游戏,或者实时预览摄像头,这一点特别有用。

四、surfaceview带来的问题

通过上面的介绍,我们大概了解了下surfaceview,我们知道surfaceview是独立于ui线程完成的绘制工作的,这也说明了需要单独的为它开辟一块空间,需要更多的内存。

好啦!!说了那么多概念性的东西,我自己都累了,下面进入surfaceview的实践:

实现surfaceview的步骤大致为:

package com.leo.camerroll;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by leo on 17/1/23.
 */

public class testview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    public testview(Context context) {
        this(context,null);
    }

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

    public testview(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mHolder=getHolder();
        mHolder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        new Thread(){
            @Override
            public void run() {
                while(true){
                    Canvas canvas=mHolder.lockCanvas();
                    ///draw something
                    mHolder.unlockCanvasAndPost(canvas);
                }
            }
        }.start();
    }

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

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mHolder.removeCallback(this);
    }
}

好啦!!我们直接把我们前面博客中写的loaddingview的代码copy过来:

package com.leo.camerroll;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by leo on 17/1/23.
 */

public class SurLoadingView extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private boolean isRunning = true;
    private Thread mDrawTread;


    private int DEFAULT_RADIUS = dp2px(15);
    private int DEFAULT_REACH_COLOR = 0XFFFFFFFF;
    private int DEFAULT_UNREACH_COLOR = 0X88000000;
    private int DEFAULT_TEXT_COLOR = 0XFFFFFFFF;
    private long ANIM_DURATION = 1000;
    private String BASE_TEXT = "00%";
    private boolean isStop;

    private int mRadius = DEFAULT_RADIUS;
    private int mStrokeWidth;
    private Paint reachPaint;
    private Paint unreachPaint;
    private Paint textPaint;
    private Paint bgPaint;
    private int mStartAngle = 0;
    private float mSweepAngle = 360 * 0.382f;
    private int progress;
    private int max;

    public int getProgress() {
        return progress;
    }

    public void setProgress(int progress) {
        this.progress = progress;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

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

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

    public SurLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        //this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);

        reachPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        reachPaint.setStrokeCap(Paint.Cap.ROUND);
        reachPaint.setStyle(Paint.Style.STROKE);
        unreachPaint = new Paint(reachPaint);
        reachPaint.setColor(DEFAULT_REACH_COLOR);
        unreachPaint.setColor(DEFAULT_UNREACH_COLOR);
        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        textPaint.setColor(DEFAULT_TEXT_COLOR);
        textPaint.setFakeBoldText(true);
        textPaint.setStyle(Paint.Style.FILL);

        bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
        bgPaint.setStrokeCap(Paint.Cap.ROUND);
        bgPaint.setColor(Color.argb(44, 0, 0, 0));
        setMax(100);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int defWidth = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        int defHeight = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        int expectSize = Math.min(defHeight, defWidth);
        if (expectSize <= 0) {
            expectSize = mRadius * 2;
        } else {
            mRadius = expectSize / 2;
        }
        mStrokeWidth = mRadius / 5;
        reachPaint.setStrokeWidth(mStrokeWidth);
        unreachPaint.setStrokeWidth(mStrokeWidth);

        setMeasuredDimension(expectSize, expectSize);

        float textSize = 0;
        while (true) {
            textSize += 0.1;
            textPaint.setTextSize(textSize);
            if (textPaint.measureText(BASE_TEXT, 0, BASE_TEXT.length()) >= mRadius) {
                break;
            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        mDrawTread = new Thread() {
            @Override
            public void run() {
                while (isRunning) {
                    synchronized (mHolder) {
                        Canvas canvas = mHolder.lockCanvas(null);
                        drawContent(canvas);
                        if ((mStartAngle+=10) == 360) {
                            mStartAngle = 0;
                        }
                        SystemClock.sleep(10);
                    }
                }
            }
        };
        mDrawTread.start();
    }

    private void drawContent(Canvas canvas) {
        try {
            canvas.drawCircle(getWidth() / 2, getWidth() / 2, mRadius - mStrokeWidth, bgPaint);
            //draw reach
            drawProgressReach(canvas);
            //draw progress text
            drawProgressText(canvas);
        } catch (Exception e) {

        } finally {
            try {
                mHolder.unlockCanvasAndPost(canvas);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    private void drawProgressText(Canvas canvas) {
        String text = String.valueOf((int) (getProgress() * 1.0f / getMax() * 100)) + "%";
        int centerX = getWidth() / 2;
        int centerY = getWidth() / 2;
        int baseX = (int) (centerX - textPaint.measureText(text, 0, text.length()) / 2);
        int baseY = (int) (centerY - (textPaint.getFontMetrics().ascent + textPaint.getFontMetrics().descent) / 2);
        canvas.drawText(text, baseX, baseY, textPaint);
    }

    private void drawProgressReach(Canvas canvas) {
        canvas.drawArc(new RectF(0 + mStrokeWidth / 2, 0 + mStrokeWidth / 2, mRadius * 2 - mStrokeWidth / 2, mRadius * 2 - mStrokeWidth / 2), mStartAngle, mSweepAngle, false, reachPaint);
        //drawonreach
        canvas.drawArc(new RectF(0 + mStrokeWidth / 2, 0 + mStrokeWidth / 2, mRadius * 2 - mStrokeWidth / 2, mRadius * 2 - mStrokeWidth / 2), mStartAngle + mSweepAngle, 360 - mSweepAngle, false, unreachPaint);
    }

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

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isRunning = false;
        if (mDrawTread != null) {
            mDrawTread = null;
        }
    }

    /**
     * @param size
     * @return px
     */
    private int dp2px(int size) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, getContext().getResources().getDisplayMetrics());
    }
}

代码做了点小修改,把前面的属性动画改成了:

mDrawTread = new Thread() {
            @Override
            public void run() {
                while (isRunning) {
                    synchronized (mHolder) {
                        Canvas canvas = mHolder.lockCanvas(null);
                        drawContent(canvas);
                        if ((mStartAngle+=10) == 360) {
                            mStartAngle = 0;
                        }
                        SystemClock.sleep(10);
                    }
                }
            }
        };
        mDrawTread.start();

好啦!!我们来运行下我们的项目:
这里写图片描述

效果不对啊,surfaceview的背景咋是这样的呢?
我们尝试把cavans的背景改为透明:

canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

结果还是这吊样:
这里写图片描述

为了去掉surfaceview原有的背景除了把canvas的背景设为透明外,还需要加上:

private void initView() {
        mHolder = getHolder();
        mHolder.addCallback(this);
        //this.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        setZOrderOnTop(true);
        getHolder().setFormat(PixelFormat.TRANSLUCENT);
           。。。。。
       }

我们运行代码:
这里写图片描述

setZOrderOnTop(true);有什么用呢?

/**
     * Control whether the surface view's surface is placed on top of its
     * window.  Normally it is placed behind the window, to allow it to
     * (for the most part) appear to composite with the views in the
     * hierarchy.  By setting this, you cause it to be placed above the
     * window.  This means that none of the contents of the window this
     * SurfaceView is in will be visible on top of its surface.
     *
     * <p>Note that this must be set before the surface view's containing
     * window is attached to the window manager.
     *
     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
     */
    public void setZOrderOnTop(boolean onTop) {
        if (onTop) {
            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
            // ensures the surface is placed below the IME
            mLayout.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
        } else {
            mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
            mLayout.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
        }
    }

是否让surfaceview的surface在当前window的上面,也就是说是否让我们绘制的内容在我们的window的上面,默认是false的,、。

/**
     * Set the desired PixelFormat of the surface.  The default is OPAQUE.
     * When working with a {@link SurfaceView}, this must be called from the
     * same thread running the SurfaceView's window.
     * 
     * @param format A constant from PixelFormat.
     * 
     * @see android.graphics.PixelFormat
     */
    public void setFormat(int format);

surface的像素格式,默认是OPAQUE(不透明的),我们需要改为透明色。

好了,我们的loaddingview完美的实现啦!! 是不是soeasy呢?

最后附上demo的Git链接:
https://github.com/913453448/CamerRoll

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值