Android自定义View-折线趋势图

先看效果图
这里写图片描述

1,首先是自定义一些属性,可自己再添加,挺基础的,上代码

 <declare-styleable name="BrokenLineTrendView">
        <attr name="trend_view_color_dotted_line" format="color"/>
        <attr name="trend_view_color_text" format="color"/>
        <attr name="trend_view_text_size" format="dimension"/>
    </declare-styleable>

2,构造数据模型类,这里主要有个两个类,BrokenLineTrendData和BrokenLineDimension
BrokenLineTrendData.class

 public List<String> mXLineDataList;//Xz轴的数据
    public List<BrokenLineDimension> mDimensionList;//数据
    public List<Double> mYLineDataList;//y轴的数据
    public int mSelectColor;//X轴选中的的颜色

BrokenLineDimension .class

public class BrokenLineDimension {
    public int mBrokenLineColor;//线的颜色
    public int mBrokenPointColor;//点的颜色
    public int mBrokenPointOutColor;//选择的点的外部颜色
    public int mBrokenPointIntColor;//选择的点的内部颜色
    public List<Double> mDatasList; //数据源
    public String remark;//备注
}

3,初始化一些变量,

/**
     * 控件的宽
     */
    private float mWith;
    /**
     * 控件的高
     */
    private float mHeight;
    /**
     * 折线的颜色
     */
    private int mBrokenLineColor;
    /**
     * 虚线的颜色
     */
    private int mDottedLineColor  = getResources().getColor(R.color.color_dotted_line);
    /**
     * 文字的颜色
     */
    private int mTexColor = getResources().getColor(R.color.color_text);
    /**
     * 折线的粗细
     */
    private float mBrokenLineWith = DensityUtil.dip2px(getContext(),1.5f);
    /**
     * 字体大小
     */
    private int mTextSize = DensityUtil.dip2px(getContext(), 12);

    /**
     * 最大的Y轴点
     */
    private Double mMaxYPoint;
    /**
     * 最小的Y轴点
     */
    private Double mMinYPoint;
    /**
     * Y轴的虚线paint
     */
    private Paint mDottedPaint;
    /**
     * 文字的paint
     */
    private Paint mTextPaint;
    /**
     * 折线的paint
     */
    private Paint mBrokenLinePaint;
    /**
     * 折线点的paint
     */
    private Paint mBrokenPointPaint;
    private Paint mScorePaint;
    /**
     * 顶部的remark Paint
     */
    private Paint mRemarkPaint;

    /**
     * 数据源
     */
    private BrokenLineTrendData mBrokenLineTrendData;
    /**
     * X轴坐标轴的数据
     */
    private List<String> mXLineDataList = new ArrayList<>();
    /**
     * Y轴坐标点的数据
     */
    private List<BrokenLineDimension> mDataList = new ArrayList<>();
    /**
     * Y轴的坐标轴的数据
     */
    private List<Double> mYLineDataList = new ArrayList<>();

    /**
     * 折线点的坐标list
     */
    private List<List<Point>> mScorePointsList;

    /**
     * 选中中的index
     */
    private int mSelectIndex = 0;
    /**
     * padding 值
     */
    private int mLeftPadding = DensityUtil.dip2px(getContext(), 15);
    private int mRightPadding = DensityUtil.dip2px(getContext(), 15);
    private int mTopPadding = DensityUtil.dip2px(getContext(), 15);
    private int mBottomPadding = DensityUtil.dip2px(getContext(), 15);
    private int mTextBottomPadding = DensityUtil.dip2px(getContext(), 8);

    /**
     * Y轴区域部分的高度
     */
    private float mYHeight;
    /**
     * Y轴区域部分的高度
     */
    private OnItemClick mOnItemClick;
    /**
     * 记录Y轴的虚线的纵坐标的List
     */
    private List<Float> dottedLineList = new ArrayList<>();

