88.android 简单的折线图,两种效果

导读:本篇文章讲解 88.android 简单的折线图,两种效果,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

 //第一种:

88.android 简单的折线图,两种效果

 //第一步 写个ChartView类继承View:

 

public class ChartView extends View {
    //xy坐标轴颜色
    private int xylinecolor = 0xffe2e2e2;
    //xy坐标轴宽度
    private int xylinewidth = dpToPx(1);
    //xy坐标轴文字颜色
    private int xytextcolor = 0xff7e7e7e;
    //xy坐标轴文字大小
    private int xytextsize = spToPx(12);
    //折线图中折线的颜色
    private int linecolor = 0xff02bbb7;
    //x轴各个坐标点水平间距
    private int interval = dpToPx(50);
    //背景颜色
    private int bgcolor = 0xffffffff;
    //是否在ACTION_UP时,根据速度进行自滑动,没有要求,建议关闭,过于占用GPU
    private boolean isScroll = false;
    //绘制XY轴坐标对应的画笔
    private Paint xyPaint;
    //绘制XY轴的文本对应的画笔
    private Paint xyTextPaint;
    //画折线对应的画笔
    private Paint linePaint;
    private int width;
    private int height;
    //x轴的原点坐标
    private int xOri;
    //y轴的原点坐标
    private int yOri;
    //第一个点X的坐标
    private float xInit;
    //第一个点对应的最大Y坐标
    private float maxXInit;
    //第一个点对应的最小X坐标
    private float minXInit;
    //x轴坐标对应的数据
    private List<String> xValue = new ArrayList<>();
    //y轴坐标对应的数据
    private List<Integer> yValue = new ArrayList<>();
    //折线对应的数据
    private Map<String, Integer> value = new HashMap<>();
    //点击的点对应的X轴的第几个点,默认1
    private int selectIndex = 1;
    //X轴刻度文本对应的最大矩形,为了选中时,在x轴文本画的框框大小一致
    private Rect xValueRect;
    //速度检测器
    private VelocityTracker velocityTracker;

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

