自定义波形图View,LayoutInflater动态加载控件保存为本地图片

news2025/2/27 8:12:29

效果图:
在这里插入图片描述

页面布局:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="@dimen/dp_145"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/white">

    <TextView
        android:id="@+id/tv_index"
        android:layout_width="@dimen/dp_30"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:background="@color/common_btn_clicked"
        android:gravity="center"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_14"
        tools:text="TH"/>

    <RelativeLayout
        android:id="@+id/rl_wave_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white">

        <com.kl.common_base.view.wave.GridView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:bgColor="#f0d3a9"/>

        <com.kl.common_base.view.wave.PdfEcgWaveView
            android:id="@+id/wave_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="visible" />

        <TextView
            android:id="@+id/tv_file_create_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/dp_20"
            android:layout_marginTop="@dimen/dp_10"
            android:text=""
            android:textColor="@color/colorPrimary"
            android:textSize="@dimen/sp_12"
            tools:text="2019年6月26日11:54" />
    </RelativeLayout>
</LinearLayout>

自定义波形图控件:

package com.kl.common_base.view.wave;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.kl.common_base.R;
import com.kl.common_base.utils.SizeUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class PdfEcgWaveView extends View {
    private List<Short> pointList = Collections.synchronizedList(new LinkedList<>());
    private Paint mPaint;
    private Paint mPaintLine;
    private int mWidth = 0;
    private int mHeight = 0;
    private int mCenterY = 0;
    public float points[];
    List<Short> nativeDatas = null;
    private int len = 0;
    private int index = 0;

    private int zoom;
    private float gapX = 0.2f;
    private int xSize = 0;
    private final int maxMillimeter = 25;
    private final int FILTER_SIZE = 50;
    private int sampleRate = 8000 / FILTER_SIZE;
    private int gain = 5;
    private final int maxMidScopeY = 0;
    private double screenTotalTime;
    private int[] lineArray = new int[]{7321, 8521, 9600, 10875};
    private Map<Integer, Integer> lineMap = new HashMap();
//    private List<Float> xList = new ArrayList<>();


    public PdfEcgWaveView(Context context) {
        super(context);
        Log.d("caowj", "55555555555555555555555555");
        initPaint();
    }

    public PdfEcgWaveView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        Log.d("caowj", "66666666666666666666666666");
        initPaint();

    }

    public PdfEcgWaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        Log.d("caowj", "77777777777777777777777777");
        initPaint();

    }

    private void initPaint() {
        Log.d("caowj", "initPaint--------------");
        if (mPaint == null) {
            mPaint = new Paint();
            mPaint.setColor(Color.BLACK);
            mPaint.setAntiAlias(true);
            mPaint.setStrokeWidth(2);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStyle(Paint.Style.STROKE);
        }
        if (mPaintLine == null) {
            mPaintLine = new Paint();
            mPaintLine.setColor(getContext().getResources().getColor(R.color.red));
            mPaintLine.setAntiAlias(true);
            mPaintLine.setStrokeWidth(2);
            mPaintLine.setStrokeCap(Paint.Cap.ROUND);
            mPaintLine.setStyle(Paint.Style.STROKE);
        }
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldw, int oldh) {
        mHeight = getHeight();
        mCenterY = mHeight / 2;
        mWidth = getWidth();
//        Log.d("caowj", "onSizeChanged--------------"+mWidth+",,,"+mHeight+",,,"+mCenterY);

        zoom = height / maxMillimeter;
//        double speed = 0.04;
        double speed = (double) 2 / ((double) width / zoom);

        Log.e("caowj", "speed=" + speed);
        screenTotalTime = width / zoom * speed;
        gapX = (float) (this.mWidth / (sampleRate * screenTotalTime));
        xSize = Math.round(this.mWidth / gapX);
        points = new float[xSize * 4];
        Log.e("caowj", "初始化计算:zoom=" + zoom + ",screenTotalTime=" + screenTotalTime + ",gapX=" + gapX + ",widthSize=" + xSize);
        super.onSizeChanged(width, height, oldw, oldh);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("caowj", "onMeasure--------------" + widthMeasureSpec + ",,," + heightMeasureSpec + ",,," + mCenterY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Log.w("caowj", "onDraw-----------" + pointList.size());
        len = pointList.size();
        if (len >= 2) {
            index = xSize - len;
//            Log.d("caowj", "drawCube  len=" + len + ",widthSize=" + widthSize + ",index=" + index);
            for (int i = index + 1; i < xSize; i++) {
                float startX = (i - 1) * gapX;
                int mIndex = i - index - 1;
                points[i * 4] = startX;
                points[i * 4 + 1] = pointList.get(mIndex);
                points[i * 4 + 2] = i * gapX;
                points[i * 4 + 3] = pointList.get(i - index);

                if (lineMap.containsKey(mIndex)) {
                    Log.w("caowj", "找到需要绘制竖线的位置了:" + startX);
//                    xList.add(startX);
                    int padding = SizeUtils.dp2px(getResources().getDimension(R.dimen.dp_25));
                    canvas.drawLine(startX, padding, startX, mCenterY * 2 - padding, mPaintLine);
                }
            }
        }

        canvas.drawLines(points, mPaint);

        super.onDraw(canvas);
    }


    public void addWaveDataInVisiable(short[] waveData) {
        if (nativeDatas == null) {
            nativeDatas = new ArrayList<>();
        }

        for (int i = 0; i < waveData.length; i++) {
            short y = (short) Math.floor(calcRealMv(maxMidScopeY - waveData[i]) * gain * zoom + mCenterY);
            nativeDatas.add(y);
//            Log.e("caowj", "y=" + y);
        }
        Log.e("caowj", "nativeDatas 长度=" + nativeDatas.size());
        if (nativeDatas.size() >= 800) {
            addPointThreadExecutor(nativeDatas);
            nativeDatas = new ArrayList<>();
        }
    }

    private void addPointThreadExecutor(List<Short> nativeDatas) {
        if (nativeDatas == null) {
            return;
        }

        List<Short> dataList = nativeDatas;
        synchronized (pointList) {
            for (int i = 0; i < dataList.size(); i += FILTER_SIZE) {
                if (pointList.size() >= xSize && xSize > 0) {
                    pointList.remove(0);
                }

                pointList.add(dataList.get(i));

                for (int linePosition : lineArray) {
                    if (linePosition > i - FILTER_SIZE && linePosition <= i) {
                        lineMap.put(pointList.size(), linePosition);
                    }
                }
            }
        }
    }


    public void clear() {
        if (pointList != null) {
            pointList.clear();
        }
        if (nativeDatas != null) {
            nativeDatas.clear();
        }

        points = new float[xSize * 4];
        postInvalidate();
    }


    /**
     * mintti  计算真实毫伏值
     *
     * @param point
     * @return
     */
    private float calcRealMv(int point) {
        return (float) (point * 3.3 / 32767);
//        int magnification = 1000;//TODO 放大倍数
//        return (float) (point / magnification * 3.3 / 32767 * 1000);
    }
}
 public static void createWaveImage(Context context, ViewGroup parentView, AudioFile audioFile, String parentDir, String fileName, int index, boolean isLast) {
        Log.w("caowj", "createWaveImage");

        View rootView = LayoutInflater.from(context).inflate(R.layout.item_wave_view, null);
        PdfEcgWaveView waveView = rootView.findViewById(R.id.wave_view);
        TextView tvTitle = rootView.findViewById(R.id.tv_file_create_date);
        TextView tvIndex = rootView.findViewById(R.id.tv_index);
        tvIndex.setText("TL");
        String[] arr = fileName.split("\\.");
        String imageName = arr[0] + "(" + index + ")";
        String imgTitle = "";
        if (arr[0].startsWith("0_")) {
            imgTitle = StringUtils.getPositionAiResult(fileName);
        }
        if (TextUtils.isEmpty(imgTitle)) {
            imgTitle = imageName;
        }
        tvTitle.setText(imgTitle);

        int width = SizeUtils.getScreenWidth() * 2 / 3;
        int height = SizeUtils.dp2px(context.getResources().getDimension(R.dimen.dp_145));

        layoutView(rootView, width, height);

        audioFile.refreshPcmDataByPosition(index, 8000 * 2);
        waveView.addWaveDataInVisiable(audioFile.getData().clone());


        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        // 利用bitmap生成画布
        Canvas canvas = new Canvas(bitmap);
        // 把view中的内容绘制在画布上
        rootView.draw(canvas);// 触发draw()

        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("caowj", "生成Bitmap");
                boolean result = FileUtils.saveWaveBitmap(bitmap, parentDir, imageName + ".jpg");
            }
        }).start();

    }

    protected static void layoutView(View v, int w, int h) {
        v.layout(0, 0, w, h);
        int measuredWidth = View.MeasureSpec.makeMeasureSpec(w, View.MeasureSpec.EXACTLY);
        int measuredHeight = View.MeasureSpec.makeMeasureSpec(h, View.MeasureSpec.EXACTLY);
        v.measure(measuredWidth, measuredHeight);
        Log.w("caowj", measuredWidth + "--" + measuredHeight + ";;;" + v.getMeasuredWidth() + "---" + v.getMeasuredHeight());
        v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1913756.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web3学习路线图,从入门到精通

前面我们聊了Web3的知识图谱&#xff0c;内容是相当的翔实&#xff0c;要从哪里入手可以快速的入门Web3&#xff0c;本篇就带你看看Web3的学习路线图&#xff0c;一步一步深入学习Web3。 这张图展示了Web3学习路线图&#xff0c;涵盖了区块链基础知识、开发方向、应用开发等内…

Canvas:掌握图像变换合成与裁剪状态像素操作

想象一下&#xff0c;用几行代码就能创造出如此逼真的图像和动画&#xff0c;仿佛将艺术与科技完美融合&#xff0c;前端开发的Canvas技术正是这个数字化时代中最具魔力的一环&#xff0c;它不仅仅是网页的一部分&#xff0c;更是一个无限创意的画布&#xff0c;一个让你的想象…

【深度学习(42)】通过vscode使用anaconda的python环境

按ctrlshiftp&#xff0c;选择Python:Select Interpreter 选择anaconda下的python虚拟环境

【spark】Exception in thread “main“ ExitCodeException exitCode=-1073741701

在window上运行spark程序写到本地文件的时候报错。 val rdd sc.sparkContext.parallelize(list)val arr rdd.collect()arr.foreach(println)rdd.saveAsTextFile("test1")sc.close()错误信息: zhangsan lisi wangwu Exception in thread "main" ExitCode…

连续6年夺冠 6项细分领域第一,中电金信持续领跑中国银行业IT解决方案市场

7月9日&#xff0c;工信部赛迪顾问发布《2023年度中国银行业IT解决方案市场分析报告》&#xff08;简称《报告》&#xff09;。中电金信以7.38%的市场份额再度蝉联2023中国银行业IT解决方案市场份额第一&#xff0c;以显著优势持续领跑中国银行业IT解决方案市场。在细分领域&am…

【普中】基于51单片机的矩阵电子密码锁LCD1602液晶显示 proteus仿真+程序+设计报告+讲解视频

【普中】基于51单片机的矩阵电子密码锁LCD1602液晶显示设计 1.主要功能&#xff1a;讲解视频&#xff1a;2.仿真3. 程序代码4. 设计报告5. 设计资料内容清单&&下载链接资料下载链接&#xff1a; 【普中】基于51单片机的矩阵电子密码锁LCD1602液晶显示设计 ( proteus仿真…

UV胶,它是否有毒?如同那些隐藏在黑暗中的危险之物?

UV胶&#xff0c;它是否有毒&#xff1f;如同那些隐藏在黑暗中的危险之物&#xff1f; 关于uv胶的毒性问题&#xff0c;或许我们可以这样深入探讨。UV胶&#xff0c;如同一位戴着神秘面纱的访客&#xff0c;在我们的生活中悄然出现&#xff0c;却带着诸多疑问。那么&#xff0…

怎样把视频字幕提取出来?分享4个零门槛的字幕提取工具

暑假正是弯道超车的好机会&#xff01;相信不少朋友都会选择宅在家自学网课。 不可否认的是&#xff0c;海量学习资源的确可以让学习变得更加便捷与自由。然而&#xff0c;如何高效地吸收和理解在线课程也就成为了一个关键问题。不敢想倘若此时能够拥有一款高效又实用的视频提…

菜花插画:成都亚恒丰创教育科技有限公司

菜花插画&#xff1a;田园诗意的视觉盛宴 在纷扰繁杂的都市生活中&#xff0c;人们往往渴望一抹清新与宁静&#xff0c;以慰藉心灵的疲惫。而菜花插画&#xff0c;恰似一股来自乡野的清风&#xff0c;以其独特的田园诗意&#xff0c;成都亚恒丰创教育科技有限公司为我们的视觉…

结构体案例1

代码 #include <iostream> using namespace std; #include <string> #include <ctime>//学生的结构体 struct Student {string sName;int score; }; //老师的结构体定义 struct Teacher {string tName;struct Student sArray[5]; };//给老师和学生赋值的函数…

HTML5使用<progress>进度条、<meter>刻度条

1、<progress>进度条 定义进度信息使用的是 progress 标签。它表示一个任务的完成进度&#xff0c;这个进度可以是不确定的&#xff0c;只是表示进度正在进行&#xff0c;但是不清楚还有多少工作量没有完成&#xff0c;也可以用0到某个最大数字&#xff08;如&#xff1…

在网上申请流量卡审核失败,可能是你的年龄有问题!

在网上申请流量卡审核失败&#xff0c;可能是你的年龄有问题&#xff01; 先上个图&#xff1a; ​ 网上的流量卡并不是随意申请的&#xff0c;而是填写申请信息后由运营商进行审核&#xff0c;审核通过后才会发卡&#xff0c;如果你提交的订单没有审核通过&#xff0c;那么大…

Unity之OpenXR+XR Interaction Toolkit实现 Gaze眼部追踪

使用 Unity OpenXR 实现Gaze眼部追踪 在虚拟现实(VR)和增强现实(AR)应用中,眼动追踪是一项强大而受欢迎的技术。它可以让开发者更好地理解用户的注意力和行为,并创造出更加沉浸和智能的体验。在本文中,我们将探讨如何使用 Unity OpenXR 实现Gaze眼部追踪功能。 Unity …

MySQL体系架构解析

1.MySQL体系架构 1.1.MySQL的分支与变种 MySQL变种有好几个,主要有三个久经考验的主流变种:Percona Server,MariaDB和 Drizzle。它们都有活跃的用户社区和一些商业支持,均由独立的服务供应商支持。同时还有几个优秀的开源关系数据库,值得我们了解一下。 1.1.1.Drizzle …

RockyLinux9上安装Nacos2.3.0(非Docker安装)

RockyLinux9上安装Nacos2.3.0 说明什么是Nacos下载并安装创建一个nacos-conf数据库修改application.properties文件 启动如果在项目中使用需要注意访问网址查看是否成功 开启访问鉴权 说明 本文采用的是&#xff1a;安装包安装&#xff0c;非Docker安装&#xff0c;系统采用的R…

雷池WAF动态防护功能初体验

一、 介绍 大名鼎鼎的雷池WAF最近新上了个名为 动态防护 的功能 所谓动态防护&#xff0c;是在用户浏览到的网页内容不变的情况下&#xff0c;将网页赋予动态特性&#xff0c;即使是静态页面&#xff0c;也会具有动态的随机性。 说白了就是给你网站的 html 和 js 代码加上加密…

Linux下为什么ls直接就可以运行,而你的程序要写./dir1/dir2/bin/bwa才可以

习惯了Windows电脑下的所见即所得&#xff0c;找到程序或文件双击即可运行或打开&#xff1b;于是我们被惯得以为电脑会像人一样聪明&#xff0c;给他一个名字就可以运行程序或打开文件&#xff1b;于是在命令行下或程序里不断碰壁&#xff0c;为啥这个命令不运行了呢&#xff…

2024年的设计理念革新:快速获取设计趋势的资源集合!

随着2024年第三季度开始&#xff0c;今年的设计趋势也逐渐出现。与2023 年设计相比&#xff0c;趋势变化空间不大&#xff0c;大部分是在 2023 年度设计趋势的延伸和发展。即使趋势不会一直改变&#xff0c;了解趋势对设计师来说仍然非常重要。接下来&#xff0c;本文将与你分享…

lora训练调参

Stable Diffusion 训练指南 (LyCORIS) | Coding HuskyStable Diffusion 文字生成图片的教程已经很多了。这篇文章是讲解如何用 Kohya Trainer 在 Google Colab 上训练一个 LyCORIS 模型。在读之前希望你已经至少玩过 Stable Diffusion。https://ericfu.me/stable-diffusion-fin…

阿里云通义千问开源两款语音基座模型分别是SenseVoice和CosyVoice

阿里巴巴近期发布了开源语音大模型项目FunAudioLLM&#xff0c;该项目包含了两个核心模型&#xff1a;SenseVoice和CosyVoice。可以精准多语言识别并且进行语音克隆。 SenseVoice&#xff1a;精准多语言识别与情感辨识 SenseVoice主要致力于高精度多语言语音识别、情感辨识和…