直播软件开发,Android自定义简单的音频波谱view
直播软件开发,Android自定义简单的音频波谱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>} |
以上就是直播软件开发,Android自定义简单的音频波谱view, 更多内容欢迎关注之后的文章
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现