    public ChartView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
        initPaint();
    }

    /**
     * 初始化畫筆
     */
    private void initPaint() {
        xyPaint = new Paint();
        xyPaint.setAntiAlias(true);
        xyPaint.setStrokeWidth(xylinewidth);
        xyPaint.setStrokeCap(Paint.Cap.ROUND);
        xyPaint.setColor(xylinecolor);

        xyTextPaint = new Paint();
        xyTextPaint.setAntiAlias(true);
        xyTextPaint.setTextSize(xytextsize);
        xyTextPaint.setStrokeCap(Paint.Cap.ROUND);
        xyTextPaint.setColor(xytextcolor);
        xyTextPaint.setStyle(Paint.Style.STROKE);

        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setStrokeWidth(xylinewidth);
        linePaint.setStrokeCap(Paint.Cap.ROUND);
        linePaint.setColor(linecolor);
        linePaint.setStyle(Paint.Style.STROKE);
    }

    /**
     * 初始化
     *
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.chartView, defStyleAttr, 0);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.chartView_xylinecolor://xy坐标轴颜色
                    xylinecolor = array.getColor(attr, xylinecolor);
                    break;
                case R.styleable.chartView_xylinewidth://xy坐标轴宽度
                    xylinewidth = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xylinewidth, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.chartView_xytextcolor://xy坐标轴文字颜色
                    xytextcolor = array.getColor(attr, xytextcolor);
                    break;
                case R.styleable.chartView_xytextsize://xy坐标轴文字大小
                    xytextsize = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, xytextsize, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.chartView_linecolor://折线图中折线的颜色
                    linecolor = array.getColor(attr, linecolor);
                    break;
                case R.styleable.chartView_interval://x轴各个坐标点水平间距
                    interval = (int) array.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, interval, getResources().getDisplayMetrics()));
                    break;
                case R.styleable.chartView_bgcolor: //背景颜色
                    bgcolor = array.getColor(attr, bgcolor);
                    break;
                case R.styleable.chartView_isScroll://是否在ACTION_UP时,根据速度进行自滑动
                    isScroll = array.getBoolean(attr, isScroll);
                    break;
            }
        }
        array.recycle();

    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (changed) {
            //这里需要确定几个基本点,只有确定了xy轴原点坐标,第一个点的X坐标值及其最大最小值
            width = getWidth();
            height = getHeight();
            //Y轴文本最大宽度
            float textYWdith = getTextBounds("000", xyTextPaint).width();
            for (int i = 0; i < yValue.size(); i++) {//求取y轴文本最大的宽度
                float temp = getTextBounds(yValue.get(i) + "", xyTextPaint).width();
                if (temp > textYWdith)
                    textYWdith = temp;
            }
            int dp2 = dpToPx(2);
            int dp3 = dpToPx(3);
            xOri = (int) (dp2 + textYWdith + dp2 + xylinewidth);//dp2是y轴文本距离左边,以及距离y轴的距离
//            //X轴文本最大高度
            xValueRect = getTextBounds("000", xyTextPaint);
            float textXHeight = xValueRect.height();
            for (int i = 0; i < xValue.size(); i++) {//求取x轴文本最大的高度
                Rect rect = getTextBounds(xValue.get(i) + "", xyTextPaint);
                if (rect.height() > textXHeight)
                    textXHeight = rect.height();
                if (rect.width() > xValueRect.width())
                    xValueRect = rect;
            }
            yOri = (int) (height - dp2 - textXHeight - dp3 - xylinewidth);//dp3是x轴文本距离底边,dp2是x轴文本距离x轴的距离
            xInit = interval + xOri;
            minXInit = width - (width - xOri) * 0.1f - interval * (xValue.size() - 1);//减去0.1f是因为最后一个X周刻度距离右边的长度为X轴可见长度的10%
            maxXInit = xInit;
        }
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);
        canvas.drawColor(bgcolor);
        drawXY(canvas);
        drawBrokenLineAndPoint(canvas);
    }

    /**
     * 绘制折线和折线交点处对应的点
     *
     * @param canvas
     */
    private void drawBrokenLineAndPoint(Canvas canvas) {
        if (xValue.size() <= 0)
            return;
        //重新开一个图层
        int layerId = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
        drawBrokenLine(canvas);
        drawBrokenPoint(canvas);

        // 将折线超出x轴坐标的部分截取掉
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setColor(bgcolor);
        linePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        RectF rectF = new RectF(0, 0, xOri, height);
        canvas.drawRect(rectF, linePaint);
        linePaint.setXfermode(null);
        //保存图层
        canvas.restoreToCount(layerId);
    }

    /**
     * 绘制折线对应的点
     *
     * @param canvas
     */
    private void drawBrokenPoint(Canvas canvas) {
        float dp2 = dpToPx(2);
        float dp4 = dpToPx(4);
        float dp7 = dpToPx(7);
        //绘制节点对应的原点
        for (int i = 0; i < xValue.size(); i++) {
            float x = xInit + interval * i;
            float y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(i)) / yValue.get(yValue.size() - 1);
            //绘制选中的点
            if (i == selectIndex - 1) {
                linePaint.setStyle(Paint.Style.FILL);
                linePaint.setColor(0xffd0f3f2);
                canvas.drawCircle(x, y, dp7, linePaint);
                linePaint.setColor(0xff81dddb);
                canvas.drawCircle(x, y, dp4, linePaint);
                drawFloatTextBox(canvas, x, y - dp7, value.get(xValue.get(i)));
            }
            //绘制普通的节点
            linePaint.setStyle(Paint.Style.FILL);
            linePaint.setColor(Color.WHITE);
            canvas.drawCircle(x, y, dp2, linePaint);
            linePaint.setStyle(Paint.Style.STROKE);
            linePaint.setColor(linecolor);
            canvas.drawCircle(x, y, dp2, linePaint);

        }
    }

    /**
     * 绘制显示Y值的浮动框
     *
     * @param canvas
     * @param x
     * @param y
     * @param text
     */
    private void drawFloatTextBox(Canvas canvas, float x, float y, int text) {
        int dp6 = dpToPx(6);
        int dp18 = dpToPx(18);
        //p1
        Path path = new Path();
        path.moveTo(x, y);
        //p2
        path.lineTo(x - dp6, y - dp6);
        //p3
        path.lineTo(x - dp18, y - dp6);
        //p4
        path.lineTo(x - dp18, y - dp6 - dp18);
        //p5
        path.lineTo(x + dp18, y - dp6 - dp18);
        //p6
        path.lineTo(x + dp18, y - dp6);
        //p7
        path.lineTo(x + dp6, y - dp6);
        //p1
        path.lineTo(x, y);
        canvas.drawPath(path, linePaint);
        linePaint.setColor(Color.WHITE);
        linePaint.setTextSize(spToPx(14));
        Rect rect = getTextBounds(text + "", linePaint);
        canvas.drawText(text + "", x - rect.width() / 2, y - dp6 - (dp18 - rect.height()) / 2, linePaint);
    }

    /**
     * 绘制折线
     *
     * @param canvas
     */
    private void drawBrokenLine(Canvas canvas) {
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setColor(linecolor);
        //绘制折线
        Path path = new Path();
        float x = xInit + interval * 0;
        float y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(0)) / yValue.get(yValue.size() - 1);
        path.moveTo(x, y);
        for (int i = 1; i < xValue.size(); i++) {
            x = xInit + interval * i;
            y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(i)) / yValue.get(yValue.size() - 1);
            path.lineTo(x, y);
        }
        canvas.drawPath(path, linePaint);
    }

    /**
     * 绘制XY坐标
     *
     * @param canvas
     */
    private void drawXY(Canvas canvas) {
        int length = dpToPx(4);//刻度的长度
        //绘制Y坐标
        canvas.drawLine(xOri - xylinewidth / 2, 0, xOri - xylinewidth / 2, yOri, xyPaint);
        //绘制y轴箭头
        xyPaint.setStyle(Paint.Style.STROKE);
        Path path = new Path();
        path.moveTo(xOri - xylinewidth / 2 - dpToPx(5), dpToPx(12));
        path.lineTo(xOri - xylinewidth / 2, xylinewidth / 2);
        path.lineTo(xOri - xylinewidth / 2 + dpToPx(5), dpToPx(12));
        canvas.drawPath(path, xyPaint);
        //绘制y轴刻度
        int yLength = (int) (yOri * (1 - 0.1f) / (yValue.size() - 1));//y轴上面空出10%,计算出y轴刻度间距
        for (int i = 0; i < yValue.size(); i++) {
            //绘制Y轴刻度
            //这里可以设置成这个Y轴刻度,和屏幕一样宽的效果:
            // canvas.drawLine(xOri, yOri - yLength * i + xylinewidth / 2, xOri+width, yOri - yLength * i + xylinewidth / 2, xyPaint);
            canvas.drawLine(xOri, yOri - yLength * i + xylinewidth / 2, xOri + length, yOri - yLength * i + xylinewidth / 2, xyPaint);
            xyTextPaint.setColor(xytextcolor);
            //绘制Y轴文本
            String text = yValue.get(i) + "";
            Rect rect = getTextBounds(text, xyTextPaint);
            canvas.drawText(text, 0, text.length(), xOri - xylinewidth - dpToPx(2) - rect.width(), yOri - yLength * i + rect.height() / 2, xyTextPaint);
        }
        //绘制X轴坐标
        canvas.drawLine(xOri, yOri + xylinewidth / 2, width, yOri + xylinewidth / 2, xyPaint);
        //绘制x轴箭头
        xyPaint.setStyle(Paint.Style.STROKE);
        path = new Path();
        //整个X轴的长度
        float xLength = xInit + interval * (xValue.size() - 1) + (width - xOri) * 0.1f;
        if (xLength < width)
            xLength = width;
        path.moveTo(xLength - dpToPx(12), yOri + xylinewidth / 2 - dpToPx(5));
        path.lineTo(xLength - xylinewidth / 2, yOri + xylinewidth / 2);
        path.lineTo(xLength - dpToPx(12), yOri + xylinewidth / 2 + dpToPx(5));
        canvas.drawPath(path, xyPaint);
        //绘制x轴刻度
        for (int i = 0; i < xValue.size(); i++) {
            float x = xInit + interval * i;
            if (x >= xOri) {//只绘制从原点开始的区域
                xyTextPaint.setColor(xytextcolor);
                canvas.drawLine(x, yOri, x, yOri - length, xyPaint);
                //绘制X轴文本
                String text = xValue.get(i);
                Rect rect = getTextBounds(text, xyTextPaint);
                if (i == selectIndex - 1) {
                    xyTextPaint.setColor(linecolor);
                    canvas.drawText(text, 0, text.length(), x - rect.width() / 2, yOri + xylinewidth + dpToPx(2) + rect.height(), xyTextPaint);
                    canvas.drawRoundRect(x - xValueRect.width() / 2 - dpToPx(3), yOri + xylinewidth + dpToPx(1), x + xValueRect.width() / 2 + dpToPx(3), yOri + xylinewidth + dpToPx(2) + xValueRect.height() + dpToPx(2), dpToPx(2), dpToPx(2), xyTextPaint);
                } else {
                    canvas.drawText(text, 0, text.length(), x - rect.width() / 2, yOri + xylinewidth + dpToPx(2) + rect.height(), xyTextPaint);
                }
            }
        }
    }

    private float startX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isScrolling)
            return super.onTouchEvent(event);
        this.getParent().requestDisallowInterceptTouchEvent(true);//当该view获得点击事件,就请求父控件不拦截事件
        obtainVelocityTracker(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                if (interval * xValue.size() > width - xOri) {//当期的宽度不足以呈现全部数据
                    float dis = event.getX() - startX;
                    startX = event.getX();
                    if (xInit + dis < minXInit) {
                        xInit = minXInit;
                    } else if (xInit + dis > maxXInit) {
                        xInit = maxXInit;
                    } else {
                        xInit = xInit + dis;
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                clickAction(event);
                scrollAfterActionUp();
                this.getParent().requestDisallowInterceptTouchEvent(false);
                recycleVelocityTracker();
                break;
            case MotionEvent.ACTION_CANCEL:
                this.getParent().requestDisallowInterceptTouchEvent(false);
                recycleVelocityTracker();
                break;
        }
        return true;
    }

    //是否正在滑动
    private boolean isScrolling = false;

    /**
     * 手指抬起后的滑动处理
     */
    private void scrollAfterActionUp() {
        if (!isScroll)
            return;
        final float velocity = getVelocity();
        float scrollLength = maxXInit - minXInit;
        if (Math.abs(velocity) < 10000)//10000是一个速度临界值,如果速度达到10000,最大可以滑动(maxXInit - minXInit)
            scrollLength = (maxXInit - minXInit) * Math.abs(velocity) / 10000;
        ValueAnimator animator = ValueAnimator.ofFloat(0, scrollLength);
        animator.setDuration((long) (scrollLength / (maxXInit - minXInit) * 1000));//时间最大为1000毫秒,此处使用比例进行换算
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float value = (float) valueAnimator.getAnimatedValue();
                if (velocity < 0 && xInit > minXInit) {//向左滑动
                    if (xInit - value <= minXInit)
                        xInit = minXInit;
                    else
                        xInit = xInit - value;
                } else if (velocity > 0 && xInit < maxXInit) {//向右滑动
                    if (xInit + value >= maxXInit)
                        xInit = maxXInit;
                    else
                        xInit = xInit + value;
                }
                invalidate();
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                isScrolling = true;
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                isScrolling = false;
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                isScrolling = false;
            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });
        animator.start();

    }

    /**
     * 获取速度
     *
     * @return
     */
    private float getVelocity() {
        if (velocityTracker != null) {
            velocityTracker.computeCurrentVelocity(1000);
            return velocityTracker.getXVelocity();
        }
        return 0;
    }

    /**
     * 点击X轴坐标或者折线节点
     *
     * @param event
     */
    private void clickAction(MotionEvent event) {
        int dp8 = dpToPx(8);
        float eventX = event.getX();
        float eventY = event.getY();
        for (int i = 0; i < xValue.size(); i++) {
            //节点
            float x = xInit + interval * i;
            float y = yOri - yOri * (1 - 0.1f) * value.get(xValue.get(i)) / yValue.get(yValue.size() - 1);
            if (eventX >= x - dp8 && eventX <= x + dp8 &&
                    eventY >= y - dp8 && eventY <= y + dp8 && selectIndex != i + 1) {//每个节点周围8dp都是可点击区域
                selectIndex = i + 1;
                invalidate();
                return;
            }
            //X轴刻度
            String text = xValue.get(i);
            Rect rect = getTextBounds(text, xyTextPaint);
            x = xInit + interval * i;
            y = yOri + xylinewidth + dpToPx(2);
            if (eventX >= x - rect.width() / 2 - dp8 && eventX <= x + rect.width() + dp8 / 2 &&
                    eventY >= y - dp8 && eventY <= y + rect.height() + dp8 && selectIndex != i + 1) {
                selectIndex = i + 1;
                invalidate();
                return;
            }
        }
    }


    /**
     * 获取速度跟踪器
     *
     * @param event
     */
    private void obtainVelocityTracker(MotionEvent event) {
        if (!isScroll)
            return;
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
        velocityTracker.addMovement(event);
    }

    /**
     * 回收速度跟踪器
     */
    private void recycleVelocityTracker() {
        if (velocityTracker != null) {
            velocityTracker.recycle();
            velocityTracker = null;
        }
    }

    public int getSelectIndex() {
        return selectIndex;
    }

    public void setSelectIndex(int selectIndex) {
        this.selectIndex = selectIndex;
        invalidate();
    }

    public void setxValue(List<String> xValue) {
        this.xValue = xValue;
    }

    public void setyValue(List<Integer> yValue) {
        this.yValue = yValue;
        invalidate();
    }

    public void setValue(Map<String, Integer> value) {
        this.value = value;
        invalidate();
    }

    public void setValue(Map<String, Integer> value, List<String> xValue, List<Integer> yValue) {
        this.value = value;
        this.xValue = xValue;
        this.yValue = yValue;
        invalidate();
    }

    public List<String> getxValue() {
        return xValue;
    }

    public List<Integer> getyValue() {
        return yValue;
    }

    public Map<String, Integer> getValue() {
        return value;
    }

    /**
     * 获取丈量文本的矩形
     *
     * @param text
     * @param paint
     * @return
     */
    private Rect getTextBounds(String text, Paint paint) {
        Rect rect = new Rect();
        paint.getTextBounds(text, 0, text.length(), rect);
        return rect;
    }

    /**
     * dp转化成为px
     *
     * @param dp
     * @return
     */
    private int dpToPx(int dp) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dp * density + 0.5f * (dp >= 0 ? 1 : -1));
    }

    /**
     * sp转化为px
     *
     * @param sp
     * @return
     */
    private int spToPx(int sp) {
        float scaledDensity = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (scaledDensity * sp + 0.5f * (sp >= 0 ? 1 : -1));
    }
}

 

