VoicePlayingIcon.java 5.92 KB
Newer Older
万齐军 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
package com.yidianling.common.view.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import com.yidianling.common.R;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


21
public class VoicePlayingIcon extends View implements Runnable {
万齐军 committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

    //画笔
    private Paint paint;

    //跳动指针的集合
    private List<Pointer> pointers;

    //跳动指针的数量
    private int pointerNum;

    //逻辑坐标 原点
    private float basePointX;
    private float basePointY;

    //指针间的间隙  默认5dp
    private float pointerPadding;

    //每个指针的宽度 默认3dp
    private float pointerWidth;

    //指针的颜色
    private int pointerColor = Color.RED;

    //控制开始/停止
    private boolean isPlaying = false;

    //指针波动速率
    private int pointerSpeed;

    private Random random;

    private RectF rectF;
    private float radius;

    public VoicePlayingIcon(Context context) {
        super(context);
        init();
    }

    public VoicePlayingIcon(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //取出自定义属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.VoicePlayingIcon);
        pointerColor = ta.getColor(R.styleable.VoicePlayingIcon_pointer_color, Color.WHITE);
        pointerNum = ta.getInt(R.styleable.VoicePlayingIcon_pointer_num, 3);//指针的数量,默认为4
        pointerWidth = dp2px(getContext(),
万齐军 committed
68
                ta.getFloat(R.styleable.VoicePlayingIcon_pointer_width, 1.5f));//指针的宽度,默认5dp
万齐军 committed
69 70 71 72 73 74 75 76 77 78
        pointerSpeed = ta.getInt(R.styleable.VoicePlayingIcon_pointer_speed, 40);
        ta.recycle();
        init();
    }

    public VoicePlayingIcon(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.VoicePlayingIcon);
        pointerColor = ta.getColor(R.styleable.VoicePlayingIcon_pointer_color, Color.RED);
        pointerNum = ta.getInt(R.styleable.VoicePlayingIcon_pointer_num, 3);
万齐军 committed
79
        pointerWidth = dp2px(getContext(), ta.getFloat(R.styleable.VoicePlayingIcon_pointer_width, 1.5f));
万齐军 committed
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
        pointerSpeed = ta.getInt(R.styleable.VoicePlayingIcon_pointer_speed, 40);
        ta.recycle();
        init();
    }

    /**
     * 初始化画笔与指针的集合
     */
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(pointerColor);
        radius = pointerWidth / 2;
        pointers = new ArrayList<>();
        for (int i = 0; i < pointerNum; i++) {
            pointers.add(new Pointer());
        }
        random = new Random();
        rectF = new RectF();
    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //获取逻辑原点的,也就是画布左下角的坐标。这里减去了paddingBottom的距离
        basePointY = getHeight() - getPaddingBottom();
        for (int i = 0; i < pointerNum; i++) {
            //创建指针对象,利用0~1的随机数 乘以 可绘制区域的高度。作为每个指针的初始高度。
            Pointer pointer = pointers.get(i);
            if (pointer != null) {
                pointer.height = (float) (0.1 * (random.nextInt(10) + 1) * (getHeight() - getPaddingBottom() - getPaddingTop()));
            }
        }
        //计算每个指针之间的间隔  总宽度 - 左右两边的padding - 所有指针占去的宽度  然后再除以间隔的数量
        pointerPadding = (getWidth() - getPaddingLeft() - getPaddingRight() - pointerWidth * pointerNum) / (pointerNum - 1);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //将x坐标移动到逻辑原点,也就是左下角
        basePointX = 0f + getPaddingLeft();
        //循环绘制每一个指针。
        for (int i = 0; i < pointers.size(); i++) {
            //绘制指针,也就是绘制矩形
            rectF.set(basePointX,
                    basePointY - pointers.get(i).height,
                    basePointX + pointerWidth,
                    basePointY);
            canvas.drawRoundRect(rectF, radius, radius, paint);
            basePointX += (pointerPadding + pointerWidth);
        }
    }

    /**
     * 开始播放
     */
    public void start() {
        if (!isPlaying) {
141 142
            removeCallbacks(this);
            postOnAnimation(this);
万齐军 committed
143 144 145 146 147 148 149 150 151
            isPlaying = true;//控制子线程中的循环
        }
    }

    /**
     * 停止子线程,并刷新画布
     */
    public void stop() {
        isPlaying = false;
152
        removeCallbacks(this);
万齐军 committed
153 154 155 156 157 158
        invalidate();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
159 160
        removeCallbacks(this);
        isPlaying = false;
万齐军 committed
161
    }
162
    private float i = 0F;
万齐军 committed
163

164 165 166 167 168
    @Override
    public void run() {
        for (int j = 0; j < pointers.size(); j++) { //循环改变每个指针高度
            float rate = (float) Math.abs(Math.sin(i + j));//利用正弦有规律的获取0~1的数。
            pointers.get(j).height = (basePointY - getPaddingTop()) * rate; //rate 乘以 可绘制高度,来改变每个指针的高度
万齐军 committed
169
        }
170 171 172
        invalidate();
        i += 0.1;
        postOnAnimationDelayed(this, pointerSpeed);
万齐军 committed
173 174 175 176 177 178 179 180 181 182 183 184 185 186
    }

    /**
     * 指针对象
     */
    private static class Pointer {
        private float height;
    }

    static int dp2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }
}