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個點(diǎn),對應(yīng)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();
? ? }
? ? //橫縱坐標(biāo)
? ? 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<PointF> pointFList = getControlPoints(points);
? ? ? ? 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];
? ? ? ? // 生成中點(diǎn)
? ? ? ? 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 + "]");
? ? ? ? }
? ? ? ? // 平移中點(diǎn)
? ? ? ? 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 + "]");
? ? ? ? }
? ? ? ? //控制點(diǎn),即所在點(diǎn)位置
? ? ? ? PointF[] controlPoint = new PointF[4];
? ? ? ? // 生成4控制點(diǎn)蹋宦,產(chǎn)生貝塞爾曲線
? ? ? ? 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);
? ? ? ? ? ? ? ? ? ? //畫筆標(biāo)記清除
? ? ? ? ? ? ? ? ? ? first = false;
? ? ? ? ? ? ? ? } else
? ? ? ? ? ? ? ? ? ? //畫筆劃線
? ? ? ? ? ? ? ? ? ? path.lineTo(px, py);
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? /**
? ? ?* 計(jì)算控制點(diǎn)
? ? ?* 斜率 y = kx + b => k = (y - b)/x
? ? ?*
? ? ?* @param points 原始點(diǎn)
? ? ?* @return 控制點(diǎn)
? ? ?*/
? ? private List<PointF> getControlPoints(PointF[] points) {
? ? ? ? List<PointF> pointFList = new ArrayList<>();
? ? ? ? float rate = 0.3f;
? ? ? ? //從第一個控制點(diǎn)開始計(jì)算
? ? ? ? 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++) {
? ? ? ? ? ? //下一個點(diǎn)
? ? ? ? ? ? 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;
? ? ? ? ? ? //左邊控制點(diǎn)
? ? ? ? ? ? float cxLeft = points[i].x - (points[i].x - points[i - 1].x) * rate;
? ? ? ? ? ? float cyLeft = k * cxLeft + b;
? ? ? ? ? ? pointFList.add(new PointF(cxLeft, cyLeft));
? ? ? ? ? ? //右邊控制點(diǎn)
? ? ? ? ? ? float cxRight = points[i].x + (points[i + 1].x - points[i].x) * rate;
? ? ? ? ? ? float cyRight = k * cxRight + b;
? ? ? ? ? ? pointFList.add(new PointF(cxRight, cyRight));
? ? ? ? }
? ? ? ? //最后一個點(diǎn)
? ? ? ? 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<PointF> pointFList, PointF[] originPoint, Path path) {
? ? ? ? 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 控制點(diǎn)
? ? ?* @return X 坐標(biāo)值
? ? ?*/
? ? 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;
? ? }
}