//第二步:在values下写个attrs.xml文件:

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- xy坐标轴颜色 -->
    <attr name="xylinecolor" format="color" />
    <!-- xy坐标轴宽度 -->
    <attr name="xylinewidth" format="dimension" />
    <!-- xy坐标轴文字颜色 -->
    <attr name="xytextcolor" format="color" />
    <!-- xy坐标轴文字大小 -->
    <attr name="xytextsize" format="dimension" />
    <!-- 折线图中折线的颜色 -->
    <attr name="linecolor" format="color" />
    <!-- x轴各个坐标点水平间距 -->
    <attr name="interval" format="dimension" />
    <!-- 背景颜色 -->
    <attr name="bgcolor" format="color" />
    <!--是否在ACTION_UP时,根据速度进行自滑动,建议关闭,过于占用GPU-->
    <attr name="isScroll" format="boolean" />
    <declare-styleable name="chartView">
        <attr name="xylinecolor" />
        <attr name="xylinewidth" />
        <attr name="xytextcolor" />
        <attr name="xytextsize" />
        <attr name="linecolor" />
        <attr name="interval" />
        <attr name="bgcolor" />
        <attr name="isScroll" />
    </declare-styleable>

</resources>

//第三步在Activity里使用:

 

public class MainActivity extends AppCompatActivity  {
    //x轴坐标对应的数据
    private List<String> xValue = new ArrayList<>();
    //y轴坐标对应的数据
    private List<Integer> yValue = new ArrayList<>();
    //折线对应的数据
    private Map<String, Integer> value = new HashMap<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取当前月份
        Time t = new Time();
        t.setToNow();
        int year = t.year;
        int month = t.month + 1;

