您的位置:首页 > 其它

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

2017-04-17 11:00 309 查看
上篇声音识别动画(上)—–矩形声音识别动画中分析了矩形声音识别动画,

这一篇来分析线形(曲线)声音识别动画

再看一遍我们的效果图(曲线):



1.分析:

这个图看上去是曲线,这个曲线非常类似正余弦图像,安卓中画出正余弦

可以使用画贝塞尔曲线的方法.

简单介绍下贝塞尔曲线:

简单的说就是通过三个点,不断地取三点连线的中点,最后近似出一条光滑的曲线。

对贝塞尔曲线不太了解的可以先去看看 Android 绘图基础:Path(绘制三角形、

贝塞尔曲线、正余弦)这篇文章.

我们还需要清楚既然每个光滑的曲线至少需要三个点来确定,那么这段光滑的曲线就是1/2T(半个周期)

的正余弦曲线,那么要想画出一个周期的正余弦曲线就至少需要五个点(前半个周期的最后一个点是最后

一个周期的起点).而我们图中的曲线不止一个周期,所要确定的就要根据实际情况去确定,我们可以水平

方向每隔一段距离取一个点.

正弦曲线:

y=Asin(ωx+φ)+b




正余弦曲线和余弦曲线之间相差的就是初始值φ,正弦曲线向左右或者右平移变化的也就是φ.

每隔5个像素取一个点,根据屏幕的长度我们就知道总共有几个点,根据点的个数进行for循环,

求出每的点的坐标.

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

周期(T): ω=2π/T 我取的是400px 那么ω=2π/400

初始偏移量:φ 我取的初始偏移量为π

振幅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);
}
}


点击这里下载源码.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息