一对一视频直播系统,实现自定义简单的音频波谱view

一对一视频直播系统,实现自定义简单的音频波谱view的相关代码

1
package com.ysalliance.qifan.util.myview.voiceview;<br> <br>import android.content.Context;<br>import android.content.res.TypedArray;<br>import android.graphics.Canvas;<br>import android.graphics.Color;<br>import android.graphics.Paint;<br>import android.os.Handler;<br>import android.os.Message;<br>import android.util.AttributeSet;<br>import android.util.Log;<br>import android.view.View;<br>import android.widget.TextView;<br> <br>import androidx.annotation.Nullable;<br> <br>import com.didichuxing.doraemonkit.widget.tableview.utils.DensityUtils;<br>import com.ysalliance.qifan.R;<br> <br>import java.util.ArrayList;<br>import java.util.List;<br>import java.util.Random;<br> <br> <br>public class VoisePlayingIcon extends View {<br> <br>    //画笔<br>    private Paint paint;<br> <br>    //跳动指针的集合<br>    private List<Pointer> pointers;<br> <br>    //跳动指针的数量<br>    private int pointerNum;<br> <br>    //逻辑坐标 原点<br>    private float basePointX;<br>    private float basePointY;<br> <br>    //指针间的间隙  默认5dp<br>    private float pointerPadding;<br> <br>    //每个指针的宽度 默认3dp<br>    private float pointerWidth;<br> <br>    //指针的颜色<br>    private int pointerColor = Color.RED;<br> <br>    //控制开始/停止<br>    private boolean isPlaying = false;<br> <br>    //子线程<br>    private Thread myThread;<br> <br>    //指针波动速率<br>    private int pointerSpeed;<br> <br> <br>    public VoisePlayingIcon(Context context) {<br>        super(context);<br>        init();<br>    }<br> <br>    public VoisePlayingIcon(Context context, @Nullable AttributeSet attrs) {<br>        super(context, attrs);<br>        //取出自定义属性<br>        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.voisePlayingIconAttr);<br>        pointerColor = ta.getColor(R.styleable.voisePlayingIconAttr_pointer_color, Color.RED);<br>        pointerNum = ta.getInt(R.styleable.voisePlayingIconAttr_pointer_num, 4);//指针的数量,默认为4<br>        pointerWidth = DensityUtils.dp2px(getContext(),<br>                ta.getFloat(R.styleable.voisePlayingIconAttr_pointer_width, 5f));//指针的宽度,默认5dp<br>        pointerSpeed = ta.getInt(R.styleable.voisePlayingIconAttr_pointer_speed, 40);<br>        init();<br>    }<br> <br>    public VoisePlayingIcon(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {<br>        super(context, attrs, defStyleAttr);<br>        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.voisePlayingIconAttr);<br>        pointerColor = ta.getColor(R.styleable.voisePlayingIconAttr_pointer_color, Color.RED);<br>        pointerNum = ta.getInt(R.styleable.voisePlayingIconAttr_pointer_num, 4);<br>        pointerWidth = DensityUtils.dp2px(getContext(), ta.getFloat(R.styleable.voisePlayingIconAttr_pointer_width, 5f));<br>        pointerSpeed = ta.getInt(R.styleable.voisePlayingIconAttr_pointer_speed, 40);<br>        init();<br>    }<br> <br>    /**<br>     * 初始化画笔与指针的集合<br>     */<br>    private void init() {<br>        paint = new Paint();<br>        paint.setAntiAlias(true);<br>        paint.setColor(pointerColor);<br>        pointers = new ArrayList<>();<br>    }<br> <br> <br>    /**<br>     * 在onLayout中做一些,宽高方面的初始化<br>     *<br>     * @param changed<br>     * @param left<br>     * @param top<br>     * @param right<br>     * @param bottom<br>     */<br>    @Override<br>    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {<br>        super.onLayout(changed, left, top, right, bottom);<br>        //获取逻辑原点的,也就是画布左下角的坐标。这里减去了paddingBottom的距离<br>        basePointY = getHeight() - getPaddingBottom();<br>        Random random = new Random();<br>        if (pointers != null)<br>            pointers.clear();<br>        for (int i = 0; i < pointerNum; i++) {<br>            //创建指针对象,利用0~1的随机数 乘以 可绘制区域的高度。作为每个指针的初始高度。<br>            pointers.add(new Pointer((float) (0.1 * (random.nextInt(10) + 1) * (getHeight() - getPaddingBottom() - getPaddingTop()))));<br>        }<br>        //计算每个指针之间的间隔  总宽度 - 左右两边的padding - 所有指针占去的宽度  然后再除以间隔的数量<br>        pointerPadding = (getWidth() - getPaddingLeft() - getPaddingRight() - pointerWidth * pointerNum) / (pointerNum - 1);<br>    }<br> <br> <br>    /**<br>     * 开始绘画<br>     *<br>     * @param canvas<br>     */<br>    @Override<br>    protected void onDraw(Canvas canvas) {<br>        super.onDraw(canvas);<br>        //将x坐标移动到逻辑原点,也就是左下角<br>        basePointX = 0f + getPaddingLeft();<br>        //循环绘制每一个指针。<br>        for (int i = 0; i < pointers.size(); i++) {<br>            //绘制指针,也就是绘制矩形<br>            canvas.drawRect(basePointX,<br>                    basePointY - pointers.get(i).getHeight(),<br>                    basePointX + pointerWidth,<br>                    basePointY,<br>                    paint);<br>            basePointX += (pointerPadding + pointerWidth);<br>        }<br>    }<br> <br>    /**<br>     * 开始播放<br>     */<br>    public void start() {<br>        if (!isPlaying) {<br>            if (myThread == null) {//开启子线程<br>                myThread = new Thread(new MyRunnable());<br>                myThread.start();<br>            }<br>            isPlaying = true;//控制子线程中的循环<br>        }<br>    }<br> <br>    /**<br>     * 停止子线程,并刷新画布<br>     */<br>    public void stop() {<br>        isPlaying = false;<br>        invalidate();<br>    }<br> <br>    /**<br>     * 处理子线程发出来的指令,然后刷新布局<br>     */<br>    private Handler myHandler = new Handler() {<br>        @Override<br>        public void handleMessage(Message msg) {<br>            super.handleMessage(msg);<br>            invalidate();<br>        }<br>    };<br> <br>    /**<br>     * 子线程,循环改变每个指针的高度<br>     */<br>    public class MyRunnable implements Runnable {<br> <br>        @Override<br>        public void run() {<br> <br>            for (float i = 0; i < Integer.MAX_VALUE; ) {//创建一个死循环,每循环一次i+0.1<br>                try {<br>                    for (int j = 0; j < pointers.size(); j++) { //循环改变每个指针高度<br>                        float rate = (float) Math.abs(Math.sin(i + j));//利用正弦有规律的获取0~1的数。<br>                        try {<br>                            pointers.get(j).setHeight((basePointY - getPaddingTop()) * rate); //rate 乘以 可绘制高度,来改变每个指针的高度<br>                        }catch (Exception e){<br>                            Log.e("VoisePlayingIcon", "run: pointers.get(j).setHeight出错:"+e.getMessage().toString());<br>                        }<br> <br>                    }<br>                    Thread.sleep(pointerSpeed);//休眠一下下,可自行调节<br>                    if (isPlaying) { //控制开始/暂停<br>                        myHandler.sendEmptyMessage(0);<br>                        i += 0.1;<br>                    }<br>                } catch (InterruptedException e) {<br>                    e.printStackTrace();<br>                }<br>            }<br>        }<br>    }<br> <br> <br>    /**<br>     * 指针对象<br>     */<br>    public class Pointer {<br>        private float height;<br> <br>        public Pointer(float height) {<br>            this.height = height;<br>        }<br> <br>        public float getHeight() {<br>            return height;<br>        }<br> <br>        public void setHeight(float height) {<br>            this.height = height;<br>        }<br>    }<br>}<br> 

​以上就是一对一视频直播系统,实现自定义简单的音频波谱view的相关代码, 更多内容欢迎关注之后的文章 

 

posted @   云豹科技-苏凌霄  阅读(89)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示