        //获取当前月的天数
        int currentMonthDay = getCurrentMonthDay();
        Log.e("TAG2", currentMonthDay + "");

        //数据集合
        List<Integer> mList = new ArrayList<>();
        mList.add(1);
        mList.add(10);
        mList.add(10);
        mList.add(9);
        mList.add(7);
        mList.add(35);
        mList.add(28);


//        (int) (Math.random() * 181 + 60)
        for (int i = 0; i < 7; i++) {
            xValue.add(month + "." + (i + 1));
            value.put(month + "." + (i + 1), mList.get(i));
        }

        //集合最大值
        Integer max = Collections.max(mList);
        //集合最小值
        Integer min = Collections.min(mList);

        yValue.add(0);//y轴坐标最小数
        yValue.add(max / 2);//y轴坐标最大数/2
        yValue.add(max);//y轴坐标最大数

      

        ChartView chartView = (ChartView) findViewById(R.id.chartview);
        //折线图设置数据
        chartView.setValue(value, xValue, yValue);
    }

    //获取当月的 天数
    public int getCurrentMonthDay() {
        Calendar a = Calendar.getInstance();
        a.set(Calendar.DATE, 1);
        a.roll(Calendar.DATE, -1);
        int maxDate = a.get(Calendar.DATE);
        return maxDate;
    }


}

 //我的布局:

 

