Android将自定义的SurfaceView保存为bitmap

文章介绍了如何将View和SurfaceView转换为Bitmap,包括常规的View方法,SurfaceView特殊处理(如使用Path和SurfaceHolder),以及在SurfaceView不可见时的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、正常将View保存为Bitmap的方法:

    private Bitmap getViewToBitmap(View view) {
//        layoutView(view);
        //创建Bitmap,最后一个参数代表图片的质量.
        Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888);
        if (bitmap != null) {
            //View把内容绘制到canvas上,同时保存在bitmap.
            Canvas c = new Canvas(bitmap);
//            c.drawColor(Color.WHITE);
            view.draw(c);
            c.setBitmap(null);
        }

        return bitmap;
    }

2、SurfaceView保存为bitmap 比较特殊:

1.通过SurfaceView绘制图形,我们可以得到绘制图形后的path;

2.启用新的canvas,将path绘制到canvas

使用示例:

Bitmap bitmap = wave_view.getBitmap(“111111111”);

public class AudioWaveView extends SurfaceView implements Callback, Runnable, Serializable {
    private short[] mWaveData;
    private List<Short> pointList = Collections.synchronizedList(new LinkedList<>());
    private Paint mPaint;
    private Paint mPaintIssue;
    private SurfaceHolder mHolder;
    private int mWidth = 0;
    private int mHeight = 0;
    private int mCenterY = 0;
    //网格颜色
    protected int mGridColor = Color.parseColor("#D7D7D7");
    protected Paint paint;
    public float points[];
    private boolean isRunning = false;
    private boolean isDraw = false;
    List<Short> nativeDatas = null;
    volatile ThreadPoolExecutor singleThreadExecutor;//单线程池
    private Canvas canvas = null;
    private int len = 0;
    private int index = 0;
    private volatile boolean isRedPaint;
    private boolean isDrawArea;//是否绘制背景区域
    private ISurfaceViewListener surfaceViewListener;

    public void setSurfaceViewListener(ISurfaceViewListener surfaceViewListener) {
        this.surfaceViewListener = surfaceViewListener;
    }

    //    private Runnable drawRunAble = new Runnable() {
//        @Override
//        public void run() {
//                drawFrame();
//        }
//    };
    @Override
    public void run() {
        while (isRunning) {
            if (isDrawArea) {
                drawArea();
            }
            if (isDraw) {
                drawFrame();
            } else {
                Thread.yield();
            }
        }
    }

