仿same音频采样UI效果

本文介绍了一种自定义录音波形UI的实现方法,包括通过继承View绘制刻度尺和采样波形图,以及如何通过手势控制画布移动和使用动画实现流畅的视觉效果。

源码地址

先看一下效果图(这个效果图是以前的,后面更新了代码,画面更加流程,完全没有卡顿):

一开始我看了这个效果图,是一脸的懵逼,完全没有思路。按照sdk提供的控件肯定是做不出来这种效果图,只能自己画,通过继承View,绘制UI。

先说一下整体思路

  • 首先这个效果图,需要拆分成两部分,一部分是上面表示录音时间的刻度尺,还有一部分是下面的录音的采样波形图
  • 整个画面的移动,这部分也是最难的一部分。我通过录音时间,和采样频率,计算出刻度尺上滑动的距离,然后通过scrollTo方法移动整个画布,来达到效果的
  • 绘制的过程中,只能绘制当前屏幕的内容,绘制整个刻度尺范围内的采样图的肯定会卡顿

整体的思路基本是这样子。下面讲解一下具体的实现过程。

  • 先定义一个BaseAudioRecord继承自View,该类主要控制滑动,初始化自定义参数等
  • 创建一个AudioRecord 类继承BaseAudioRecord, 该类主要负责画面绘制工作

AudioRecord绘制

1.先讲解一下AudioRecord绘制的第一步,绘制上面体现录音时间的刻度尺的绘制。重写onDraw方法,通过canvas来绘制。

代码如下:

 private void drawScale(Canvas canvas) {
        int firstPoint = (getScrollX() - mDrawOffset) / scaleIntervalLength;
        int lastPoint = (getScrollX() + canvas.getWidth() + mDrawOffset) / (scaleIntervalLength);
        for (int i = firstPoint; i < lastPoint; i++) {
            float locationX = i * scaleIntervalLength;
            if (i % intervalCount == 0) {
                canvas.drawLine(locationX, ruleHorizontalLineHeight - bigScaleStrokeLength, locationX, ruleHorizontalLineHeight, bigScalePaint);
                if (showRuleText) {
                    int index = i / intervalCount;
                    canvas.drawText(formatTime(index), locationX + bigScaleStrokeWidth + 5, ruleHorizontalLineHeight - bigScaleStrokeLength + ruleTextSize / 1.5f, ruleTextPaint);
                }
            } else {
                canvas.drawLine(locationX, ruleHorizontalLineHeight - smallScaleStrokeLength, locationX, ruleHorizontalLineHeight, smallScalePaint);
            }
        }
        //画轮廓线
        canvas.drawLine(getScrollX(), ruleHorizontalLineHeight, getScrollX() + canvas.getWidth(), ruleHorizontalLineHeight, ruleHorizontalLinePaint);
    }
复制代码

上面代码说明如下:

  • getScrollX() 表示画布移动的距离,往右侧移动是正数,往左侧移动是负数,值表示画布在屏幕内移动的平素点的个数
  • firstPoint 表示绘制的第一个点,减去一个 mDrawOffset 表示往左侧屏幕多绘制了一个缓冲区域
  • lastPoint 表示绘制的最后一个点,加上一个 mDrawOffset表示往右侧屏幕多绘制了一个缓冲区域
  • scaleIntervalLength 表示刻度间隔
  • intervalCount 表示两个大刻度之间小刻度的间隔数

2.绘制中间的采样波形图

代码如下:

private void drawLine(Canvas canvas) {
        int middleLineY = canvas.getHeight() / 2;
        canvas.drawLine(getScrollX(), middleLineY, getScrollX() + canvas.getWidth(), middleLineY, middleHorizontalLinePaint);

        //从数据源中找出需要绘制的矩形
        List<SampleLineModel> drawRectList = getDrawSampleLineList(canvas);
        if (drawRectList == null || drawRectList.size() == 0) {
            return;
        }
        //绘制采样点
        for (SampleLineModel sampleLineModel : drawRectList) {
            canvas.drawLine(sampleLineModel.startX, sampleLineModel.startY, sampleLineModel.stopX, sampleLineModel.stopY, linePaint);
            int invertedStartY = canvas.getHeight() / 2;
            float invertedStopY = invertedStartY + sampleLineModel.stopY - sampleLineModel.startY;
            canvas.drawLine(sampleLineModel.startX, invertedStartY, sampleLineModel.stopX, invertedStopY, lineInvertedPaint);
        }
    }
复制代码
  • 矩形的绘制,是用drawLine来表示的,矩形宽用线宽表示即可。从中心的水平横线上下各绘制了同等长度的线,用来表示采样的波形图
  • 同样采样波形的绘制,也只绘制屏幕内的采样波形

AudioRecord两块区域绘制的核心代码基本就是这样子了

BaseAudioRecord 控制移动

1.重写onTouchEvent()方法,根据手势移动,来移动画布

核心代码如下:

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        float currentX = event.getX();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastX = currentX;
                break;
            case MotionEvent.ACTION_MOVE:
                float moveX = mLastX - currentX;
                mLastX = currentX;
                scrollBy((int) (moveX), 0);
                break;
            }
            
         return true;
    }