<com.example.hasee.a1029ceshi.ChartView
    android:id="@+id/chartview"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:layout_centerInParent="true"
    app:bgcolor="#ffffff"
    app:interval="40dp"
    app:isScroll="false"
    app:linecolor="#02bbb7"
    app:xylinecolor="#e2e2e2"
    app:xylinewidth="2dp"
    app:xytextcolor="#7e7e7e"
    app:xytextsize="15sp"
    />

 

 //第二种 带动画的折线图效果:

 88.android 简单的折线图,两种效果

 88.android 简单的折线图,两种效果

//首先第一步:写个LineChartView继承View

 

 

public class LineChartView extends View {
    private float xOrigin; //  x轴原点坐标
    private float yOrigin;  //  y轴原点坐标
    private int mMargin10;  //  10dp的间距
    private int mWidth; //  控件宽度
    private int mHeight;  //  控件高度
    private int max = 100, min = 0;  //  最大值、最小值
    private float yInterval;  //  y轴坐标间隔
    private float xInterval;  //  x轴坐标间隔

    private String startTime = "2017-03-15";
    private String endTime = "2017-03-24";
    private int textWidth;  //  日期宽度

    private List<ItemBean> mItems;//  折线数据

    private int[] shadeColors; //  渐变阴影颜色

    private int mAxesColor; //  坐标轴颜色
    private float mAxesWidth; //  坐标轴宽度
    private int mTextColor;  //  字体颜色
    private float mTextSize; //  字体大小
    private int mLineColor;  //  折线颜色
    private int mBgColor;       //  背景色

    private Paint mPaintText;     //  画文字的画笔
    private Paint mPaintAxes;   //  坐标轴画笔
    private Paint mPaintLine;   //  折线画笔
    private Path mPath;   //    折线路径
    private Paint mPaintShader; //  渐变色画笔
    private Path mPathShader;   //  渐变色路径
    private float mProgress;    //  动画进度
    private Paint mPaintXPoint;//坐标轴点画笔
    private Paint mPaintPath;//折线折点画笔

    public int[] getShadeColors() {
        return shadeColors;
    }

    public void setShadeColors(int[] shadeColors) {
        this.shadeColors = shadeColors;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public int getMin() {
        return min;
    }

    public void setMin(int min) {
        this.min = min;
    }

    public String getStartTime() {
        return startTime;
    }

    public void setStartTime(String startTime) {
        this.startTime = startTime;
    }

    public String getEndTime() {
        return endTime;
    }

    public void setEndTime(String endTime) {
        this.endTime = endTime;
    }

    public List<ItemBean> getItems() {
        return mItems;
    }

    public void setItems(List<ItemBean> items) {
        mItems = items;
        int max=0;
        int min=0;
        for (ItemBean mItem : mItems) {
            int anInt=mItem.getValue();
            if(max<anInt){
                max=anInt;
            }
            if(min>anInt){
                min=anInt;
            }
        }
        setMax(max%10==0?max:(max/10+1)*10);
        setMin(min<=10?0:(max/10-1)*10);
        setStartTime(mItems.get(mItems.size()-1).getDate());
        setEndTime(mItems.get(0).getDate());
    }

    public int getAxesColor() {
        return mAxesColor;
    }

    public void setAxesColor(int axesColor) {
        mAxesColor = axesColor;
    }

    public float getAxesWidth() {
        return mAxesWidth;
    }

    public void setAxesWidth(float axesWidth) {
        mAxesWidth = axesWidth;
    }

    public int getTextColor() {
        return mTextColor;
    }

    public void setTextColor(int textColor) {
        mTextColor = textColor;
    }

    public float getTextSize() {
        return mTextSize;
    }

    public void setTextSize(float textSize) {
        mTextSize = textSize;
    }


    public int getLineColor() {
        return mLineColor;
    }

    public void setLineColor(int lineColor) {
        mLineColor = lineColor;
    }

    public int getBgColor() {
        return mBgColor;
    }

    public void setBgColor(int bgColor) {
        mBgColor = bgColor;
    }

    public LineChartView(Context context) {
        super(context);
        init();
    }

    public LineChartView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        init();
    }

