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>