老套路献上图:

第一张是通过播放歌曲拿到歌曲播放的数据进行动态展示的

第二张是通过定时器随机生成的数据动态展示的

先说下这个自定义view也不难很简单,就是绘制矩形,唯一的难点在于计算矩形的坐标

说下矩形的计算思路:

第一步先判断是不是第一个矩形,如果是第一个,那么第一个矩形的左边距就等于=0或者你需要设置的左边距即可,右边距就等于=左边距+矩形的宽度+2个矩形的间距

      rect = new Rect();if (newData.size() == 0) {rect.left = 35;} else {rect.left = newData.get(newData.size() - 1).right + rectSpace;
}rect.right = rect.left + rectWidth + rectSpace;

第二步判断非0个矩形,那么左边距就等于上一一个的右边距+2个矩形之间的间距代码如上

高度好说动态设置就好,top直接设置动态,bottom直接是固定的值

         rect.top = 720 - datum * 3;rect.bottom = 720;

拿到矩形数据后添加到集合中然后开始绘制矩形即可

 private void drawRectContent(List<Rect> newData) {Canvas canvas = surfaceHolder.lockCanvas();if (null != canvas) {for (int i = 0; i < newData.size(); i++) {if (i == 0) {//每次绘制第一个的时候需要清空下画布canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);}canvas.drawRect(newData.get(i), paint);}surfaceHolder.unlockCanvasAndPost(canvas);}}

看下SurfaceView完整代码吧

package cn.yhsh.surfaceviewmodel.view;import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Shader;
import android.os.CountDownTimer;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;import cn.yhsh.surfaceviewmodel.R;/*** @author xiayiye5* @date 2022/7/11 10:03*/
public class ColumnarSurfaceView extends SurfaceView implements SurfaceHolder.Callback {//每一个能量柱的宽度private static int rectWidth = 30;//每一个能量柱之间的间距private int rectSpace = 2;//能量块高度private int blockHeight = 5;//能量块下将速度private int blockSpeed = 3;//能量块与能量柱之间的距离private int distance = 2;private final Paint paint = new Paint();private final List<Rect> newData = new ArrayList<>();private boolean isLoop;private SurfaceHolder surfaceHolder;byte[] bytes = new byte[20];Random random = new Random();private ExecutorService executorService;private final CountDownTimer countDownTimer = new CountDownTimer(60 * 1000, 200) {@Overridepublic void onTick(long millisUntilFinished) {startDrawable(executorService);}@Overridepublic void onFinish() {countDownTimer.start();}};public ColumnarSurfaceView(Context context) {this(context, null);}public ColumnarSurfaceView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public ColumnarSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}public ColumnarSurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColumnarSurfaceView);rectWidth = (int) typedArray.getDimension(R.styleable.ColumnarSurfaceView_rect_width, 30);rectSpace = (int) typedArray.getDimension(R.styleable.ColumnarSurfaceView_rect_space, 2);typedArray.recycle();initView();}private void initView() {surfaceHolder = getHolder();surfaceHolder.addCallback(this);setZOrderOnTop(true);surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {Log.e("打印生命周期", "surfaceCreated");executorService = Executors.newSingleThreadExecutor();startDrawable(executorService);countDownTimer.start();}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {Log.e("打印生命周期", "surfaceChanged");}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {Log.e("打印生命周期", "surfaceDestroyed:");isLoop = false;countDownTimer.cancel();if (null != executorService) {executorService.shutdown();Log.d("打印线程", "线程状态:" + executorService.isShutdown());}}private void startDrawable(ExecutorService executorService) {executorService.execute(() -> {Log.d("打印线程", Thread.currentThread().getName());for (int i = 0; i < bytes.length; i++) {bytes[i] = (byte) random.nextInt(128);}setWaveData(bytes);drawRectContent(newData);});}public void setWaveData(byte[] data) {isLoop = true;Rect rect;for (int i = 0; i < data.length; i++) {if (newData.size() == 0) {for (byte datum : data) {rect = new Rect();if (newData.size() == 0) {rect.left = 35;} else {rect.left = newData.get(newData.size() - 1).right + rectSpace;}rect.top = 720 - datum * 3;rect.right = rect.left + rectWidth + rectSpace;rect.bottom = 720;Log.e("打印矩形数据", rect.left + "=" + rect.top + "==" + rect.right + "==" + rect.bottom);newData.add(rect);}} else {rect = newData.get(i);rect.top = 720 - data[i] * 3;}}}private void drawRectContent(List<Rect> newData) {Canvas canvas = surfaceHolder.lockCanvas();if (null != canvas) {for (int i = 0; i < newData.size(); i++) {if (i == 0) {canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);}canvas.drawRect(newData.get(i), paint);}surfaceHolder.unlockCanvasAndPost(canvas);}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);Log.e("打印生命周期", "onSizeChanged");
//        paint.setShader(new LinearGradient(0f, 0f, getWidth(), getHeight(),
//                new int[]{0xffff0000, 0xff00ff00, 0xff0000ff},
//                new float[]{0, 0.5f, 1f}, Shader.TileMode.CLAMP));}public void setPaintColor(@ColorInt int color) {if (null != paint) {paint.setColor(color);}}public void setShaderColor(@ColorInt int... colors) {if (null != paint) {int[] intColors = new int[colors.length];System.arraycopy(colors, 0, intColors, 0, colors.length);this.post(() -> {Log.e("打印宽高1", getWidth() + "==" + getHeight());Log.e("打印宽高2", getMeasuredWidth() + "==" + getMeasuredHeight());paint.setShader(new LinearGradient(0f, 0f, getWidth(), getHeight(), intColors,new float[]{0, 0.5f, 1f}, Shader.TileMode.CLAMP));});}}
}

自定义属性文件attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="ColumnarSurfaceView"><attr name="rect_width" format="dimension" /><attr name="rect_space" format="dimension" /></declare-styleable>
</resources>

xml布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><cn.yhsh.surfaceviewmodel.view.ColumnarSurfaceViewandroid:id="@+id/surface_view"android:layout_width="match_parent"android:layout_height="match_parent"app:rect_space="1dp"app:rect_width="15dp" />
</LinearLayout>

上面代码是针对图2的,如果需要看图一的完整代码:我提供一份都在一个项目中

源码查看记得自己登录下gitee账号

GitHub地址下载

Android开发之自定义SurfaceView绘制动效音波图 | 动效音阶图 | Android自定义View相关推荐

  1. android开发关掉发现更新的官方版本,XUpdate:轻量级、高可用性的 Android 版本更新框架...

    一个轻量级.高可用性的Android版本更新框架 关于我 特点 支持post和get两种版本检查方式,支持自定义网络请求. 支持设置只在wifi下进行版本更新. 支持静默下载.自动版本更新. 提供界面 ...

  2. Android开发北漂 8 年,飘飘飘 飘够了。,android移动开发基础案例教程

    我推荐的学习思路: 学会记录未知 平时注意记录那些没听过,不了解的点,有链接的要记下链接,然后根据自己的理解分类,分出优先级,难易度,这样你时刻都能对自己有个清晰的认识,我们手头不会的有多少,会了对少 ...

  3. Android开发之六:SurfaceView、ANativeWindow原生绘制

    SurfaceView ​ Activity的View hierachy的树形结构,最顶层的DecorView,也就是根结点视图,在SurfaceFlinger中有对应的Layer. ​ 对于具有Su ...

  4. Android 开发使用OpenGL ES绘制三棱锥并进行纹理贴图

    效果图: 直接上代码 MainActivity.java的代码 package com.zzu.shiyan3;import androidx.appcompat.app.AppCompatActiv ...

  5. Android开发界面显示慢-过度绘制优化

    本文原文链接:http://www.cnblogs.com/liuling/p/2015-10-08-2.html 作者名:残剑 如果一个布局十分复杂,那么就需要来排查是否出现了过度绘制,如果出现了, ...

  6. Android开发--圆角按钮和绘制直线的实现

    我们通常会觉得系统给出的按钮不够美观,这时,我们可以自己定义一个按钮,已达到自己的需求,在这里实现一个圆角按钮. 需要在drawable文件夹下新建一个xml文件,并以shape为根标签,利用如下的s ...

  7. android开发比例图表,Android开发中如何使用绘制图表

    [IT168技术]在日常的统计中,经常要用图表来给用户恰当的数据体验,比如用饼状图,柱型图等.在传统的web中,有比较多的开源的这方面的解决方案.本文将简单介绍如何在Android中,如何使用开源工具 ...

  8. Android开发实战,借机智云提供SDK从头开始造一个的Android开源框架工程,实现无须登录即可轻松控制您的设备 !

    一.前言. 此开源框架 XHOpenSouresGizAndroid设计在于个人以后的面试作品要用到,希望大家多多支持,开源于 GitHub , 欢迎收藏star一下! 此工程全部由我小徐一个人完成, ...

  9. Android开发11年,分享一下我眼中程序员的三六九等,android基础考试题及答案

    深入理解Android绘制原理,理解VSYNC和surface机制和窗口原理. 深入理解Android动画原理,包括补间动画和属性动画. 对于Android安全机制有基本认知,理解Android安全原 ...

最新文章

  1. HIN2Vec:异质信息网络中的表示学习 | PaperDaily #18
  2. zoj 3209 Dancing links/hust 1017
  3. DevOps时代,企业数字化转型需要强大的工具链
  4. oracle sql 语句如何插入全年日期?
  5. Servlet之请求转发和响应重定向
  6. asp.net C#绘制太极图
  7. Javascript第三章循环最后一种方法for..in与for区别第二课
  8. XX项目技术架构模板
  9. 收银系统服务器ip设置,如何修改打印机IP地址?
  10. java毕业设计老师评语_java教学评价管理系统毕业设计答辩PPT.ppt
  11. 阿里云MVP精选2018年终盘点:大咖专访+最佳实践,丰富干货等你来!...
  12. LeetCode-外观数列【小名带你解读LeetCode第38题-外观数列 的题目!最清晰的题解】
  13. 关于聊天室文字聊天(ListView 显示数据变化后滚动到最底部 )
  14. 编辑SRT字幕,添加在视频中播放
  15. 微信小程序导航栏怎么写
  16. C# 之 Math取整数
  17. 2016 GitHub章鱼猫观察报告之开源统计
  18. 虚机安装Linux网络配置的一些笔记(隔离,桥接,NAT)
  19. 上课笔记--商法(上)
  20. win10哪里看计算机配置,Win10如何查看电脑配置信息?

热门文章

  1. 什么是Vanilla JavaScript?
  2. Leetcode算法Java全解答--41. 缺失的第一个正数
  3. vps mysql卸载_MySQL数据库卸载的完整步骤
  4. Layui分页乱码,时间控件乱码
  5. 蓝桥杯scratch集训操作题:绘制蜘蛛网
  6. 关于卷积神经网络的书籍,卷积神经网络基础知识
  7. 她一生三任伴侣,三次流产,却被誉为坠落凡间的天使,优雅到老
  8. 深入理解Lustre文件系统-第2篇 Portal RPC
  9. 论文阅读《Calculation Method for Visual Navigation Integrity Monitoring》
  10. ByVal 和ByRef区别