    public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.LineChartView);
        mAxesColor = typedArray.getColor(R.styleable.LineChartView_axesColor, Color.parseColor("#CCCCCC"));
        mAxesWidth = typedArray.getDimension(R.styleable.LineChartView_axesWidth, 2);
        mTextColor = typedArray.getColor(R.styleable.LineChartView_textColor, Color.parseColor("#ABABAB"));
        mTextSize = typedArray.getDimension(R.styleable.LineChartView_textSize, 32);
        mLineColor = typedArray.getColor(R.styleable.LineChartView_lineColor, Color.parseColor("#1fbaf3"));
        mBgColor = typedArray.getColor(R.styleable.LineChartView_bgColor, Color.WHITE);
        typedArray.recycle();

        //  初始化渐变色
        shadeColors = new int[]{
                Color.argb(100, 31, 186, 243), Color.argb(20, 31, 186, 243),
                Color.argb(0, 31, 186, 243)};
        //  初始化折线数据
        mItems = new ArrayList<>();
        mMargin10 = ScreenUtils.dp2px(context, 10);
        init();
    }

    private void init() {
        //  初始化坐标轴画笔
        mPaintAxes = new Paint();
        mPaintAxes.setColor(mAxesColor);
        mPaintAxes.setStrokeWidth(mAxesWidth);

        //  初始化文字画笔
        mPaintText = new Paint();
        mPaintText.setStyle(Paint.Style.FILL);
        mPaintText.setAntiAlias(true); //抗锯齿
        mPaintText.setTextSize(mTextSize);
        mPaintText.setColor(mTextColor);
        mPaintText.setTextAlign(Paint.Align.CENTER);//对齐方式

        //  初始化折线的画笔
        mPaintLine = new Paint();
        mPaintLine.setStyle(Paint.Style.STROKE);
        mPaintLine.setAntiAlias(true);
        mPaintLine.setStrokeWidth(mAxesWidth*2);
        mPaintLine.setColor(mLineColor);

        //  初始化折线路径
        mPath = new Path();
        mPathShader = new Path();

        //初始化x轴坐标点画笔
        mPaintXPoint = new Paint();
        mPaintXPoint.setStyle(Paint.Style.FILL);
        mPaintXPoint.setAntiAlias(true); //抗锯齿
        mPaintXPoint.setColor(mAxesColor);
        mPaintXPoint.setStrokeWidth(mAxesWidth);

        //初始化线上折点画笔
        mPaintPath=new Paint();
        mPaintPath.setStyle(Paint.Style.FILL);
        mPaintPath.setAntiAlias(true); //抗锯齿
        mPaintPath.setColor(mLineColor);
        mPaintPath.setStrokeWidth(mAxesWidth);


        //  阴影画笔
        mPaintShader = new Paint();
        mPaintShader.setAntiAlias(true);
        mPaintShader.setStrokeWidth(2f);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            mWidth = getWidth();
            mHeight = getHeight();
            textWidth = (int) mPaintText.measureText(""+max);
            //  初始化原点坐标
            xOrigin = mMargin10;
            yOrigin = (mHeight - mTextSize - mMargin10);
            //  设置背景色
            setBackgroundColor(mBgColor);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //  画坐标轴
        drawAxes(canvas);
        //  画文字
        drawText(canvas);
        //  X,Y轴间距
        yInterval = (yOrigin - mMargin10)/(max - min) ;
        xInterval = (mWidth - mMargin10*2) / (mItems.size() - 1);
        //  画折线
        drawLine(canvas);
        //  设置动画
        setAnim(canvas);
    }

    /*@Override
    public boolean onTouchEvent(MotionEvent event) {
        this.getParent().requestDisallowInterceptTouchEvent(true);
        if(event.getAction()==MotionEvent.ACTION_UP){
            for (ItemBean mItem : mItems) {
                if(event.getX()<mItem.getiX()+xInterval/4&&event.getX()>mItem.getiX()-xInterval/4){
                    Log.d("LineChartView", "mItem.getValue():" + mItem.getValue());
                }
            }
        }

        return true;
    }*/

    private void setAnim(Canvas canvas) {
        //测量path
        PathMeasure measure = new PathMeasure(mPath, false);
        float pathLength = measure.getLength();
        /*冲刺路径效应
             float数组,必须是偶数长度,且>=2,指定了多少长度的实线之后再画多少长度的空白。
             phase 是跳过这么长的长度,从此长度之后还是绘制*/
        PathEffect effect = new DashPathEffect(new float[]{pathLength, pathLength}, pathLength - pathLength * mProgress);
        mPaintLine.setPathEffect(effect);
        canvas.drawPath(mPath, mPaintLine);
        if(mProgress==1.0){
            for (ItemBean mItem : mItems) {
                canvas.drawCircle(mItem.getiX(),mItem.getiY(),mAxesWidth*3,mPaintPath);
            }
        }
    }

    private void drawLine(Canvas canvas) {
        //  画坐标点
        ItemBean itemBean=null;
        for (int i = 0; i < mItems.size(); i++) {
            float x = i * xInterval + xOrigin + mAxesWidth;
            float y=yOrigin - (mItems.get(i).getValue() - min) * yInterval;
            itemBean=mItems.get(i);
            if (i == 0) {
                mPath.moveTo(x, y);
                mPathShader.moveTo(x, y);
                canvas.drawText(itemBean.getDate(), x+mMargin10, mHeight - mMargin10, mPaintText);
            } else if (i == mItems.size() - 1) {
                mPath.lineTo(x, y);
                mPathShader.lineTo(x, y);
                canvas.drawText(itemBean.getDate(), x-mMargin10, mHeight - mMargin10, mPaintText);
                mPathShader.lineTo(x, yOrigin);
                mPathShader.lineTo(xOrigin, yOrigin);
                mPathShader.close();
            }else{
                mPath.lineTo(x, y);
                mPathShader.lineTo(x, y);
                if(mItems.size()>24){
                    if(i%6==0){
                        canvas.drawText(itemBean.getDate(), x-mAxesWidth, mHeight - mMargin10, mPaintText);
                    }
                }else {
                    canvas.drawText(itemBean.getDate(), x-mAxesWidth, mHeight - mMargin10, mPaintText);
                }
            }
            itemBean.setiX(x);
            itemBean.setiY(y);
            canvas.drawCircle(x-mAxesWidth,yOrigin,mAxesWidth*2,mPaintXPoint);
        }

        //  渐变阴影  创建着色器
        /*线性渐变
        x0表示渲染起始位置的x坐标,
        y0表示渲染起始位置的y坐标,
        x1表示渲染结束位置的x坐标,
        y1表示渲染结束位置的y坐标,
        colors表示渲染的颜色,它是一个颜色数组,数组长度必须大于等于2,
        positions表示colors数组中几个颜色的相对位置,是一个float类型的数组,该数组的长度必须与colors数组的长度相同。
        如果这个参数使用null也可以,这时系统会按照梯度线来均匀分配colors数组中的颜色,
        tileMode表示平铺方式
            1:TileMode.CLAMP  表示重复最后一种颜色直到该View结束的地方
            2:TileMode.REPEAT 表示着色器在水平或者垂直方向上对控件进行重复着色,类似于windows系统桌面背景中的“平铺”
            3:TileMode.MIRROR 在水平方向或者垂直方向上以镜像的方式进行渲染,这种渲染方式的一个特征就是具有翻转的效果
            3种模式的区别在于起始位置到结束位置是否包含全部面积,包含全部则效果一样
        */
        Shader mShader = new LinearGradient(0, 0, 0, getHeight(), shadeColors, null, Shader.TileMode.CLAMP);
        mPaintShader.setShader(mShader);

        //  绘制渐变阴影
        canvas.drawPath(mPathShader, mPaintShader);
    }

    private void drawText(Canvas canvas) {

        //  绘制最大值
        canvas.drawText(max+"", xOrigin + textWidth/2, 2 * mMargin10, mPaintText);
        canvas.drawCircle(xOrigin,mMargin10,mAxesWidth*2,mPaintXPoint);
        //  绘制最小值
        canvas.drawText(min+"", xOrigin + textWidth/2, yOrigin - 6, mPaintText);
        canvas.drawCircle(xOrigin,yOrigin,mAxesWidth*2,mPaintXPoint);
        //  绘制中间值
        canvas.drawText((max+min)/2+"", xOrigin + textWidth/2, (yOrigin + mMargin10) / 2, mPaintText);
        canvas.drawCircle(xOrigin,(yOrigin + mMargin10) / 2,mAxesWidth*2,mPaintXPoint);
       /* //  绘制开始日期
        canvas.drawText(startTime, xOrigin, mHeight - mMargin10, mPaintText);
        //  绘制结束日期
        canvas.drawText(endTime, mWidth - textWidth - mMargin10, mHeight - mMargin10, mPaintText);*/
    }

    //  画坐标轴
    private void drawAxes(Canvas canvas) {
        //  绘制X轴
        canvas.drawLine(xOrigin, yOrigin,                     mWidth - mMargin10, yOrigin,                  mPaintAxes);
        //  绘制X中轴线
        canvas.drawLine(xOrigin, (yOrigin+ mMargin10) / 2,    mWidth - mMargin10, (yOrigin+ mMargin10) / 2, mPaintAxes);
        //  绘制X上边线
        //canvas.drawLine(xOrigin, mMargin10,                   mWidth - mMargin10, mMargin10,                mPaintAxes);
        //  绘制画Y轴
        canvas.drawLine(xOrigin, yOrigin, xOrigin, mMargin10, mPaintAxes);
        //  绘制Y右边线
        //canvas.drawLine(mWidth - mMargin10, mMargin10, mWidth - mMargin10, yOrigin, mPaintAxes);
    }

    //  计算动画进度
    public void setPercentage(float percentage) {
        if (percentage < 0.0f || percentage > 1.0f) {
            throw new IllegalArgumentException("setPercentage not between 0.0f and 1.0f");
        }
        mProgress = percentage;
        invalidate();
    }

    /**
     * @param lineChartView
     * @param duration      动画持续时间
     */
    public void startAnim(LineChartView lineChartView, long duration) {
        //ObjectAnimator通过反射机制,找到了ImageView中的setTranslationX()这个方法,
        // 然后每个十几ms就调用这个方法,并把我们的变化的值传到里面去,从而实现动画效果。
        //当我们需要用ObjectAnimator实现一个控件的动画效果时,
        // 我们首先需要做的就是在这个控件中找到对应的setXXX()驼峰式写法的方法,
        // 只有控件拥有相应的setXXX()方法, 我们传入的propertyName参数才起到作用
        ObjectAnimator anim = ObjectAnimator.ofFloat(lineChartView, "percentage", 0.0f, 1.0f);
        anim.setDuration(duration);
        anim.setInterpolator(new LinearInterpolator());
        anim.start();
    }

    public void clear(){
        mPath.reset();
        mPathShader.reset();
    }

    //  折线数据的实体类
    public static class ItemBean {

        private int value;
        private float iX;
        private float iY;
        private String date;

        public ItemBean(){}


        public ItemBean(int value,String date) {
            super();
            this.value = value;
            this.date = date;
        }

        public String getDate() {
            return date;
        }

        public void setDate(String date) {
            this.date = date;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public float getiX() {
            return iX;
        }

        public void setiX(float iX) {
            this.iX = iX;
        }

        public float getiY() {
            return iY;
        }

        public void setiY(float iY) {
            this.iY = iY;
        }

        @Override
        public String toString() {
            return "ItemBean{" +
                    "value=" + value +
                    ", iX=" + iX +
                    ", iY=" + iY +
                    ", date='" + date + '\'' +
                    '}';
        }
    }
}

