自定义View--折线图

  • 最近写一个天气的软件练手,需要用到折线图显示天气,效果图如下

这里写图片描述

自定义View步骤

    现在我们来使用Canvas类自定义一个View控件。自定义控件步骤如下: 
1. 自定义View,首先定义一个MyView类继承View类。
2.重写View的两个构造器。

  View是包含四个构造器的,我们必须重写MyWidgetView(Context context, AttributeSet attrs)构造器,因为该构造器的第二个参数是与xml布局文件相联系的,如果没有重写该构造器,将不能在布局中使用该控件。这里我们重写他的两个构造器:

    public MyView(Context context) {
        super(context);
    }
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
3. 重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,定义控件的尺寸:宽度和高度。在布局中使用该控件时会会传入控件的尺寸,只有当传入尺寸之后且调用onMesure之后,控件才会有宽度和高度。
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);//设置宽和高
    }
4.重写onDraw(Canvas canvas)方法,我们在该方法中定义绘制View,当我们在Activity或其他地方使该控件时, UI主线程会调用onDraw方法绘制。
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

  onDraw(Canvas canvas)方法中传入了一个Canvas对象,我们在定义控件时,使用Canvas绘制。

折线图

public class TempTrendView extends View {

    //两个构造函数,没说的。
    public TempTrendView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public TempTrendView(Context context) {
        super(context);
        init();
    }

    //定义一些必要的属性,两个ArrayLsit是存放传进来的温度值
    Paint paint;
    private int screenWidth;
    private int viewHeight;
    private int everyWidth;
    private ArrayList<Integer> highTemp;
    private ArrayList<Integer> lowTemp;


    public void setHighTemp(ArrayList<Integer> highTemp) {
        this.highTemp = highTemp;
        //这个函数是用来清屏的,每次加载,都先把以前的内容清除掉
        invalidate();
    }

    public void setLowTemp(ArrayList<Integer> lowTemp) {
        this.lowTemp = lowTemp;
        //这个函数是用来清屏的,每次加载,都先把以前的内容清除掉
        invalidate();
    }
    //初始化各种实例及进行必要的设置
    private void init() {
        paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setStrokeWidth(4);
        paint.setAntiAlias(true);
        paint.setTextSize(40);
    }

    //获取屏幕宽度,折线图控件本身的高度用于计算
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        WindowManager wm = (WindowManager) getContext().getSystemService(
                Context.WINDOW_SERVICE);
        screenWidth = wm.getDefaultDisplay().getWidth();
        viewHeight = getLayoutParams().height;
        //一周分为7天,我们要把宽度均分
        everyWidth = screenWidth / 7;
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (highTemp == null) {
            //System.out.println("highTemp不能为空");
            return;
        }
        //System.out.println(highTemp.size());
        //设置开始绘制高温的x坐标和y坐标
        float startX = everyWidth / 2;
        float startY = viewHeight / 5*2;
        float detay;
        //这两个用来记录是否为当天的位置坐标,在最后划竖线的时候用到
        float todaySY = 0;
        float todayEY = 0;
        //这个变量是每差1度纵向的偏移量
        int fudu = -10;
        //这个是文字的偏移量
        int textPianyi = -40;
        //用来记录第二天和第一天总的纵向偏移的距离的
        int extra = -5;
        canvas.drawText("一周温度走势图", screenWidth / 2 - 140, 50, paint);
        paint.setColor(Color.rgb(241,162,89));
        for (int i = 0; i < highTemp.size() - 1; i++) {
            //设置画笔的颜色透明度
            paint.setAlpha(0xff);
            //就是等比例的改变不同点的高度显示
            detay = (highTemp.get(i + 1) - highTemp.get(i)) * fudu;
            //折线点的圆的绘制
            canvas.drawCircle(startX, startY, 15, paint);
            //折线的绘制
            canvas.drawLine(startX, startY, startX + everyWidth,
                    startY + detay, paint);
            canvas.drawText(highTemp.get(i) + "℃", startX - 15, startY+ textPianyi + extra,paint);
            if (i == 0) {
                todaySY = startY;
            }
            startX = startX + everyWidth;
            startY = startY + detay;
        }
        canvas.drawCircle(startX, startY, 15, paint);
        // paint.setColor(0xffFF3030);
        canvas.drawText(highTemp.get(highTemp.size() - 1) + "℃", startX, startY +textPianyi+ extra, paint);

        //这是绘制最顶温度的代码,最低温度和上面思路基本一致,就不再解释
        if (lowTemp == null) {
            //System.out.println("lowTemp不能为空");
            return;
        }
        startX = everyWidth / 2;
        //这里设置最低温度也和最高温度一样的起始位置,然后根据二者插值,同时计算上纵向二者的偏移量
        startY = viewHeight / 5*2 +(lowTemp.get(0) - highTemp.get(0)) * fudu;
        paint.setColor(Color.rgb(71,213,234));
        for (int i = 0; i < lowTemp.size() - 1; i++) {
            paint.setAlpha(0xff);
            detay = (lowTemp.get(i + 1) - lowTemp.get(i)) * fudu;
            canvas.drawLine(startX, startY, startX + everyWidth,startY + detay, paint);
            canvas.drawCircle(startX, startY, 15, paint);
            canvas.drawText(lowTemp.get(i) + "℃", startX + 5, startY- textPianyi-extra,paint);
            if (i == 0) {
                todayEY = startY;
            }
            startX = startX + everyWidth;
            startY = startY + detay;
        }
        canvas.drawCircle(startX, startY, 15, paint);
        canvas.drawText(lowTemp.get(lowTemp.size()-1) + "℃", startX + 5, startY -textPianyi-extra,paint);

        drawDottedLine(everyWidth / 2, todaySY, todayEY+ (viewHeight - todayEY) - 30, canvas);
        canvas.drawText("今天", everyWidth / 2 - 38, viewHeight - 5, paint);
    }

    /**
     * 绘制竖直的虚线, endY必须大于startY
     * 根据记录的当天的点位置,划线标注出当天对应的温度位置
     */
    private void drawDottedLine(float sX, float startY, float endY,Canvas canvas) {
        while (startY < endY) {
            canvas.drawLine(sX, startY, sX, startY + 10, paint);
            startY = startY + 20;
        }
    }
}
  • 注释就在上面的代码中了,这里就不再对应贴出写解释了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值