Android自定义view---折线图

一,先上效果图。
结果图
二,思路讲解。
①先画出X Y轴 xy的坐标如下Xy示意图

②,画出Y轴的两条等量线(标识线)未消毒,已消毒的横线(比较浅的那两条)。
③,循环画出底部的X轴上的时间,以及折现的开始结束

1)由于折线的长短要根据时间的长短来显示,所以这里通过计算比例,实现了,时间越长,折现越长。反之

④,最后要处理一下长宽*的问题,所以一开始要给好dp/px做好转换,适配。

三,代码详细


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class XyTime1 extends View {
    private Context context;

    public XyTime1(Context context) {
        super(context);
        this.context = context;
        initView();
    }

    public XyTime1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView();
        this.context = context;

    }

    public XyTime1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
        this.context = context;

    }

    public XyTime1(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView();
        this.context = context;

    }

    //背景的颜色
    public Paint xyPaint = new Paint();
    public Paint xyPaint1 = new Paint();
    public Paint xPaint = new Paint();
    public Paint font = new Paint();

    public Paint linfont = new Paint();

    public void initView() {
        xyPaint.setColor(ContextCompat.getColor(getContext(), R.color.linexy));
        xyPaint.setStrokeWidth(3f);
        xyPaint.setStrokeCap(Paint.Cap.ROUND);
        xyPaint1.setColor(ContextCompat.getColor(getContext(), R.color.white1));
        xyPaint1.setStrokeWidth(3f);
        xyPaint1.setStrokeCap(Paint.Cap.ROUND);
        xPaint.setColor(ContextCompat.getColor(getContext(), R.color.linexy));
        xPaint.setStrokeWidth(1f);
        font.setTextSize(12f);
        font.setColor(ContextCompat.getColor(getContext(), R.color.white));
        linfont.setTextSize(20f);
        linfont.setStrokeWidth(10f);
        linfont.setColor(ContextCompat.getColor(getContext(), R.color.chart));
        linfont.setStrokeCap(Paint.Cap.ROUND);
//        startX:开始点X坐标
//        startY:开始点Y坐标
//        stopX:结束点X坐标
//        stopY:结束点Y坐标

    }
    int PointX;

    public float left = 0f;
    public float right = 1080 + 150f;
    public float top = 0f;
    public float bottom = 192;//40f
    private int def = 1230;
    int slideX = 50+PointX;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //X
        canvas.drawLine(slideX, 192, def + PointX, 192, xyPaint);
        //Y
        canvas.drawLine(slideX, 50, slideX, 192, xyPaint1);
        Log.d("onDraw:", "" + PointX);
        //已消毒 上条
        canvas.drawLine(slideX, 192 - 100, def + PointX, 192 - 100, xPaint);
        //未消毒下
        canvas.drawLine(slideX, 192 - 50, def + PointX, 192 - 50, xPaint);
        canvas.drawText("未消毒", slideX-50, 192 - 100 + 10, font);
        canvas.drawText("消毒中", slideX-50, 192 - 50 + 10, font);

        //底部时间坐标,根据每个坐标 间距30
        //初始位置,150
        int x = 100 + PointX;
        int x1 ;//每次改变后,x1
        int nextType = 0;//下一个标记
        int a = 0;//当前一个时间
        int b = 0;//下一个时间

        for (int i = 0; i < names.length; i++) {

            x += 120;//x结束位置 //每次的位置 这里要保证x不能小于50,
            x1 = x - 120;//x结束的位置     //140 -27
            canvas.drawText(names[i], x - 140, 192 + 30, font);
            //这里还要增加 当前位置 X Y 线

            if (i + 1 < names.length) {
                //就取下一个
                nextType = imts[i + 1];
                //判断一下,长度大于2不然,报错。
                a = Integer.parseInt(names[i].substring(0, 2));//当前这个
                b = Integer.parseInt(names[i + 1].substring(0, 2));//下一个
                //每小时增加15px
                x += (b - a) * 15;


            } else {
                //没有下一个imts[i+1]
                nextType = imts[i];
            }

            setLine(canvas, x, x1, imts[i], nextType);
        }

    }

    private String[] names = {"08:00", "08:00", "11:30", "13:30", "14:00", "15:00", "23:00", "24:00", "24:00"};
    private int[] imts = {0, 1, 0, 1, 0, 1, 0, 1, 0};
    int statex = 0;

    //设置两点之间的线。
    private void setLine(Canvas canvas, int x, int x1, int type, int nextType) {
        // x
        // y
        Log.d("c:", "");

        if (type == 1) {//开始消毒
            canvas.drawLine(x1 + 3, 192 - 100, x, 192 - 100, linfont);

        } else {
            //未消毒
            canvas.drawLine(x1 + 3, 192 - 50, x, 192 - 50, linfont);

        }
        //2条线之间的Y线
        //判断当前这个线 跟下一条线的 也就是 type 是不是一样 ,如果是一样,就不画Y线,
        //如果不一样,就是画Y线的时候
        if (type != nextType) {
//            //画Y线
            canvas.drawLine(x, 192 - 50, x, 192 - 100, linfont);
        }

    }

    /**
     * 测量宽高。不然 设置大小无用
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {


        // TODO Auto-generated method stub
        int width = 0;
        int height = 0;
        //获得宽度MODE
        int modeW = MeasureSpec.getMode(widthMeasureSpec);
        //获得宽度的值
        if (modeW == MeasureSpec.AT_MOST) {
            width = MeasureSpec.getSize(widthMeasureSpec);
        }
        if (modeW == MeasureSpec.EXACTLY) {
            width = widthMeasureSpec;
        }
        if (modeW == MeasureSpec.UNSPECIFIED) {
            width = 600;
        }
        //获得高度MODE
        int modeH = MeasureSpec.getMode(height);
        //获得高度的值
        if (modeH == MeasureSpec.AT_MOST) {
            height = MeasureSpec.getSize(heightMeasureSpec);
        }
        if (modeH == MeasureSpec.EXACTLY) {
            height = heightMeasureSpec;
        }
        if (modeH == MeasureSpec.UNSPECIFIED) {
            //ScrollView和HorizontalScrollView
            height = 240;
        }
        //设置宽度和高度
        setMeasuredDimension(width, height);

    }

    private int lastX;
    private int lastY;
    //手指按下的时间长
    public long startTime = 0;
    int downX;//按下的坐标

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            //按下
            case MotionEvent.ACTION_DOWN:
                startTime = System.currentTimeMillis();
                downX = (int) event.getX();
//                invalidate();
                break;
            //拖动
            case MotionEvent.ACTION_MOVE:
//                startTime = System.currentTimeMillis();
                PointX = (int) event.getX();
                Log.d("getRawX:", "" + event.getRawX());
                Log.d("getX:", "" + event.getX());
                Log.d("getLeft:", "" +getLeft()); //0
                Log.d("getRight:", "" + getRight());//1280
                Log.d("getTop:", "" + getTop());//0
                Log.d("getBottom:", "" + getBottom());//240
//                if (downX > PointX) {
//                    //说明 在忘左边滑动
//                    PointX = PointX - downX;
//
//                }
//                if (downX < PointX) {
//                    if (PointX < 700) {
//                        PointX = (int) event.getX();
//                    }
//                }
                invalidate();


//                    //右边
//                    if (PointX>50){
//                        invalidate();
//                    }
//                }

                break;

            //抬起
            case MotionEvent.ACTION_UP:
                PointX = (int) event.getX();

                long endTime = System.currentTimeMillis();

                if (endTime - startTime > 500) {//如果按下,抬起时间过大才认为是拖动,要执行动画。

                }
                break;

        }
        return true;

        //这种整体移动
//        int action = event.getAction();
//        switch (action) {
//            case MotionEvent.ACTION_DOWN:
//                lastX = (int) event.getRawX();//获取触摸事件触摸位置的原始X坐标
//                lastY = (int) event.getRawY();
//                break;
//            case MotionEvent.ACTION_MOVE:
//                //event.getRawX();获得移动的位置
//                int dx = (int) event.getRawX() - lastX;
//                int dy = (int) event.getRawY() - lastY;
//                int l = this.getLeft() + dx;
//                int b = this.getBottom() + dy;
//                int r = getRight() + dx;
//                int t = getTop() + dy;
//                this.layout(l, t, r, b);//重新布局
//                lastX = (int) event.getRawX();
//                lastY = (int) event.getRawY();
//                break;
//            case MotionEvent.ACTION_UP:
//                break;
//        }
//        return true;//由于要处理所有手势,全部返回true;

    }


//    @Override
//    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//        super.onLayout(changed, left, top, right, bottom);
//        this.left = left;
//        this.top = top;
//        this.right = right;
//        this.bottom = bottom;
//
//    }


    /**
     * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

}

四,有需要定制UI的加我Q:1181620038

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值