//第二步 写个ScreenUtils类,用来初始化数据时使用:

 

public class ScreenUtils {
    private static int screenW;
    private static int screenH;
    private static float screenDensity;

    public static int getScreenW(Context context){
        if (screenW == 0){
            initScreen(context);
        }
        return screenW;
    }

    public static int getScreenH(Context context){
        if (screenH == 0){
            initScreen(context);
        }
        return screenH;
    }

    public static float getScreenDensity(Context context){
        if (screenDensity == 0){
            initScreen(context);
        }
        return screenDensity;
    }

    private static void initScreen(Context context){
        DisplayMetrics metric = context.getResources().getDisplayMetrics();
        screenW = metric.widthPixels;
        screenH = metric.heightPixels;
        screenDensity = metric.density;
    }


    public static int dp2px(Context context, float dpValue) {
        return (int) (dpValue * getScreenDensity(context) + 0.5f);
    }


    public static int px2dp(Context context, float pxValue) {
        return (int) (pxValue / getScreenDensity(context) + 0.5f);
    }
}

//第三步 在values下写个attrs.xml文件来设置风格颜色等: 

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="LineChartView">
        <attr name="axesColor" format="color"/> <!--坐标轴颜色-->
        <attr name="axesWidth" format="dimension"/><!--坐标轴宽度-->
        <attr name="textColor" format="color"/> <!--x轴字体颜色-->
        <attr name="textSize" format="dimension"/> <!--字体大小-->
        <attr name="lineColor" format="color"/> <!--折线颜色-->
        <attr name="bgColor" format="color"/> <!--背景色-->
    </declare-styleable>