    synchronized void drawArea() {
        canvas = null;
        try {
            if (!isRunning) {
                return;
            }
            canvas = mHolder.lockCanvas();
            if (canvas != null) {
                // draw something
                canvas.drawRect(mWidth / 4, 0, mWidth / 4 * 3, mHeight, mPaintIssue);
                isDrawArea = false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            isDrawArea = false;
        } finally {
            isDrawArea = false;
            if (canvas != null)
                try {
                    mHolder.unlockCanvasAndPost(canvas);
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }

    //    public void addWaveDataInVisiable(short[] waveData, int height) {
    public void addWaveDataInVisiable(short[] waveData) {
        this.isRedPaint = true;
        this.isRunning = true;
//        mCenterY = height / 2;
//        initViewInvisiable();
        Log.i("caowj", "addWaveData mCenterY=" + mCenterY);
        addWaveData(waveData);
    }

    public void addWaveDataAndPaint(short[] waveData, boolean isRedPaint) {
        this.isRedPaint = isRedPaint;
        addWaveData(waveData);
    }

    public void addWaveData(short[] waveData) {
        Log.d("caowj", "isRuning = " + isRunning + ",mCenterY=" + mCenterY);
        if (!isRunning || waveData == null) {
            return;
        }
        if (nativeDatas == null) {
            nativeDatas = new ArrayList<>();
        }
        for (int i = 0; i < waveData.length; i++) {
            nativeDatas.add((short) (mCenterY - waveData[i] / (32768 / mCenterY)));
        }
        Log.d("caowj", "dataSize=" + nativeDatas.size());
        if (nativeDatas.size() >= 800) {
            addPointThreadExecutor(nativeDatas);
            nativeDatas = new ArrayList<>();
        }
    }

    private void addPointThreadExecutor(List<Short> nativeDatas) {
        if (!isRunning || nativeDatas == null) {
            return;
        }
        if (singleThreadExecutor == null || singleThreadExecutor.isShutdown()) {
            startSingleThreadExecutor();
            return;
        }
        //Log.e("====>", "singleThreadExecutor.getQueue().size() = " + singleThreadExecutor.getQueue().size());
        if (singleThreadExecutor.getQueue().size() >= 5) {
            Log.e("====>", "singleThreadExecutor.getQueue().size() = " + singleThreadExecutor.getQueue().size());
            return;
        }
        singleThreadExecutor.execute(new Runnable() {
            @Override
            public void run() {
                Log.d("caowj", "单线程执行");
                List<Short> dataList = nativeDatas;
                synchronized (pointList) {
                    for (int i = 0; i < dataList.size(); i += 10) {
                        if (pointList.size() >= mWidth && mWidth > 0) {
                            pointList.remove(0);
                        }
                        pointList.add(dataList.get(i));
                    }
                    isDraw = true;
                }
            }
        });
    }

    synchronized void drawFrame() {
        canvas = null;
        try {
            if (!isRunning) {
                return;
            }
            canvas = mHolder.lockCanvas();
            if (canvas != null) {
                // draw something
                drawCube(canvas);
            } else {
                Log.w("caowj", "canvas is null");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (canvas != null)
                try {
                    mHolder.unlockCanvasAndPost(canvas);
                } catch (Exception e) {
                    e.printStackTrace();
                }
        }
    }

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

    public AudioWaveView(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        initView();
    }

    private void initView() {
        if (mHolder == null) {
            mHolder = getHolder();
            mHolder.addCallback(this);
            Log.d("caowj", "initView  addCallback");
            setZOrderOnTop(true);// 设置画布 背景透明
            mHolder.setFormat(PixelFormat.TRANSLUCENT);
        }
        Rect frame = mHolder.getSurfaceFrame();
        mHeight = frame.height();
        mCenterY = frame.centerY();
        mWidth = frame.width();
        Log.d("caowj", "initView  mCenterY=" + mCenterY);
        points = new float[mWidth * 4];
        if (mPaint == null) {
            mPaint = new Paint();
            mPaint.setColor(Color.parseColor("#0077D4"));
            mPaint.setAntiAlias(true);
            mPaint.setStrokeWidth(2);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStyle(Paint.Style.STROKE);
        }
        if (paint == null) {
            paint = new Paint();
            paint.setColor(mGridColor);
            paint.setStrokeWidth(1);
        }
        mPaintIssue = new Paint();
        mPaintIssue.setColor(Color.parseColor("#7F0077D4"));
        mPaintIssue.setAntiAlias(true);
        mPaintIssue.setStrokeWidth(1);
        mPaintIssue.setStrokeCap(Paint.Cap.ROUND);
        mPaintIssue.setStyle(Paint.Style.FILL_AND_STROKE);
    }

    public void drawIssueArea() {
        this.isDrawArea = true;
        new Thread(this).start();
    }

    public void drawCube(Canvas canvas) {
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        /*画波形,数据在之前已经填充好了*/
        len = pointList.size();
        Log.d("caowj", "drawCube  len="+len+","+(mWidth-len));
        if (len >= 2) {
            index = mWidth - len;
            for (int i = index + 1; i < mWidth; i++) {
                points[i * 4] = i - 1;
                points[i * 4 + 1] = pointList.get(i - index - 1);
                points[i * 4 + 2] = i;
                points[i * 4 + 3] = pointList.get(i - index);
            }
        }
        if (isRedPaint) {
            mPaint.setColor(Color.RED);
        } else {
            mPaint.setColor(Color.parseColor("#0077D4"));
        }
        canvas.drawLines(points, mPaint);
        isDraw = false;
    }

    @Override
    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        Log.d("caowj", "onVisibilityChanged = " + visibility);
        if (visibility == VISIBLE) {
            //LoggerUtil.d("onVisibilityChanged : VISIBLE");
            isRunning = true;
            //mDrawWaveThread.execute(drawRunAble);
        } else if (visibility == INVISIBLE) {
//            LoggerUtil.d("onVisibilityChanged : INVISIBLE");
            isRunning = false;
            //pointList.clear();
        }
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHeight = h;
        mWidth = w;
        mCenterY = h / 2;
        Log.w("caowj", "onSizeChanged  mCenterY=" + mCenterY + ",mWidth=" + mWidth);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//        Log.d("caowj", "surfaceChanged");
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.w("caowj", "surfaceCreated");
        initView();
        initViewInvisiable();
        if (surfaceViewListener != null) {
            surfaceViewListener.onSurfaceCreated(mWidth, mHeight);
        }
    }

    private void initViewInvisiable() {
        isRunning = true;
        Thread thread = new Thread(this);
        thread.start();
        startSingleThreadExecutor();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isRunning = false;
        Log.w("caowj", "surfaceDestroyed");
    }

    private void startSingleThreadExecutor() {
        if (singleThreadExecutor != null && !singleThreadExecutor.isShutdown()) {
            singleThreadExecutor.shutdownNow();
        }
        singleThreadExecutor = new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(10));
    }

    public void clearDatas() {
        if (pointList != null) {
            pointList.clear();
        }
        if (nativeDatas != null) {
            nativeDatas.clear();
        }
        startSingleThreadExecutor();
        isDraw = true;
        points = new float[mWidth * 4];
        drawFrame();
    }

    public Bitmap getBitmap(String title) {
        //新建bitmap
        Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
        //创建新的画布
        Canvas canvas = new Canvas(bitmap);
        //设置画布背景色
//        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        canvas.drawColor(Color.WHITE);
        //设置绘制路径、画笔
        drawGird(canvas, Color.parseColor("#D7D7D7"));
        canvas.drawLines(points, mPaint);
        if (!TextUtils.isEmpty(title)) {
            Paint textPaint = new Paint();
            textPaint.setColor(Color.parseColor("#0077D4"));
            textPaint.setAntiAlias(true);//设置画笔是否抗锯齿
            textPaint.setStrokeWidth(1);
            textPaint.setStrokeCap(Paint.Cap.ROUND);
            textPaint.setStyle(Paint.Style.STROKE);
            textPaint.setTextSize(16f);
            canvas.drawText(title, 20, 30, textPaint);
        }
        return bitmap;
    }

    /**
     * 画网格
     *
     * @param canvas
     * @param color
     */
    private void drawGird(Canvas canvas, int color) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(color);
        paint.setStrokeWidth(2);
        //float colSpace = width * 1f / cols;
        int colSpace = 20;
        int cols = this.mWidth / colSpace;
        int heightCount = (int) Math.floor((double) mHeight / 20);//向下取整
        float space = (this.mHeight - heightCount * 20) / 2;
        //画竖线
        for (int i = 0; i <= this.mWidth / 20; i++) {
            canvas.drawLine(i * colSpace, space, i * colSpace, heightCount * colSpace + space, paint);
        }
        //画横线
        for (int i = 0; i <= heightCount; i++) {
            canvas.drawLine(0, i * colSpace + space, mWidth, i * colSpace + space, paint);
        }
    }
}

3、使用LayoutInflater加载SurfaceView

使用 LayoutInflater.from(getContext()).inflate() 加载SurfaceView,等SurfaceView渲染完成后将View保存为图片;

疑难问题点:

  • SurfaceView 不可见时,不会触发 surfaceCreated(),surfaceChanged()等方法;需要先将SurfaceView先添加到父View中,渲染完成后再移除;
  • SurfaceView 的内容渲染需要时间,需要添加 addOnGlobalLayoutListener()监听;
  • SurfaceView 不同于普通View,无法把内容绘制到canvas上;需要使用前文的方法获取Bitmap;
  private void createWaveImage(AudioFile file, String fileName, int index, boolean isLast) {
        View rootView = LayoutInflater.from(getContext()).inflate(R.layout.item_wave_view, llContent, false);
        TextView tvTitle = rootView.findViewById(R.id.tv_file_create_date);
        AudioWaveView waveView = rootView.findViewById(R.id.wave_view);

        waveView.setSurfaceViewListener(new ISurfaceViewListener() {
            
            public void onSurfaceCreated(int width, int height) {
                waveView.setSurfaceViewListener(null);

                String[] arr = fileName.split("\\.");
                String imageName = arr[0] + "(" + index + ")";
                tvTitle.setText(imageName);

                file.refreshData(index, 16000);
                waveView.addWaveDataInVisiable(file.getData().clone());

                rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    
                    public void onGlobalLayout() {
                        // 移除监听器以避免不必要的回调
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                            rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                        } else {
                            // 对于 API 级别低于 16 的情况,使用 deprecated 的方法
                            rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                        }
                        new Handler().postDelayed(new Runnable() {
                            
                            public void run() {
                                Bitmap bitmap = waveView.getBitmap(imageName);// SurfaceView无法把内容绘制到canvas上
                                boolean result = saveBitmap(bitmap, imageName + ".jpg");
                                Log.w("caowj", "图片保存:" + result);
                                if (result) {
                                    llContent.removeView(rootView);
                                    if (isLast) {
                                        //等图片保存成功后,再生成PDF
                                        // TODO: 2024/5/10 测试参数
                                        createPdf(mRecordEntity, "苏州附属第二医院", "123456789", "异常");
                                    }
                                }
                            }
                        }, 500);
                    }
                });
            }
        });
        llContent.addView(rootView);//触发 SurfaceView的Create
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值