复制代码
  • return true; 将onTouchEvent触摸事件消费掉,一遍能够执行到ACTION_MOVE
  • 通过scrollBy来移动画布,距离通过,手指滑动的距离获取float moveX = mLastX - currentX;

2.通过ObjectAnimator.ofFloat()方法来移动画布,开启动画

核心代码如下: 动画开始:

            float startX = getScrollX();
            //小于半屏的时候,要重新计算偏移量,因为有个左滑的动作
            float endX = maxLength - getMeasuredWidth() / 2;
            float dx = Math.abs(endX - startX);
            final double duration = 1000 * dx / (recordSamplingFrequency * (lineWidth + rectGap));
            animator = ObjectAnimator.ofFloat(this, "translateX", startX, endX);
            animator.setInterpolator(new LinearInterpolator());
            animator.setDuration((long) Math.floor(duration));
            animator.removeAllListeners();
            animator.start();

复制代码

移动画布:

        public void setTranslateX(float translateX) {
        this.translateX = translateX;
        scrollTo((int) translateX, 0);
        if (isStartRecordTranslateCanvas) {
            translateVerticalLineX = getScrollX() + getMeasuredWidth() / 2 + rectGap;
        }
        onTick(getScrollX() + getMeasuredWidth() / 2);

    }
复制代码

根据画布移动的距离,算出时间,再根据定义好的采样频率,回调采样函数,生成波形图:

 private void onTick(float translateX) {
   if (isRecording) {
        long duration = (long) (translateX * recordTimeInMillis / maxLength);
        if (duration > getSampleCount() * recordDelayMillis) {
            makeSampleLine(recordCallBack.getSamplePercent());
        }
       
   }
 }
复制代码

这个自定义的录音采集声音波形的UI基本上就完成了,有兴趣的小伙伴可以去查看源码,有什么不对的地方,欢迎指正交流。

源码中,有一个播放器的类AudioRecordMp3.java,采用了AudioRecord录制的音频,使用了Lame将AudioRecord录制的pcm格式的音频实时转码成MP3格式,支持暂停录制,删除上一段录音的功能。

源码里面还有一个播放声音的波形图,原理和上面类似,效果如下:

转载于:https://juejin.im/post/5a3720c851882506e50cbb19

### 复制任意用户界面的设计与实现 在 MATLAB 中复制任意用户界面 (UI) 的设计与实现,主要涉及以下几个方面: #### 1. **分析目标 UI 的结构** 首先需要仔细观察并记录目标 UI 的布局、组件及其行为特性。这包括但不限于窗口大小、位置、颜色方案、字体样式、按钮标签以及其他视觉元素。可以利用 `inspect` 函数来查看现有 GUI 的对象层次结构和属性设置[^1]。 ```matlab % 打开当前运行的图形界面对话框进行检查 inspect(gcf); ``` 通过此工具能够深入了解各个控件的具体参数配置,从而为后续仿制提供依据。 --- #### 2. **创建基础框架** 使用 GUIDE 或者纯脚本编程构建初始框架。如果偏好可视化编辑器,则可通过启动 GUIDE 创建一个新的 FIG 文件作为起点;而倾向于代码控制的话,则可以直接定义 Figure 和 Axes 属性[^2]。 ```matlab figure('Name', 'Replicated Interface', ... 'NumberTitle', 'off',... 'Units','pixels',... 'Position',[200,200,800,600]); uicontrol('Style','pushbutton',... 'String','Click Me',... 'Callback',{@myButtonCallback},... 'Position',[300,450,100,50]); function myButtonCallback(hObject,eventdata) disp('Button was clicked!'); end ``` 上述例子展示了如何手动添加一个简单的 Push Button 及其响应逻辑到新建立的应用程序当中。 --- #### 3. **精确匹配控件外观** 基于第一步收集的信息调整每一个单独部件的位置(`Position`)、尺寸(`FontSize`, `Extent`)、前景背景色调(`BackgroundColor`, `ForegroundColor`)等细节直至完全一致。此外还需注意某些特殊效果可能依赖额外资源文件(如图标图片),需一并导入项目目录下供加载使用。 --- #### 4. **模拟交互流程** 关键在于重现原有系统的动态表现部分—即当用户触发特定动作时所产生的连锁反应链路。这就要求开发者不仅要熟悉基本 Callbacks 编写技巧,还要善于运用 Handle Graphics 提供的各种高级功能去捕捉实时数据变化状况,并据此更新显示内容或者执行其他操作[^2]。 例如下面这段演示了怎样监听滑动条数值改变事件并将结果显示在一个静态文本框里: ```matlab slider = uicontrol('Style','slider',... 'Min',0,'Max',10,... 'Value',5,... 'Callback',{@updateText}); textDisplay = uicontrol('Style','text',... 'String','',... 'Position',[150,270,50,20]); function updateText(source,callbackData) currentVal = get(source,'Value'); set(textDisplay,'String',num2str(currentVal)); end ``` --- #### 5. **测试和完善** 最后一步是对整个副本进行全面的功能性和兼容性验证,确保没有任何遗漏之处影响用户体验质量。必要时候还可以借助 Profiler 工具找出性能瓶颈所在加以优化改进。 --- 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值