IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android开发之自定义SurfaceView绘制动效音波图 | 动效音阶图 | Android自定义View -> 正文阅读

[移动开发]Android开发之自定义SurfaceView绘制动效音波图 | 动效音阶图 | Android自定义View

老套路献上图:

?

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

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

先说下这个自定义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) {
        @Override
        public void onTick(long millisUntilFinished) {
            startDrawable(executorService);
        }

        @Override
        public 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);
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        Log.e("打印生命周期", "surfaceCreated");
        executorService = Executors.newSingleThreadExecutor();
        startDrawable(executorService);
        countDownTimer.start();
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
        Log.e("打印生命周期", "surfaceChanged");
    }

    @Override
    public 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);
        }
    }

    @Override
    protected 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.ColumnarSurfaceView
        android: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账号

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:00:48  更:2022-07-20 19:02:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:05:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码