</resources>

//第四步 在Activity里使用:

 

 private void initData() {
        record_header_linecharview = (LineChartView) findViewById(R.id.record_header_linecharview);
        //数据集合
        List<Integer> mList = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            mList.add(i);
        }
        //日期数据集合
        ArrayList<String> mStringList = new ArrayList<>();
        mStringList.add("周一");
        mStringList.add("周二");
        mStringList.add("周三");
        mStringList.add("周四");
        mStringList.add("周五");
        mStringList.add("周六");
        mStringList.add("周日");

        List<LineChartView.ItemBean> mItems = new ArrayList<>();
        for (int i = 0; i < mList.size(); i++) {
            mItems.add(new LineChartView.ItemBean(mList.get(i),mStringList.get(i)));
        }
        //  设置折线数据
        record_header_linecharview.setItems(mItems);
//        //  开启动画
//        record_header_linecharview.startAnim(record_header_linecharview,2000);

        Button mBut = (Button) findViewById(R.id.mBut);
        mBut.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //  开启动画
                record_header_linecharview.clear();
                record_header_linecharview.startAnim(record_header_linecharview,2000);
            }
        });
    }

//我的布局:

 

 <com.example.hasee.a1024ceshi.LineChartView
     android:id="@+id/record_header_linecharview"
     android:layout_width="match_parent"
     android:layout_height="200dp"
     app:textColor="#ABABAB"
     app:textSize="12dp"/>


<Button
    android:id="@+id/mBut"
    android:text="开始动画"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

//———————————————————————完—————————————————————————————–

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/118306.html

(0)
seven_的头像seven_bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!