surfaceview自定义显示效果,在小米mix2上显示默认背景色黑色问题 5C

一下是控件代码



import android.content.Context;
import android.content.res.TypedArray;
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.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.voiceai.voicekey.R;

public class VolumeView extends SurfaceView implements Runnable {


    private SurfaceHolder mSurfaceHolder;

    private Paint paint;

    private int highVolColor = 0xFF4500, midVolColor = 0xFB90F, lowVolColor = 0x32CD32, edgeColor = 0xffffff;

    private int volValue, maxVolume;

    private float unitSize;

    private int width, height, edgeSize;


    /***
     * 是否在绘制:用于关闭子线程:true则表示一直循环
     */
    private boolean isDrawing = true;


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

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

    public VolumeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr);
        initSurface();
    }

    private void init(AttributeSet attrs, int defStyleAttr) {
        paint = new Paint();
        TypedArray array = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable
                .VolumeBar, defStyleAttr, 0);
        int n = array.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.VolumeBar_HighVolumeColor:
                    highVolColor = array.getColor(attr, 0xFF4500);
                    break;
                case R.styleable.VolumeBar_LowVolumeColor:
                    lowVolColor = array.getColor(attr, 0x32CD32);
                    break;
                case R.styleable.VolumeBar_MidVolumeColor:
                    midVolColor = array.getColor(attr, 0xFB90F);
                    break;
                case R.styleable.VolumeBar_VolumeValue:
                    volValue = array.getInt(attr, 50);
                    break;
                case R.styleable.VolumeBar_MaxVolume:
                    maxVolume = array.getInt(attr, 100);
                    break;
                case R.styleable.VolumeBar_EdgeColor:
                    edgeColor = array.getColor(attr, 0xffffff);
                    break;
                case R.styleable.VolumeBar_EdgeSize:
                    edgeSize = array.getDimensionPixelSize(attr, 0);
                    break;

            }
        }
    }

    private void initSurface() {


        /**通过holder去申请绘图表面的画布,surfaceview其实draw()或dispathDraw()都只是一块默认的黑色区域,并不是用作宿主
         * 真正要做的事情由开发者自行绘制,绘制之前就是通过holder获取一块内存区域的画布,
         * 然后可在UI线程或工作线程在这个画布上进行绘制所需要的视图,最后还是通过holder提交这个画布就可以显示
         *
         *
         * 生命周期自动和activity一起
         * */
        mSurfaceHolder = getHolder();

        setZOrderOnTop(true);
        //回调
        mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
            /***
             * surfaceview的绘图表面(就是activity宿主创建一个透明的表面用于surfaceView绘制)被创建时执行
             * 在updateWindow()创建宿主(activity的窗口)的绘图表面时会回调,虽然surfaceView是独立于一个线程但还是离不开宿主窗口,
             * 最后还是要粘贴到window中
             *
             * surfaceCreated方法,是当SurfaceView被显示时会调用的方法,所以你需要再这边开启绘制的线 程
             *
             * @param holder
             */
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                isDrawing = true;
                new Thread(VolumeView.this).start();
                Log.e("VolumeView", "SurfaceHolder生命周期surfaceCreated");
            }

            /**
             * 创建、更新会认为发生变化也会回调这个方法
             * @param holder
             * @param format
             * @param width
             * @param height
             */
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                Log.e("VolumeView", "SurfaceHolder生命周期surfaceChanged");
            }

            /***
             *surfaceDestroyed方法是当SurfaceView被隐藏会销毁时调用的方法,在这里你可以关闭绘制的线程
             * @param holder
             */
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                isDrawing = false;
                Log.e("VolumeView", "SurfaceHolder生命周期surfaceDestroyed");
            }
        });
    }

    public void setDrawing(boolean drawing) {
        isDrawing = drawing;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        height = measureHeight(heightMeasureSpec);
        width = measureWidth(widthMeasureSpec);
        setMeasuredDimension(width, height);
    }

    private int measureHeight(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        // Default size if no limits are specified.
        int result = 500;
        if (specMode == MeasureSpec.AT_MOST) {
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        }
        return result;
    }

    private int measureWidth(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        // Default size if no limits are specified.
        int result = 500;
        if (specMode == MeasureSpec.AT_MOST) {
            result = specSize;
        } else if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        }
        return result;
    }

    @Override
    public void run() {
        while (isDrawing) {
            draw();
        }
    }

    //设置音量
    public void setVolValue(int value) {
        if (value > maxVolume) {
            value = maxVolume;
        }
        this.volValue = value;
    }

    /***
     * 注意这个是在子线程中绘制的,surface支持子线程更新ui,所以
     */
    private void draw() {
        Canvas canvas = null;
        //给画布加锁,防止线程安全,防止该内存区域被其他线程公用
        try {
            synchronized (mSurfaceHolder) {
                canvas = mSurfaceHolder.lockCanvas();
                if (null != canvas) {
                    //清屏操作或者设置背景
                    onDrawPic(canvas);
                    Thread.sleep(50);
                }
            }
        } catch (Exception e){
            Log.e("VolumeView", "VolumeView异常:"+e.getMessage());
        }finally {
            //提交显示视图并解锁,防止长期占用此内存
            if (null != mSurfaceHolder) mSurfaceHolder.unlockCanvasAndPost(canvas);
        }


    }


    protected void onDrawPic(Canvas canvas) {
        final Rect rect = new Rect(0, 0, this.width, this.width);
        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);

        unitSize = (float) (width - 2 * edgeSize) / (float) maxVolume;

        int drawSize = Math.round(volValue * unitSize);

        //颜色渐变原则:当音量小于50时,R值递增/递减(根据指定的low、mid颜色值来判断),G值不变,B值递增/递减,
        //当音量大于50时,R值不变,G值递减/递增(),B值递增/递减

        float redColorUnitFirstStage = (float) (((midVolColor & 0xff0000) - (lowVolColor &
                0xff0000)) >> 16) / 70;
        float greenColorUnitFirstStage = (float) (((midVolColor & 0x00ff00) - (lowVolColor &
                0x00ff00)) >> 8) / 70;
        float blueColorUnitFirstStage = (float) (((midVolColor & 0x0000ff) - (lowVolColor &
                0x0000ff))) / 70;

        float redColorUnitSecondStage = (float) (((highVolColor & 0xff0000) - (midVolColor &
                0xff0000)) >> 16) / 30;
        float greenColorUnitSecondStage = (float) (((highVolColor & 0x00ff00) - (midVolColor &
                0x00ff00)) >> 8) / 30;
        float blueColorUnitSecondStage = (float) (((highVolColor & 0x0000ff) - (midVolColor &
                0x0000ff))) / 30;

        int red = (lowVolColor & 0xff0000) >> 16;
        int green = (lowVolColor & 0x00ff00) >> 8;
        int blue = (lowVolColor & 0x0000ff);

        //绘制有颜色区域
        for (int i = 0; i < volValue; i++) {
            if (i <= 70) {
                //当音量小于50时,更新颜色的R值,递增/递减。
                red = red + (int) redColorUnitFirstStage;
                green = green + (int) greenColorUnitFirstStage;
                blue = blue + (int) blueColorUnitFirstStage;

                int colorValue = Color.rgb(red, green, blue);
                paint.setColor(colorValue);
                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
                canvas.drawRect(i * unitSize, edgeSize, (i + 1) * unitSize + 1, height - edgeSize, paint);
            } else {
                //当音量小于50时,更新颜色的G值,递减。
                red = red + (int) redColorUnitSecondStage;
                green = green + (int) greenColorUnitSecondStage;
                blue = blue + (int) blueColorUnitSecondStage;

                int colorValue = Color.rgb(red, green, blue);
                paint.setColor(colorValue);
                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
                canvas.drawRect(i * unitSize, edgeSize, (i + 1) * unitSize + 1, height, paint);
            }
        }

        //绘制边框
        paint.setColor(edgeColor);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //绘制左边框
        canvas.drawRect(0, 0, edgeSize, height, paint);
        //绘制上边框
        canvas.drawRect(0, 0, width, edgeSize, paint);
        //绘制下边框
        canvas.drawRect(0, height - edgeSize, width, height, paint);
        //绘制有边框
        canvas.drawRect(width - edgeSize, 0, width, height, paint);
        //绘制第一个刻度
        canvas.drawRect(width / 4, height - 10 * edgeSize, width / 4 + 2 * edgeSize, height, paint);
        //绘制第二个刻度
        canvas.drawRect(width * 4 / 5, height - 10 * edgeSize, width * 4 / 5 + 2 * edgeSize, height, paint);

    }

}

在activity调用了 vVolumeBar.setVolValue(20);然后其他手机都ok就是小米,初次进去就会显示默认黑色背景,再次回到该页面显示效果正常

2个回答

我自己找到答案了:改成了这样

   /***
     * 注意这个是在子线程中绘制的,surface支持子线程更新ui,所以
     */
    private void draw() {
        Canvas canvas = null;
        //给画布加锁,防止线程安全,防止该内存区域被其他线程公用
        if(mSurfaceHolder!=null&&isDrawing)
            canvas = mSurfaceHolder.lockCanvas();

        try {
            if (null != canvas) {
                //清屏操作或者设置背景
                canvas.drawColor(Color.BLACK);
                onDrawPic(canvas);

            }
        } catch (Exception e) {

        } finally {
            //提交显示视图并解锁,防止长期占用此内存
            if(mSurfaceHolder!=null&&isDrawing)
                mSurfaceHolder.unlockCanvasAndPost(canvas);
        }

    }

但是新的问题:
AndroidRuntime: java.lang.IllegalArgumentException: canvas object must be the same instance that was previously returned by lockCanvas?同样请教

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!