Android心率热图实现

4次阅读

共计 6280 个字符,预计需要花费 16 分钟才能阅读完成。

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>
正文完
 0