Android自定义View实现天气预报折线图

本文实例为大家分享了Android自定义View画天气预报折线图的具体代码,供大家参考,具体内容如下

效果图如下:

刚开始尝试用第三方画曲线的框架来画效果图,后来发现曲线间的阴影当有负数的度数的时候画不出来,而且不需要点击放大、点点可点的效果,用框架显得很臃肿,所以最后用自定义View来画的折线图。自定义画折线图的大致思路:这个图是有多个四边形组成的(4个点连接起来就是一个四边形),两边延伸:添加四个多余的点,将左右的边距设置成负数即可。

代码如下:

public class WeatherChartView extends View {

    /**
     * x轴集合
     */
    private float mXAxis[] ;

    /**
     * 白天y轴集合
     */
    private float mYAxisDay[] ;

    /**
     * 夜间y轴集合
     */
    private float mYAxisNight[] ;

    /**
     * x,y轴集合数
     */
    private int LENGTH ;

    /**
     * 白天温度集合
     */
    private int mTempDay[] ;

    /**
     * 夜间温度集合
     */
    private int mTempNight[] ;

    /**
     * 控件高
     */
    private int mHeight;

    /**
     * 字体大小
     */
    private float mTextSize;

    /**
     * 圓半径
     */
    private float mRadius ;

    /**
     * 圓半径今天
     */
    private float mRadiusToday ;

    /**
     * 文字移动位置距离
     */
    private float mTextSpace ;

    /**
     * 线的大小
     */
    private float mStokeWidth ;

    /**
     * 白天折线颜色
     */
    private int mColorDay = Color.parseColor("#ffffff");

    /**
     * 夜间折线颜色
     */
    private int mColorNight = Color.parseColor("#ffffff");;

    /**
     * 字体颜色
     */
    private int mTextColor = Color.parseColor("#ffffff");;

    /**
     * 屏幕密度
     */
    private float mDensity;

    /**
     * 控件边的空白空间
     */
    private float mSpace;

    @SuppressWarnings("deprecation")
    public WeatherChartView(Context context, AttributeSet attrs) {
        super(context, attrs); 
        mDensity = getResources().getDisplayMetrics().density;
        mRadius = 3 * mDensity;
        mRadiusToday = 3 * mDensity;
        //mSpace = 3 * mDensity;
        mTextSpace = 10 * mDensity;
        mStokeWidth = 2 * mDensity;
        mTextSize = BreakRuleTools.dip2px(context, 12);
    }

