声音识别动画(下)-----线形(曲线)声音识别动画

上篇声音识别动画(上)—–矩形声音识别动画中分析了矩形声音识别动画,
这一篇来分析线形(曲线)声音识别动画
再看一遍我们的效果图(曲线):
这里写图片描述

1.分析:

这个图看上去是曲线,这个曲线非常类似正余弦图像,安卓中画出正余弦
可以使用画贝塞尔曲线的方法.

简单介绍下贝塞尔曲线:

简单的说就是通过三个点,不断地取三点连线的中点,最后近似出一条光滑的曲线。
对贝塞尔曲线不太了解的可以先去看看 Android 绘图基础:Path(绘制三角形、
贝塞尔曲线、正余弦)
这篇文章.

我们还需要清楚既然每个光滑的曲线至少需要三个点来确定,那么这段光滑的曲线就是1/2T(半个周期)
的正余弦曲线,那么要想画出一个周期的正余弦曲线就至少需要五个点(前半个周期的最后一个点是最后
一个周期的起点).而我们图中的曲线不止一个周期,所要确定的就要根据实际情况去确定,我们可以水平
方向每隔一段距离取一个点.

正弦曲线:

y=Asin(ωx+φ)+b

这里写图片描述
正余弦曲线和余弦曲线之间相差的就是初始值φ,正弦曲线向左右或者右平移变化的也就是φ.
每隔5个像素取一个点,根据屏幕的长度我们就知道总共有几个点,根据点的个数进行for循环,
求出每的点的坐标.

这个自定义控件我们需要这么几个值:

  1. 周期(T): ω=2π/T 我取的是400px 那么ω=2π/400
  2. 初始偏移量:φ 我取的初始偏移量为π
  3. 振幅A b明显等于1/2A 振幅我取的是35dp(注意转化成像素)

    因此y=1/2*35+35*sin(2π/400*n*5+π)

    好了,这样就可以画出一条完整的曲线,而且从最左边到最右边,下变看看
    怎么让这条曲线在怎么动起来:
    刚才有个初始偏移量:φ 如果我每隔100毫秒让这个φ增加1/4π,就会发现这条曲线动起来了

 postDelayed(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                i = 0;
                path1.reset();
                startAngle1 += (Math.PI / 4);
                path2.reset();
                startAngle2 += (Math.PI / 4);
                path3.reset();
                startAngle3 += (Math.PI / 4);
                postInvalidate();
            }
        }, 150);

2.下边贴出所有代码

自定义属性

    <declare-styleable name="VoiceLine">
        <attr name="amplitude_big" format="dimension|reference" />
        <attr name="amplitude_small" format="dimension|reference" />
        <attr name="lineColor" format="color|reference" />
        <attr name="backColor" format="color|reference" />
        <attr name="frequency" format="float" />
    </declare-styleable>

布局文件

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:VoiceRect="http://schemas.android.com/apk/res-auto"
    xmlns:VoiceLine="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical"
    tools:context="com.gelitenight.waveview.sample.TestVoiceLineActivity">

    <com.yzhx.testvoiceani.VoiceLine
        android:layout_width="match_parent"
        android:layout_height="80dp"
        VoiceLine:amplitude_big="35dp"
        VoiceLine:amplitude_small="25dp"
        VoiceLine:backColor="#ffffff"
        VoiceLine:frequency="400"
        VoiceLine:lineColor="#ff6600"
        />

</LinearLayout>

自定义控件类

public class VoiceLine extends View {

    private int height;
    private int width;

    private Path path1;//第一条线
    private Path path2;//第二条线
    private Path path3;//第三条线

    private Paint paint;

    private float waveHeight_big;//大真毒
    private float waveHeight_small;//小高度

    private int lineColor;//线的颜色


    private float amplitude_big;//大振幅
    private float amplitude_small;//小振幅

    private float frequency;//频率
    private float frequency_small;//频率

    /**
     * 三条线的偏移
     */
    private float startAngle1 = (float) (Math.PI / 4);
    private float startAngle2 = (float) (Math.PI / 4 * 3);
    private float startAngle3 = (float) (Math.PI);

    private int i = 0;

    public VoiceLine(Context context) {
        this(context, null);
    }

    public VoiceLine(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context, attrs);
    }

    public VoiceLine(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    private void initView(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs,
                R.styleable.VoiceLine);

        amplitude_big = ta.getDimension(R.styleable.VoiceLine_amplitude_big, 100);
        amplitude_small = ta.getDimension(R.styleable.VoiceLine_amplitude_small, 50);

        frequency = ta.getFloat(R.styleable.VoiceLine_frequency, 100);
        lineColor = ta.getColor(R.styleable.VoiceLine_lineColor, Color.GREEN);

        paint = new Paint();
        paint.setColor(lineColor);
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.STROKE);
        paint.setAntiAlias(true);

        path1 = new Path();
        path2 = new Path();
        path3 = new Path();

        ta.recycle();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        // TODO Auto-generated method stub
        super.onSizeChanged(w, h, oldw, oldh);
        if (height == 0) {
            height = getMeasuredHeight();
            waveHeight_big = Math.min(height, amplitude_big) - 20;
            waveHeight_small = Math.min(height, amplitude_small) - 20;
            Log.e("KFJKFJ", "高度" + waveHeight_big + "getMeasuredHeight()" + getMeasuredHeight() + "amplitude_big" + amplitude_big);
        }
        if (width == 0) {
            width = getMeasuredWidth();
        }

    }

    @Override
    protected void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
//        每隔5个像素获取一个点
        for (i = 0; i < width; i += 5) {
            float y = (float) (waveHeight_big / 2 + waveHeight_big / 2 * Math.sin(i * (2 * Math.PI / frequency) + startAngle1)) + 10;
            if (i == 0) {
                //设置path的起点
                path1.moveTo(0, y);
            } else {
                //连线
                path1.lineTo(i, y);
            }
            y = (float) (waveHeight_big / 2 + waveHeight_big / 2 * Math.sin(i * (2 * Math.PI / frequency) + startAngle2)) + 10;
            if (i == 0) {
                //设置path的起点
                path2.moveTo(0, y);
            } else {
                //连线
                path2.lineTo(i, y);
            }
            y = (float) (waveHeight_small / 2 + waveHeight_small / 2 * Math.sin(i * (2 * Math.PI / frequency) + startAngle3)) + 10;
            if (i == 0) {
                //设置path的起点
                path3.moveTo(0, y);
            } else {
                //连线
                path3.lineTo(i, y);
            }

        }
        //每隔150毫秒刷新一次界面  将所有的起点增加1/4π   让曲线动起来
        postDelayed(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                i = 0;
                path1.reset();
                startAngle1 += (Math.PI / 4);
                path2.reset();
                startAngle2 += (Math.PI / 4);
                path3.reset();
                startAngle3 += (Math.PI / 4);
                postInvalidate();
            }
        }, 150);
        canvas.drawPath(path1, paint);
        canvas.drawPath(path2, paint);
        canvas.drawPath(path3, paint);
    }
}

点击这里下载源码.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值