import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
*
* 画EQ(均衡器)频响曲线
*/
public class GainView extends View {
private static final String TAG = "GainView";
private int[] mGains;
private Paint mPain;
public GainView(Context context) {
this(context, null);
}
public GainView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GainView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
//8个点,对应8个频段
mGains = new int[]{0, 0, 0, 0, 0, 0, 0, 0};
mPain = new Paint();
}
//更新增益曲线
public void updateGain(int index, int gain) {
mGains[index] = gain;
postInvalidate();
}
//横纵坐标
private int[] getXIndex() {
int[] xs = new int[8];
int realWidth = getWidth() - getPaddingLeft() - getPaddingRight();
int step = realWidth / (xs.length - 1);
for (int i = 0; i < xs.length; i++) {
xs[i] = step * i;
}
return xs;
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int[] gains = new int[mGains.length];
for (int i = 0; i < mGains.length; i++) {
gains[i] = -mGains[i];
}
int[] xs = getXIndex();
int[] ys = new int[xs.length];
int half_height = (getHeight() - getPaddingTop() - getPaddingBottom()) / 2;
for (int i = 0; i < ys.length; i++) {
ys[i] = half_height * gains[i] / 12 + half_height;
}
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (int x : xs) {
if (x > max)
max = x;
if (x < min)
min = x;
}
int sc = canvas.save();
int left = getPaddingLeft();
int top = getPaddingTop();
canvas.translate(left, top);
Paint p = mPain;
p.setAntiAlias(true);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(2);
p.setColor(Color.parseColor("#959595"));
//画中间线
canvas.drawLine(0, half_height, getWidth() - getPaddingRight() - getPaddingLeft(), half_height, p);
PointF[] points = new PointF[xs.length];
for (int i = 0; i < points.length; i++) {
points[i] = new PointF();
points[i].x = xs[i];
points[i].y = ys[i];
}
List
p.setStrokeWidth(3);
p.setColor(Color.BLACK);
Path path = new Path();
createCurve2(pointFList, points, path);
//createCurve(points, path);
canvas.drawPath(path, p);
// p.setStyle(Paint.Style.FILL);
// p.setStrokeWidth(1);
// p.setColor(Color.LTGRAY);
// p.setTextSize(8 * getContext().getResources().getDisplayMetrics().scaledDensity);
// p.setFakeBoldText(true);
// float density = getContext().getResources().getDisplayMetrics().density;
// canvas.drawText("+12db", 5, 10 * density, p);
// canvas.drawText("-12db", 5, getHeight() - getPaddingBottom() - 3 * density, p);
canvas.restoreToCount(sc);
}
void createCurve(PointF[] originPoint, Path path) {
int originCount = originPoint.length;
float scale = 0.6f;
PointF[] midPoints = new PointF[originCount];
// 生成中点
for (int i = 0; i < originCount; i++) {
int next = (i + 1) % originCount;
midPoints[i] = new PointF();
if (i == originCount - 1) {
//midPoints[i].x = getWidth() - getPaddingEnd();
midPoints[i].x = originPoint[i].x;
midPoints[i].y = (originPoint[i].y + (getHeight() - getPaddingTop() - getPaddingBottom()) / 2.0f) / 2.0f;
} else {
midPoints[i].x = (originPoint[i].x + originPoint[next].x) / 2.0f;
midPoints[i].y = (originPoint[i].y + originPoint[next].y) / 2.0f;
}
// Log.d(TAG, " originPoint[" + i + "]= [" + originPoint[i].x + ", " + originPoint[i].y + "]");
// Log.d(TAG, " midPoints[" + i + "]= [" + midPoints[i].x + ", " + midPoints[i].y + "]");
}
// 平移中点
PointF[] extraPoints = new PointF[2 * originCount];
for (int i = 0; i < originCount; i++) {
int back = (i + originCount - 1) % originCount;
if (i == 0)
back = 0;
PointF midInMid = new PointF();
midInMid.x = (midPoints[i].x + midPoints[back].x) / 2.0f;
midInMid.y = (midPoints[i].y + midPoints[back].y) / 2.0f;
float offsetX = originPoint[i].x - midInMid.x;
float offsetY = originPoint[i].y - midInMid.y;
int extraIndex = 2 * i;
extraPoints[extraIndex] = new PointF();
extraPoints[extraIndex].x = midPoints[back].x + offsetX;
extraPoints[extraIndex].y = midPoints[back].y + offsetY;
// 朝 originPoint[i]方向收缩
float addx = (extraPoints[extraIndex].x - originPoint[i].x) * scale;
float addy = (extraPoints[extraIndex].y - originPoint[i].y) * scale;
extraPoints[extraIndex].x = originPoint[i].x + addx;
extraPoints[extraIndex].y = originPoint[i].y + addy;
//Log.d(TAG, " extraPoints[" + extraIndex + "]= [" + extraPoints[extraIndex].x + ", " + extraPoints[extraIndex].y + "]");
int extraNext = (extraIndex + 1) % (2 * originCount);
extraPoints[extraNext] = new PointF();
extraPoints[extraNext].x = midPoints[i].x + offsetX;
extraPoints[extraNext].y = midPoints[i].y + offsetY;
// 朝 originPoint[i]方向收缩
addx = (extraPoints[extraNext].x - originPoint[i].x) * scale;
addy = (extraPoints[extraNext].y - originPoint[i].y) * scale;
extraPoints[extraNext].x = originPoint[i].x + addx;
extraPoints[extraNext].y = originPoint[i].y + addy;
//Log.d(TAG, " extraPoints[" + extraNext + "]= [" + extraPoints[extraNext].x + ", " + extraPoints[extraNext].y + "]");
}
//控制点,即所在点位置
PointF[] controlPoint = new PointF[4];
// 生成4控制点,产生贝塞尔曲线
boolean first = true;
for (int i = 0; i < originCount; i++) {
controlPoint[0] = originPoint[i];
int extraIndex = 2 * i;
controlPoint[1] = extraPoints[extraIndex + 1];
if (controlPoint[1].x < originPoint[i].x)
controlPoint[1] = originPoint[i];
//Log.d(TAG, " controlPoint[0]= " + controlPoint[0] + ", controlPoint[1]= " + controlPoint[1]);
int extraNext = (extraIndex + 2) % (2 * originCount);
controlPoint[2] = extraPoints[extraNext];
if (controlPoint[2].x < originPoint[i].x)
controlPoint[2] = originPoint[i];
int next = (i + 1) % originCount;
controlPoint[3] = originPoint[next];
if (controlPoint[3].x < originPoint[i].x)
controlPoint[3] = originPoint[i];
//Log.d(TAG, " controlPoint[2]= " + controlPoint[2] + ", controlPoint[3]= " + controlPoint[3]);
float u = 1;
while (u >= 0) {
float px = bezier3funcX(u, controlPoint);
float py = bezier3funcY(u, controlPoint);
// u的步长决定曲线的疏密
u -= 0.005;
//Log.d(TAG, "u= " + u + ", px= " + px + ", py= " + px );
if (first) {
//画笔移动
path.moveTo(px, py);
//画笔标记清除
first = false;
} else
//画笔划线
path.lineTo(px, py);
}
}
}
/**
* 计算控制点
* 斜率 y = kx + b => k = (y - b)/x
*
* @param points 原始点
* @return 控制点
*/
private List
List
float rate = 0.3f;
//从第一个控制点开始计算
float cx1 = points[0].x + (points[1].x - points[0].x) * rate;
float cy1 = points[0].y;
pointFList.add(new PointF(cx1, cy1));
for (int i = 1; i < points.length - 1; i++) {
//下一个点
float k = (points[i + 1].y - points[i - 1].y) / (points[i + 1].x - points[i - 1].x);
float b = points[i].y - k * points[i].x;
//左边控制点
float cxLeft = points[i].x - (points[i].x - points[i - 1].x) * rate;
float cyLeft = k * cxLeft + b;
pointFList.add(new PointF(cxLeft, cyLeft));
//右边控制点
float cxRight = points[i].x + (points[i + 1].x - points[i].x) * rate;
float cyRight = k * cxRight + b;
pointFList.add(new PointF(cxRight, cyRight));
}
//最后一个点
float cxLast = points[points.length - 1].x - (points[points.length - 1].x - points[points.length - 2].x) * rate;
float cyLast = points[points.length - 1].y;
pointFList.add(new PointF(cxLast, cyLast));
return pointFList;
}
void createCurve2(List
for (int i = 0; i < originPoint.length - 1; i++) {
path.moveTo(originPoint[i].x, originPoint[i].y);
path.cubicTo(
pointFList.get(i * 2).x, pointFList.get(i * 2).y,
pointFList.get(i * 2 + 1).x, pointFList.get(i * 2 + 1).y,
originPoint[i + 1].x, originPoint[i + 1].y
);
}
}
/**
* 三次贝塞尔曲线
* B(t) = P0 * (1-t)^3 + 3 * P1 * t(1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 *t^3
*
* @param uu 属于[0, 1]
* @param controlP 控制点
* @return X 坐标值
*/
float bezier3funcX(float uu, PointF[] controlP) {
float part0 = controlP[0].x * uu * uu * uu;
float part1 = 3 * controlP[1].x * uu * uu * (1 - uu);
float part2 = 3 * controlP[2].x * uu * (1 - uu) * (1 - uu);
float part3 = controlP[3].x * (1 - uu) * (1 - uu) * (1 - uu);
return part0 + part1 + part2 + part3;
}
float bezier3funcY(float uu, PointF[] controlP) {
float part0 = controlP[0].y * uu * uu * uu;
float part1 = 3 * controlP[1].y * uu * uu * (1 - uu);
float part2 = 3 * controlP[2].y * uu * (1 - uu) * (1 - uu);
float part3 = controlP[3].y * (1 - uu) * (1 - uu) * (1 - uu);
return part0 + part1 + part2 + part3;
}
}