    public WeatherChartView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHeight == 0) {
            // 设置控件高度,x轴集合
            setHeightAndXAxis();
        }
        // 计算y轴集合数值
        computeYAxisValues();
        // 画白天折线图
        drawChart(canvas, mColorDay, mTempDay, mYAxisDay, 0);
        // 画夜间折线图
        drawChart(canvas, mColorNight, mTempNight, mYAxisNight, 1);
    }

    /**
     * 计算y轴集合数值
     */
    private void computeYAxisValues() {
        // 存放白天最低温度
        int minTempDay = mTempDay[0];
        // 存放白天最高温度
        int maxTempDay = mTempDay[0];
        for (int item : mTempDay) {
            if (item < minTempDay) {
                minTempDay = item;
            }
            if (item > maxTempDay) {
                maxTempDay = item;
            }
        }

        // 存放夜间最低温度
        int minTempNight = mTempNight[0];
        // 存放夜间最高温度
        int maxTempNight = mTempNight[0];
        for (int item : mTempNight) {
            if (item < minTempNight) {
                minTempNight = item;
            }
            if (item > maxTempNight) {
                maxTempNight = item;
            }
        }

        // 白天,夜间中的最低温度
        int minTemp = minTempNight < minTempDay ? minTempNight : minTempDay;
        // 白天,夜间中的最高温度
        int maxTemp = maxTempDay > maxTempNight ? maxTempDay : maxTempNight;

        // 份数(白天,夜间综合温差)
        float parts = maxTemp - minTemp;
        // y轴一端到控件一端的距离
        float length = mSpace + mTextSize + mTextSpace + mRadius;
        // y轴高度
        float yAxisHeight = mHeight - length * 2;

        // 当温度都相同时(被除数不能为0)
        if (parts == 0) {
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = yAxisHeight / 2 + length;
                mYAxisNight[i] = yAxisHeight / 2 + length;
            }
        } else {
            float partValue = yAxisHeight / parts;
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = mHeight - partValue * (mTempDay[i] - minTemp) - length;
                mYAxisNight[i] = mHeight - partValue * (mTempNight[i] - minTemp) - length;
            }
        }
    }

    /**
     * 画折线图
     *
     * @param canvas 画布
     * @param color  画图颜色
     * @param temp   温度集合
     * @param yAxis  y轴集合
     * @param type   折线种类:0,白天;1,夜间
     */
    private void drawChart(Canvas canvas, int color, int temp[], float[] yAxis, int type) {
        color = Color.parseColor("#ffffff");
        // 线画笔
        Paint linePaint = new Paint();
        // 抗锯齿
        linePaint.setAntiAlias(true);
        // 线宽
        linePaint.setStrokeWidth(mStokeWidth);
        linePaint.setColor(color);
        // 空心
        linePaint.setStyle(Paint.Style.STROKE);

        // 点画笔
        Paint pointPaint = new Paint();
        pointPaint.setAntiAlias(true);
        pointPaint.setColor(color);

        // 字体画笔
        Paint textPaint = new Paint();
        textPaint.setAntiAlias(true);
        textPaint.setColor(mTextColor);
        textPaint.setTextSize(mTextSize);
        // 文字居中
        textPaint.setTextAlign(Paint.Align.CENTER);

        int alpha1 = 102;
        int alpha2 = 255;
        for (int i = 0; i < LENGTH; i++) {
            // 画线
            if (i < LENGTH - 1) {
                // 昨天
                if (i == -1) {
                    linePaint.setAlpha(alpha1);
                    // 设置虚线效果
                    linePaint.setPathEffect(new DashPathEffect(new float[]{2 * mDensity, 2 * mDensity}, 0));
                    // 路径
                    Path path = new Path();
                    // 路径起点
                    path.moveTo(mXAxis[i], yAxis[i]);
                    // 路径连接到
                    path.lineTo(mXAxis[i + 1], yAxis[i + 1]);
                    canvas.drawPath(path, linePaint);
                } else {
                    if(type == 0) {
                        linePaint.setAlpha(76);
                        linePaint.setPathEffect(null);

                        linePaint.setStyle(Paint.Style.FILL);//设置实心
                        Path path = new Path();                     //Path对象
                        path.moveTo(mXAxis[i], mYAxisDay[i]);                           //起始点
                        path.lineTo(mXAxis[i + 1], mYAxisDay[i + 1]);                           //连线到下一点
                        path.lineTo(mXAxis[i + 1], mYAxisNight[i + 1]);                      //连线到下一点
                        path.lineTo(mXAxis[i], mYAxisNight[i]);                      //连线到下一点
                        path.lineTo(mXAxis[i], mYAxisDay[i]);                      //连线到下一点
                        canvas.drawPath(path, linePaint);                   //绘制任意多边形
                    }
                    //canvas.drawLine(mXAxis[i], yAxis[i], mXAxis[i + 1], yAxis[i + 1], linePaint);
                }
            }

            // 画点
            if (i != 1) {
                // 昨天
                if (i == 0 || i == LENGTH - 1) {
                    /*pointPaint.setAlpha(alpha1);
                    canvas.drawCircle(mXAxis[i], yAxis[i], mRadius, pointPaint);*/
                } else {
                    pointPaint.setAlpha(alpha2);
                    canvas.drawCircle(mXAxis[i], yAxis[i], mRadius, pointPaint);
                }
                // 今天
            } else {
                pointPaint.setAlpha(alpha2);
                canvas.drawCircle(mXAxis[i], yAxis[i], mRadiusToday, pointPaint);
            }

            // 画字
            // 昨天
            if (i == 0 || i == LENGTH - 1) {
                /*textPaint.setAlpha(alpha1);
                drawText(canvas, textPaint, i, temp, yAxis, type);*/
            } else {
                textPaint.setAlpha(alpha2);
                drawText(canvas, textPaint, i, temp, yAxis, type);
            }
        }
    }

    /**
     * 绘制文字
     *
     * @param canvas    画布
     * @param textPaint 画笔
     * @param i         索引
     * @param temp      温度集合
     * @param yAxis     y轴集合
     * @param type      折线种类:0,白天;1,夜间
     */
    private void drawText(Canvas canvas, Paint textPaint, int i, int[] temp, float[] yAxis, int type) {
        switch (type) {
            case 0:
                // 显示白天气温
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] - mRadius - mTextSpace, textPaint);
                break;
            case 1:
                // 显示夜间气温
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] + mTextSpace + mTextSize, textPaint);
                break;
        }
    }

    /**
     * 设置高度,x轴集合
     */
    private void setHeightAndXAxis() {
        mHeight = getHeight();
        // 控件宽
        int width = getWidth();
        int i = LENGTH - 2;
        // 每一份宽
        float w = width / (i*2);

        for(int j =0;j<LENGTH;j++){
            if(j == 0){
                mXAxis[j] = 0;
            } else if(j == LENGTH -1){
                mXAxis[j] = width;
            } else{
                mXAxis[j] = w * (2*j -1);
            }
        }
       /* mXAxis[0] = 0;
        mXAxis[1] = w;
        mXAxis[2] = w * 3;
        mXAxis[3] = w * 5;
        mXAxis[4] = w * 7;
        mXAxis[5] = w * 9;
        mXAxis[6] = width;*/
       /* mXAxis[5] = w * 11;
        mXAxis[6] = w * 13;*/
    }

    /**
     * 设置白天温度
     *
     * @param tempDay 温度数组集合
     */
    public void setTempDay(int[] tempDay) {
        mTempDay = tempDay;
        LENGTH = mTempDay.length;

        mXAxis = new float[LENGTH];
        mYAxisDay = new float[LENGTH];
        mYAxisNight = new float[LENGTH];
        /*mTempDay = new int[LENGTH];
        mTempNight = new int[LENGTH];*/
    }

    /**
     * 设置夜间温度
     *
     * @param tempNight 温度数组集合
     */
    public void setTempNight(int[] tempNight) {
        mTempNight = tempNight;
    }

    /**
     * 设置白天曲线的颜色
     */
    public void setColorDay(){}
}

