import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import com.careeach.sport.R;
import java.util.List;
/**
* 心率热图
*/
public class HeartRateView extends View {
private List<Integer> values;
private int[] guidesValues;
private float height;
private float width;
private Paint linkLinePoint;
private Paint shadePoint;
private Paint xTextPoint;
private Paint xLintPoint;
private Paint yTextPoint;
private float itemHeight;
//ATTR
private int attrXTextColor;
private float attrXTextSize;
private int attrXLineColor;
private float attrXLineWidth;
private int attrYTextColor;
private float attrYTextSize;
private float attrLineWidth;
private int attrLinkLineColor;
private int attrShadeColor;
private float attrGuidesTextMarginLeft;
private float attrGuidesTextMarginRight;
private float attrPaddingRight;
private int maxValue; // 最大值
private float itemWidth;
private float contentEndY;
private float contentStartX;
public HeartRateView(Context context) {super(context);
}
public HeartRateView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);
init(context, attrs);
}
public HeartRateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
init(context, attrs);
}
public HeartRateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HeartRateView);
attrLineWidth = typedArray.getDimension(R.styleable.HeartRateView_hr_lineWidth, dip2px(0.5f));
attrLinkLineColor = typedArray.getColor(R.styleable.HeartRateView_hr_linkLineColor, Color.RED);
attrShadeColor = typedArray.getColor(R.styleable.HeartRateView_hr_shadeColor, Color.RED);
maxValue = typedArray.getInteger(R.styleable.HeartRateView_hr_maxValue, 180);
attrXTextColor = typedArray.getColor(R.styleable.HeartRateView_hr_xTextColor, Color.GRAY);
attrXTextSize = typedArray.getDimension(R.styleable.HeartRateView_hr_xTextSize, 20);
attrXLineColor = typedArray.getColor(R.styleable.HeartRateView_hr_xLineColor, Color.GRAY);
attrYTextSize = typedArray.getDimension(R.styleable.HeartRateView_hr_yTextSize, 20);
attrYTextColor = typedArray.getColor(R.styleable.HeartRateView_hr_yTextColor, Color.GRAY);
attrXLineWidth = typedArray.getDimension(R.styleable.HeartRateView_hr_xLineWidth, dip2px(0.5f));
attrGuidesTextMarginLeft = typedArray.getDimension(R.styleable.HeartRateView_hr_guidesTextMarginLeft, 20);
attrGuidesTextMarginRight = typedArray.getDimension(R.styleable.HeartRateView_hr_guidesTextMarginRight, 20);
attrPaddingRight = typedArray.getDimension(R.styleable.HeartRateView_hr_paddingRight, 20);
typedArray.recycle();
linkLinePoint = new Paint();
linkLinePoint.setColor(attrLinkLineColor);
linkLinePoint.setStrokeWidth(attrLineWidth);
linkLinePoint.setStyle(Paint.Style.STROKE);
linkLinePoint.setAntiAlias(true);
shadePoint = new Paint();
shadePoint.setStyle(Paint.Style.FILL);
xTextPoint = new Paint();
xTextPoint.setColor(attrXTextColor);
xTextPoint.setTextSize(attrXTextSize);
xTextPoint.setAntiAlias(true);
yTextPoint = new Paint();
yTextPoint.setColor(attrYTextColor);
yTextPoint.setTextSize(attrYTextSize);
yTextPoint.setAntiAlias(true);
xLintPoint = new Paint();
xLintPoint.setColor(attrXLineColor);
xLintPoint.setStrokeWidth(attrXLineWidth);
xLintPoint.setAntiAlias(true);
xLintPoint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getMeasuredWidth();
height = getMeasuredHeight();
contentEndY = height - measureTextHeight(xTextPoint);
itemHeight = contentEndY / maxValue;
contentStartX = attrGuidesTextMarginLeft + yTextPoint.measureText(String.valueOf(maxValue)) + attrGuidesTextMarginRight;
}
public static float measureTextHeight(Paint paint) {
float height = 0f;
if (null == paint) {return height;}
Paint.FontMetrics fontMetrics = paint.getFontMetrics();
height = fontMetrics.descent - fontMetrics.ascent;
return height;
}
@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);
if (guidesValues != null && guidesValues.length > 0) {for (int guidesValue : guidesValues) {
float x = attrGuidesTextMarginLeft;
float y = contentEndY - guidesValue * itemHeight;
canvas.drawText(String.valueOf(guidesValue), x, y, yTextPoint);
}
}
if (values != null && values.size() > 1) {Path path = new Path();
Path pathLine = new Path();
path.moveTo(contentStartX, contentEndY - values.get(0) * itemHeight);
pathLine.moveTo(contentStartX, contentEndY - values.get(0) * itemHeight);
for (int i = 1; i < values.size(); i++) {
float x = contentStartX + i * itemWidth;
float y = contentEndY - values.get(i) * itemHeight;
// 为了好看,计算误差在最初一个点打消
if (i == values.size() - 1) {x = width - attrPaddingRight;}
path.lineTo(x, y);
pathLine.lineTo(x, y);
}
path.lineTo(width - attrPaddingRight, contentEndY);
path.lineTo(contentStartX, contentEndY);
path.lineTo(contentStartX, contentEndY - values.get(0) * itemHeight);
path.close();
Shader mShader = new LinearGradient(0, contentEndY, 0, 0, Color.WHITE, attrShadeColor, Shader.TileMode.MIRROR);
shadePoint.setShader(mShader);
canvas.drawPath(path, shadePoint);
canvas.drawPath(pathLine, linkLinePoint);
}
// X 轴
canvas.drawLine(0, contentEndY, width, contentEndY, xLintPoint);
}
public void setValues(List<Integer> values) {
this.values = values;
itemWidth = (width - attrPaddingRight - contentStartX) / values.size();
postInvalidate();}
/**
* 设置参考值
*
* @param values
*/
public void setGuidesValues(int[] values) {this.guidesValues = values;}
private float dip2px(float dpValue) {final float scale = getContext().getResources().getDisplayMetrics().density;
return (dpValue * scale + 0.5f);
}
}
attrs.xml 增加属性
<declare-styleable name="HeartRateView">
<attr name="hr_lineWidth" format="dimension" />
<attr name="hr_linkLineColor" format="color" />
<attr name="hr_maxValue" format="integer" />
<attr name="hr_xTextSize" format="dimension" />
<attr name="hr_xTextColor" format="color" />
<attr name="hr_xLineColor" format="color" />
<attr name="hr_xLineWidth" format="dimension" />
<attr name="hr_yTextSize" format="dimension" />
<attr name="hr_yTextColor" format="color" />
<attr name="hr_shadeColor" format="color" />
<attr name="hr_guidesTextMarginLeft" format="dimension" />
<attr name="hr_guidesTextMarginRight" format="dimension" />
<attr name="hr_paddingRight" format="dimension" />
</declare-styleable>