4,在构筑方法中初始化

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

    public BrokenLineTrendView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public BrokenLineTrendView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initConfig(context, attrs);
    }

    private void initConfig(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.BrokenLineTrendView);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
   
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.BrokenLineTrendView_trend_view_color_dotted_line:
                    mDottedLineColor = array.getColor(attr, mBrokenLineColor);
                    break;
                case R.styleable.BrokenLineTrendView_trend_view_color_text:
                    mTexColor = array.getColor(attr, mTexColor);
                    break;
                case R.styleable.BrokenLineTrendView_trend_view_text_size:
                    mTextSize = array.getDimensionPixelSize(attr, mTextSize);
                    break;
            }
        }
        array.recycle();
        initPaint();
        initData();
    }

    private void initPaint() {
        mDottedPaint = new Paint();
        mDottedPaint.setAntiAlias(true);
        mDottedPaint.setStyle(Paint.Style.STROKE);
        mDottedPaint.setColor(mDottedLineColor);
        mDottedPaint.setStrokeWidth(mBrokenLineWith);
        //mDottedPaint.setStrokeCap(Paint.Cap.SQUARE);


        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mTexColor);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(mTextSize);


        mBrokenLinePaint = new Paint();
        mBrokenLinePaint.setAntiAlias(true);
        mBrokenLinePaint.setStyle(Paint.Style.STROKE);
        mBrokenLinePaint.setColor(Color.GREEN);
        mBrokenLinePaint.setStrokeWidth(mBrokenLineWith);
        mBrokenLinePaint.setStrokeCap(Paint.Cap.ROUND);

        mBrokenPointPaint = new Paint();
        mBrokenPointPaint.setAntiAlias(true);
        mBrokenPointPaint.setStyle(Paint.Style.STROKE);
        mBrokenPointPaint.setColor(Color.GREEN);
        mBrokenPointPaint.setStrokeWidth(mBrokenLineWith);
        mBrokenPointPaint.setStrokeCap(Paint.Cap.ROUND);


        mScorePaint = new Paint();
        mScorePaint = new Paint();
        mScorePaint.setColor(Color.WHITE);
        mScorePaint.setTextSize(DensityUtil.dip2px(getContext(), 12));
        mScorePaint.setAntiAlias(true);

        mRemarkPaint = new Paint();
        mRemarkPaint.setAntiAlias(true);
        mRemarkPaint.setStyle(Paint.Style.FILL);
    }

    private void initData() {
        mBrokenLineTrendData = new BrokenLineTrendData();
        mDataList = mBrokenLineTrendData.mDimensionList;
        mXLineDataList = mBrokenLineTrendData.mXLineDataList;
        mYLineDataList = mBrokenLineTrendData.mYLineDataList;
    }

5,绘制X轴的数据

  /**
     * 绘制X轴的text
     * @param canvas canvas
     */
    private void drawXLineText(Canvas canvas) {
        int count = mXLineDataList.size();//拿到X轴数据,进行循环,
        if (count > 0) {
            canvas.save();
            float newWidth = mWith - (mWith * 0.1f) * 2;
            float startX;
            for (int i = 0; i < count; i++) {
  //在循环中计算相应的坐标点
                mTextPaint.setColor(mTexColor);
                String text = mXLineDataList.get(i);
                startX = ((newWidth * i) / (count - 1) + mWith * 0.1f);
                Rect rectText = new Rect();
                mTextPaint.getTextBounds(text, 0, text.length(), rectText);
                float textSize = rectText.width();//文字的宽
                float textHeight = rectText.height();//文字的高
                if (mSelectIndex == i) {
  //绘制选中后的text和框框
                    mTextPaint.setStyle(Paint.Style.STROKE);
                    int XDataSelectColor = mBrokenLineTrendData.mSelectColor;
                    mTextPaint.setColor(XDataSelectColor);
                    RectF rectF = new RectF();
                    rectF.left = startX - DensityUtil.dip2px(getContext(), 8);
                    rectF.right = startX + textSize + DensityUtil.dip2px(getContext(), 8);
                    rectF.bottom = mHeight - mBottomPadding;
                    rectF.top = rectF.bottom - mTextBottomPadding - textHeight - DensityUtil.dip2px(getContext(), 4);
                    canvas.drawRoundRect(rectF, 10, 10, mTextPaint);
                    mYHeight = rectF.top;//得到Y轴区域的高度,拿这个高度去计算Y轴需要多高
                }
                canvas.drawText(text, startX, mHeight - mBottomPadding - mTextBottomPadding, mTextPaint);

            }
            canvas.restore();
        }
    }

这样我们就绘制好了X轴的,如图
这里写图片描述

5,绘制顶部

 /**
     * 绘制顶部的remark
     * @param canvas canvas
     */
    private void drawRemark(Canvas canvas) {
        canvas.save();
        mRemarkPaint.setStrokeWidth(DensityUtil.dip2px(getContext(),2));
        int size = mDataList.size();
        int paddingRight = DensityUtil.dip2px(getContext(), 40);//右边Padding值
        int totalPadding = paddingRight * (size - 1);//总的Padding值
        int oneWidth = DensityUtil.dip2px(getContext(), 90);//一个itme的长度
        int width = oneWidth * size + totalPadding;//一个item的总长度
        int x = (int) (mWith - width) / 2;//确定开始是坐标位置
        int y = mTopPadding;
        for (int i = 0; i < size; i++) {
            BrokenLineDimension dimension = mDataList.get(i);
            mRemarkPaint.setColor(dimension.mBrokenLineColor);
            int startX = x + (i * oneWidth) + paddingRight;//开始的x点
            int endX = startX + oneWidth - paddingRight;
            canvas.drawLine(startX, y, endX, y, mRemarkPaint);//绘制线条
            float textWidth = mTextPaint.measureText(dimension.remark);
            int offset = (int) ((oneWidth - textWidth - paddingRight) / 2);
            canvas.drawText(dimension.remark, startX + offset , y * 2, mTextPaint);//绘制文字
        }
        canvas.restore();
    }

效果如下:

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值