布局代码:

<com.pingan.carowner.weather.view.WeatherChartView
            android:id="@+id/line_char"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:layout_below="@id/weather_over_view_item3"
            android:layout_centerInParent="true"/>

代码引用:

// 设置白天温度曲线
mChartView = (WeatherChartView) findViewById(R.id.line_char);
mChartView.setTempDay(highTemp);//highTemp 高温度集合
// 设置夜间温度曲线
mChartView.setTempNight(lowTemp);//lowTemp 低温集合
mChartView.invalidate();

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android自定义View实现水波纹扩散效果

    目录 1.创建RippleView.class, 继承与View 1.1特殊属性解释 1.2新建attrs.xml文件(res/values) 1.3初始化画笔 2.开始绘制onDraw() 效果:水波纹扩散 场景:雷达.按钮点击效果.搜索等 实现:先上效果图,之前记得支付宝有一个咻一咻,当时就是水波纹效果,实现起来一共两步,第一画内圆,第二画多个外圆,不同时创建有间隔创建然后缓慢增大外圆半径,到达最远距离时移除掉,扩散时把透明度从255-1不断赋值即可.复杂在第二步,开工. 开工 1.创建Ri

  • Android自定义View实现时钟功能

    最近在练习自定义view, 想起之前面试的时候笔试有道题是写出自定义一个时钟的关键代码. 今天就来实现一下. 步骤依然是先分析, 再上代码. 实现效果 View分析 时钟主要分为五个部分: 1.中心点: 圆心位置2.圆盘: 以中心点为圆心,drawCircle画个圆3.刻度: paint有个aip, setPathEffect可以根据path画特效, 那么刻度就可以根据圆的path画一个矩形path的特效, 并且这个api只会画特效, 不会画出圆. /** * shape: 特效的path, 这

  • Android自定义View实现跟随手指移动

    对View的移动,实现的方法有好几种,原理是通过改变View的位置来移动View,下面来实现这样的效果 动画的方法 通过改变View的tranlationX和tranlationY的值来实现移动,首先来写一个自定义View类,重写onTouchEvent方法,实现构造方法 public class MyView extends View {     public MyView(Context context) {         super(context);     }     public

  • Android自定义view实现侧滑栏详解

    目录 前言 需求 效果图 编写代码 主要问题 前言 上一篇文章学了下自定义View的onDraw函数及自定义属性,做出来的滚动选择控件还算不错,就是逻辑复杂了一些.这篇文章打算利用自定义view的知识,直接手撕一个安卓侧滑栏,涉及到自定义LayoutParams.带padding和margin的measure和layout.利用requestLayout实现动画效果等,有一定难度,但能重新学到很多知识! 需求 这里类似旧版QQ(我特别喜欢之前的侧滑栏),有两层页面,滑动不是最左侧才触发的,而是从

  • Android自定义View绘制贝塞尔曲线实现流程

    目录 前言 二阶贝塞尔曲线 三阶贝塞尔曲线 前言 对于Android开发,实现贝塞尔曲线还是比较方便的,有对应的API供你调用.由于一阶贝塞尔曲线就是一条直线,实际没啥多大用处,因此,下面主要讲解二阶和三阶. 二阶贝塞尔曲线 在Android中,使用quadTo来实现二阶贝塞尔 path.reset() path.moveTo(startX, startY) path.quadTo(currentX, currentY, endX, endY) canvas.drawPath(path, cur

  • Android自定义view实现滚动选择控件详解

    目录 前言 需求 编写代码 主要问题 前言 上篇文章通过一个有header和footer的滚动控件(Viewgroup)学了下MeasureSpec.onMeasure以及onLayout,接下来就用一个滚动选择的控件(View)来学一下onDraw的使用,并且了解下在XML自定义控件参数. 需求 这里就是一个滚动选择文字的控件,还是挺常见的,之前用别人的,现在选择手撕一个,核心思想如下: 1.有三层不同大小及透明度的选项,选中项放在中间 2.接受一个列表的数据,静态时显示三个值,滚动时显示四个

  • Android小工具自定义view课表

    本文实例为大家分享了Android自定义view课表的具体代码,供大家参考,具体内容如下 这里是模拟课表,数据写死了的,不过也可以通过抓包获取教务系统课表 1.xml文件 <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:app="

  • Android自定义View实现天气预报折线图

    本文实例为大家分享了Android自定义View画天气预报折线图的具体代码,供大家参考,具体内容如下 效果图如下: 刚开始尝试用第三方画曲线的框架来画效果图,后来发现曲线间的阴影当有负数的度数的时候画不出来,而且不需要点击放大.点点可点的效果,用框架显得很臃肿,所以最后用自定义View来画的折线图.自定义画折线图的大致思路:这个图是有多个四边形组成的(4个点连接起来就是一个四边形),两边延伸:添加四个多余的点,将左右的边距设置成负数即可. 代码如下: public class WeatherCh

  • Android自定义View之渐变色折线图的实现

    目录 前言 如何实现 总结 前言 在之前的项目中,有做过一个需求,需要实现一个颜色渐变的折线图.当时项目中使用的图表库是MPAndroidChart,但是该库没有提供合适的方法来实现想要的效果,因此只能通过自定义view来实现. 通过这篇文章记录一下,便于之后需要实现类似的效果时查找使用. 如何实现 通过创建LinearGradient来实现颜色渐变,并将之设置到画笔Paint的着色器Shader,绘制想要的路径即可实现该效果. 实现代码如下: class GradientLineChart :

  • Android自定义View实现圆形切图效果

    使用自定义View实现圆形ImageView的效果,具体内容如下 目前圆形边框还需要调整,这里有点问题 实现思路 使用一个Paint,将得到的Bitmap设置成paint的Shader,设置完成后,使用Matrix调整图片至居中,使用RectF约束边框,最后完成绘制 初始化Paint,设置Shader private void init() { getBitmapFromDrawable(); if (mBitmap == null) { return; } mShader = new Bitm

  • Android自定义View实现折线图效果

    下面就是结果图(每种状态用一个表情图片表示): 一.主页面的布局文件如下: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height=&quo

  • Android自定义View实现公交成轨迹图

    本文实例为大家分享了Android自定义View实现公交成轨迹图的具体代码,供大家参考,具体内容如下 总体分析下:水平方向recyclewview,item包含定位点,站台位置和站台名称. 下面看实现: 1.继承framelayout,实现构造方法: public class BusStopPlateView extends FrameLayout { ... public BusStopPlateView(@NonNull Context context) { super(context);

  • Android使用自定义View实现饼状图的实例代码

    本文讲述了Android使用自定义View实现饼状图的实例代码.分享给大家供大家参考,具体如下: 1.效果图 2.代码实现 public class PieChartView extends View { private Paint mPaint; private List<PieData>pieDataList; // 饼状图初始绘制角度 private float mStartAngle = 0; public PieChartView(Context context) { this(co

  • Android 自定义View实现芝麻分曲线图效果

    1.简介 其实这个效果几天之前就写了,但是一直没有更新博客,本来想着把芝麻分雷达图也做好再发博客的,然后今天看到鸿洋的微信公众号有朋友发了芝麻分的雷达图,所以就算了,算是一个互补吧.平时文章也写的比较少,所以可能有点杂乱,有什么需要改进的地方欢迎给出建议,不胜感激. 效果图: 2.步骤: 初始化View的属性 初始化画笔 绘制代表最高分和最低分的两根虚线 绘制文字 绘制代表月份的属性 绘制芝麻分折线 绘制代表芝麻分的圆点 绘制选中分数的悬浮文字以及背景 处理点击事件 3.编码: 初始化View属

  • Android自定义View实现柱状波形图的绘制

    目录 前言 实现 基本属性 设计监听器 绘制图形 左右拖动 完整代码 前言 柱状波形图是一种常见的图形.一个个柱子按顺序排列,构成一个波形图. 柱子的高度由输入数据决定.如果输入的是音频的音量,则可得到一个声波图. 在一些音频软件中,我们也可以左右拖动声波,来改变音频的播放进度 本文举例的自定View,实现如下功能: 以柱状形式展示数据的大小 标明图形当前最中间的数据 可以横向拖动进度,进度就是让某个特定的数据居中展示 可以改变左右两边的柱子颜色 可以调整柱子的宽度 拖动完毕后监听当前进度 实现

  • Android自定义view实现阻尼效果的加载动画

    效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又称减幅振动.衰减振动.[1] 不论是弹簧振子还是单摆由于外界的摩擦和介质阻力总是存在,在振动过程中要不断克服外界阻力做功,消耗能量,振幅就会逐渐减小,经过一段时间,振动就会完全停下来.这种振幅随时间减小的振动称为阻尼振动.因为振幅与振动的能量有关,阻尼振动也就是能量不断减少的振动.阻尼振动是非简谐运

  • Android自定义View实现简单的圆形Progress效果

    先给大家展示下效果图,如果感觉不错,请参考实现思路: 我们要实现一个自定义的再一个圆形中绘制一个弧形的自定义View,思路是这样的: 先要创建一个类ProgressView,继承自View类,然后重写其中的两个构造方法,一个是一个参数的,一个是两个参数的,因为我们要在xml文件中使用该自定义控件,所以必须要定义这个两个参数的构造函数.创建完了这个类后,我们先不去管它,先考虑我们实现的这个自定义View,我们想让它的哪些部分可以由使用者自己指定,比如说这个Demo中我们让他的外面圆的外边框颜色和宽

随机推荐