自定义 View ———— 折线图(重写 onDraw方法)

实现一个如下效果的折线图
line_result
这样的效果实现的方式很容易想到的就是重写 onDraw 方法,给定一组 float 值,分别画点、线,那么下面有颜色的阴影部分是用什么画呢,就用到了 Path, 一个点一个点相连成一个封闭的图形,便可画出这个图形。如果先画点,再画阴影部分,是会把点遮住一部分的,所以实现的时候先画完了阴影部分,再画线和点。不过这样子在 onDraw 方法里就有两个 for 循环,如果有好的办法欢迎提出。
下面直接贴出 View 的整个代码,代码不多

/**
 * @author hexiaosa
 * @date 2018/8/15
 */

public class CustomLineChart extends View {

    private int width; // 宽
    private int height; // 高
    private int pointNum; // 折点个数
    private int lineColor; // 折线颜色
    private int shadowColor; // 阴影颜色
    private float[] percents; // 百分比,可以理解为任务完成百分比
    private int itemWidth;
    private Path path; // 避免在 onDraw 方法内创建多个对象,所以在这里声明一个成员变量

    public CustomLineChart(Context context) {
        this(context, null);
    }

    public CustomLineChart(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomLineChart(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        // 线颜色,阴影颜色,折点个数
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomLineChart);
        lineColor = typedArray.getColor(R.styleable.CustomLineChart_lineColor, Color.BLACK);
        shadowColor = typedArray.getColor(R.styleable.CustomLineChart_shadowColor, Color.BLACK);
        pointNum = typedArray.getInteger(R.styleable.CustomLineChart_pointNum, 0);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (pointNum == 0) {
            return;
        }
        if (percents == null || percents.length != pointNum) {
            return;
        }
        if (width == 0) {
            width = getMeasuredWidth();
        }
        if (height == 0) {
            height = getMeasuredHeight();
        }
        if (path == null) {
            path = new Path();
        }
        itemWidth = width/(pointNum-1);
        Paint paint = new Paint();
        paint.setStrokeWidth(5);
        // 阴影部分使用 Path 连接成一个封闭图形
        path.reset();
        path.moveTo(0, height);
        path.lineTo(0, height*(1-percents[0]));
        for (int i = 1; i < percents.length; i++) {
            // 连接阴影
            path.lineTo(itemWidth*i, height*(1-percents[i]));
        }
        path.lineTo(width, height);
        path.lineTo(0, height);
        paint.setColor(shadowColor);
        // 画阴影
        canvas.drawPath(path, paint);
        path.close();

        paint.setColor(lineColor);
        canvas.drawCircle(0, height*(1-percents[0]), 10, paint);
        for (int i = 1; i < percents.length; i++) {
            // 画线
            canvas.drawLine(itemWidth*(i-1), height*(1-percents[i-1]), itemWidth*i, height*(1-percents[i]), paint);
            // 画点
            canvas.drawCircle(itemWidth*i, height*(1-percents[i]), 10, paint);
        }

        canvas.save();
    }

    public void setPercents(float[] percents) {
        this.percents = percents;
        invalidate();
    }

    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
    }

    public void setShadowColor(int shadowColor) {
        this.shadowColor = shadowColor;
    }

    public void setPointNum(int pointNum) {
        this.pointNum = pointNum;
    }
}


// attr.xml:
    <declare-styleable name="CustomLineChart">
        <attr name="lineColor" format="color"/>
        <attr name="shadowColor" format="color"/>
        <attr name="pointNum" format="integer"/>
    </declare-styleable>

使用示例:

CustomLineChart clc = (CustomLineChart) findViewById(R.id.clc);
// 必须设置,还有 pointNum 在布局或代码设置至少有一个地方设置,且 pointNum 需等于 percents.length
clc.setPercents(new float[]{0.5f, 0.3f, 0.4f, 0.7f, 0.1f, 0.2f, 0.6f, 0.8f, 0.9f, 1f});

猜你喜欢

转载自blog.csdn.net/hexiaosa91/article/details/81714157