Android中绘制的两个天气相关的View

news2024/11/18 5:47:56

文章目录

  • 一、前言
  • 二、降雨的代码
  • 三、风向代码

一、前言

开发天气相关软件时候,做了两个自定义View,这里进行记录,由于涉及类较多,这里仅包含核心代码,需要调整后才可以运行,自定义View范围仅包含网格相关UI。需要注意的是横向坐标需要25个数据,来表示一天24小时。最后一个数据表示0点,效果如下:
降雨的效果:
该效果可以通过滑动来更新顶部日期和详细降雨信息。自定义View包含天气图标、网格、横竖坐标
在这里插入图片描述
风向效果:
该效果可以通过滑动来更新顶部日期和详细降雨信息。自定义View包含天气图标、网格、横竖坐标
在这里插入图片描述

二、降雨的代码

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.special.aspire.weather.R;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by YM on 2023/7/28.
 */
public class WeatherColumn extends View {

    private float sumHeight;//总控件的高度
    private float sumWidth;// 总空间的宽度
    private Paint linePaint;//网格横线线的画笔
    private Paint verticalDividerLinePaint;//网格线中垂直分割线的画笔
    private Paint verticalDividerLinePaint2;//网格线中第二条垂直分割线的画笔
    private Paint mColumnPaint;//圆柱的画笔
    private Path mColumnPath;//圆柱的路径
    private Paint textPaint;//文字的画笔
    private Paint topImagePaint;//顶部图标的画笔
    private List<Float> mPrecipitationList;//降水
    private List<String> mDateTimeList;//底部的时间
    private List<String> mCodeList;//天气代码
    private float oneHeight; //每一个小段所要分成的高
    private float oneWidth;//每一个小段所要分成的宽
    private float buttomHeiht; //给底部一排日期预留出的时间
    private Path baseLinePath;//折线路径
    private List<PointF> xyList;//储存定好的坐标点的集合

    private List<Integer> topImageList = new ArrayList<>();
    private List<Boolean> weatherEnableList = new ArrayList<>();
    // Y轴显示的列表 必须是9个
    private final List<String> yDataList = new ArrayList<>();


    // 设置进来的数据是否有效
    // 例:windSpeedIsEnableList  false false  true true  true true true false false
    // firstIndicateIndex 为2  secondIndicateIndex = 6
    private int beforeIndicateIndex = -1;
    private int afterIndicateIndex = -1;
    private int ratioY = 25;

    private float unitRation = 10;// 不同降雨单位之间的比值

    private RainData rainData;


    public WeatherColumn(Context context) {
        super(context);
        initPaint();
        initYDataList();
    }

    public WeatherColumn(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
        initYDataList();
    }


    private void initYDataList() {
        ratioY = 17;
        if (yDataList.isEmpty()) {
            for (int i = 0; i < 17; i++) {
                if (i % 2 == 0) {
                    yDataList.add(i + "");
                }
            }
        }
    }

    public void setYDataListIn() {
        ratioY = 25;
        yDataList.clear();
        unitRation = 100f;
        for (int i = 0; i <= ratioY; i += 5) {
            float value = i / unitRation;
            yDataList.add(value + "in");
        }
        invalidate();
    }

    public void setYDataListMm() {
        ratioY = 25;
        yDataList.clear();
        unitRation = 10f;
        for (int i = 0; i <= ratioY; i += 10) {
            float value = i / unitRation;
            yDataList.add(value + "mm");
        }
        invalidate();
    }

    /**
     * 设置纵坐标显示刻度
     * 必须是9个
     *
     * @param unit 降雨单位,默认in
     */
    public void setYDataListUnit(String unit) {
        switch (unit) {
            case "mm":
                setYDataListMm();
                break;
            case "in":
                setYDataListIn();
                break;
        }
    }

    /**
     * 初始化画笔
     *
     * @linpaint 线条画笔
     */
    private void initPaint() {
        //画线的画笔
        linePaint = new Paint();
        linePaint.setColor(Color.parseColor("#1AFFFFFF"));
        linePaint.setAntiAlias(true);
        linePaint.setTextSize(dp2px(getContext(), 12));
        linePaint.setStrokeWidth(dp2px(getContext(), 1));

        verticalDividerLinePaint = new Paint();
        verticalDividerLinePaint.setColor(Color.parseColor("#FF58FFBE"));
        verticalDividerLinePaint.setAntiAlias(true);
        verticalDividerLinePaint.setStrokeWidth(dp2px(getContext(), 2));

        verticalDividerLinePaint2 = new Paint();
        verticalDividerLinePaint2.setColor(Color.parseColor("#FF58FFBE"));
        verticalDividerLinePaint2.setAntiAlias(true);
        verticalDividerLinePaint2.setStrokeWidth(dp2px(getContext(), 2));


        //文字的画笔
        textPaint = new Paint();
        textPaint.setColor(Color.WHITE);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(dp2px(getContext(), 14));

        buttomHeiht = dp2px(35);//线距离底部高度

        mColumnPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mColumnPaint.setColor(Color.parseColor("#FF58FFBE"));
        mColumnPaint.setStrokeWidth(dp2px(getContext(), 2));
        mColumnPaint.setStyle(Paint.Style.FILL);
        mColumnPath = new Path();

        topImagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        sumHeight = getMeasuredHeight();
        sumWidth = getMeasuredWidth();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

    }

    private void measure() {
        String text = "min";
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        // 总共分几个高度
        oneHeight = (sumHeight - buttomHeiht) / ratioY;
        TOP_MARGIN = rect.height();
        oneWidth = (sumWidth - RIGHT_MARGIN - LEFT_WIDTH) / HOR_SIZE;
    }

    int RIGHT_MARGIN = dp2px(40);
    int LEFT_WIDTH = dp2px(15);//距离左边宽度
    int TOP_MARGIN = dp2px(13);

    private final int HOR_SIZE = 25;
    private float topLineY = 0;//网格顶部的Y坐标
    private float bottomLineY = 0;//网格底部的Y坐标

    /**
     * 设置数据
     * WindLineData 里的所有集合数量必须是25个 从00点  到24点
     */
    public void setWindData(RainData data) {
        this.mPrecipitationList = data.getPrecipitation();
        this.mDateTimeList = data.getDateTimeList();
        this.topImageList = data.getWeatherImageResList();
        this.weatherEnableList = data.getWeatherIsEnableList();
        this.mCodeList = data.getCodeList();
        beforeIndicateIndex = -1;
        afterIndicateIndex = -1;
        for (int i = 0; i < this.weatherEnableList.size(); i++) {
            Boolean thisValue = this.weatherEnableList.get(i);
            // 当前为false 下一个为true
            if (!thisValue && i != this.weatherEnableList.size() - 1 && this.weatherEnableList.get(i + 1)) {
                beforeIndicateIndex = i + 1;
            }
            // 当前为true 下一个为false
            if (thisValue && i != this.weatherEnableList.size() - 1 && !this.weatherEnableList.get(i + 1)) {
                afterIndicateIndex = i;
            }
        }
        if (this.mPrecipitationList != null && this.mPrecipitationList.size() > 0 && mDateTimeList != null && mDateTimeList.size() > 0) {
            invalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mPrecipitationList == null || mDateTimeList == null) return;
        measure();
        toGetXy();//获取x和y的坐标
        toDrawColumn(canvas);
        drawWindImg(canvas);
        drawTouchLine(canvas);
    }

    private void toGetXy() {
        xyList = new ArrayList<>();
        for (int i = 0; i < mPrecipitationList.size(); i++) {
//            float x = i * (HOR_SIZE * 1.0f / (mPrecipitationList.size() - 1)) * oneWidth;

            float x = i * oneWidth;
            float prec = mPrecipitationList.get(i) * unitRation;//降雨量为0.01mm的
            float y = (sumHeight - (oneHeight * prec));
            xyList.add(new PointF(x + LEFT_WIDTH, y - buttomHeiht));
        }
    }

    /**
     */
    private void toDrawColumn(Canvas canvas) {
        if (xyList == null || xyList.size() == 0) {
            return;
        }
        drawLine(canvas);
        List<PointF> NewPoints = new ArrayList<>(xyList);
        baseLinePath = new Path();
        baseLinePath.moveTo(NewPoints.get(0).x, NewPoints.get(0).y);
        for (int i = 1; i < NewPoints.size(); i++) {
            PointF p = NewPoints.get(i);
            int colorRes = RainUtil.getWeatherColor(mCodeList.get(i),mDateTimeList.get(i));
            int color = getResources().getColor(colorRes);
            drawColumn(canvas, p, color);
        }
        //绘制底部文字
        for (int i = 0; i < NewPoints.size(); i++) {
            float x = NewPoints.get(i).x;
            Rect rectf = new Rect();
            textPaint.setColor(Color.WHITE);
            textPaint.getTextBounds(mDateTimeList.get(i), 0, mDateTimeList.get(i).length(), rectf);
            // 只画 00  06 12 18 这几个标志
            if (i % 6 == 0 && i != NewPoints.size() - 1) {
                canvas.drawText(mDateTimeList.get(i), x - rectf.width() / 2f, sumHeight - dp2px(18), textPaint);//画最下方的字体
            }
        }
    }

    private Path touchLinePath = new Path();

    /**
     * 画触摸线
     * @param canvas
     */
    private void drawTouchLine(Canvas canvas) {
        if (currentTouchX == -1) {
            verticalDividerLinePaint.setColor(Color.parseColor("#FF58FFBE"));
            canvasDividerLine(canvas);
            return;
        }
        verticalDividerLinePaint.setColor(Color.parseColor("#7E58FFBE"));
        canvas.drawLine(currentTouchX,
                topLineY,
                currentTouchX,
                bottomLineY,
                verticalDividerLinePaint2);
    }

    /**
     * 网格分割线
     * 书写右侧垂直标注动文字
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        textPaint.setColor(Color.parseColor("#B3FFFFFF"));
        int dividerTextStartX = (int) sumWidth - RIGHT_MARGIN;
        int startX = 0;
        int stopX = 0;
        if (xyList.size() != 0) {
            startX = (int) xyList.get(0).x;
            stopX = (int) xyList.get(xyList.size() - 1).x;
        }
//        String text = "0";
        float ratioHeight = (sumHeight - buttomHeiht) / yDataList.size();
        topLineY = buttomHeiht;
        bottomLineY = ratioHeight * (yDataList.size() - 1) + buttomHeiht;
        //画横线
        for (int i = 0; i < yDataList.size(); i++) {//因为横线是从y轴开始画的,开始Y坐标为0,要留出画图片的空间,这里随便取一个值,暂时取buttomHeiht
            String text = yDataList.get(yDataList.size() - i - 1);//倒叙取值
            float y5 = ratioHeight * i + buttomHeiht;//可绘制高度除于总横线的数量,得到每一份绘制高度,然后乘以i,得到每一条横线的高度
            canvas.drawText(text, dividerTextStartX, y5, textPaint);
            canvas.drawLine(startX, y5, stopX, y5, linePaint);
        }

        // 画竖线
        for (int i = 0; i < xyList.size(); i++) {
            //  // 补充最后一条线
            if (i % 6 == 0 || i == xyList.size() - 1) {  // 一共出4条竖线 一共24个数据
                canvas.drawLine(xyList.get(i).x, topLineY, xyList.get(i).x, bottomLineY, linePaint);
            }
        }
        Log.e("YM---->","drawLine-->beforeIndicateIndex:"+beforeIndicateIndex+"--->afterIndicateIndex:"+afterIndicateIndex);
        canvasDividerLine(canvas);
    }
    private void canvasDividerLine(Canvas canvas){
        //        if (currentTouchX != -1) {//手指滑动过程中,该分割线不显示
//            return;
//        }
        //这里绘制左侧阴影部分与右侧非阴影部分的分割线
        if (beforeIndicateIndex != -1) {
            canvas.drawLine(xyList.get(beforeIndicateIndex).x, topLineY, xyList.get(beforeIndicateIndex).x, bottomLineY, verticalDividerLinePaint);
        }
        if (afterIndicateIndex != -1) {
            canvas.drawLine(xyList.get(afterIndicateIndex).x, topLineY, xyList.get(afterIndicateIndex).x, bottomLineY, verticalDividerLinePaint);
        }
    }
    public int dp2px(float dp) {
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }
    private final Map<Integer, Bitmap> bitmaps = new HashMap<>();

    @SuppressLint("SuspiciousIndentation")
    public void drawWindImg(Canvas canvas) {
        for (int i = 0; i < topImageList.size(); i++) {
            if (i % 2 != 0) {
                // 隔一个画一个
                continue;
            }
            Bitmap bmp;
            if (bitmaps.containsKey(topImageList.get(i))) {
                bmp = bitmaps.get(topImageList.get(i));
            } else {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                bmp = BitmapFactory.decodeResource(getResources(), topImageList.get(i));
                bmp = Bitmap.createScaledBitmap(bmp, dp2px(24), dp2px(24), false);
                bitmaps.put(topImageList.get(i), bmp);
            }
            // 有效值才渲染图标
            if (i >= beforeIndicateIndex && (afterIndicateIndex == -1 || i <= afterIndicateIndex)) {
                canvas.drawBitmap(bmp, xyList.get(i).x - bmp.getWidth() / 2f, 0, topImagePaint);
            }
        }
    }

    //画一个顶部为圆角的矩形
    public void drawColumn(Canvas canvas, PointF pointF, int color) {
        float round = dp2px(4);
        float width = dp2px(6);
        float startX = pointF.x - oneWidth / 2; //减横向半个位置的宽度,否则会超过画布
        float endX = startX + width;
        float startY = pointF.y;
        float endY = bottomLineY;
        mColumnPaint.setColor(color);
        if (startY == endY) { //如果开始的Y坐标与结束的Y坐标相同,说明今天是晴天
            return;
        }else if (startY + round > endY){//开始的Y坐标加上半圆的Y坐标 大于 结束的Y坐标,说明有雨,但是雨不大, 这时候稍微画一点痕迹表示有雨
            canvas.drawRoundRect(startX, endY - round, endX, endY, round, round, mColumnPaint);
            return;
        }
        mColumnPath.reset();
        mColumnPath.moveTo(startX + round, startY); // 移动到左上角圆角的起点
        mColumnPath.lineTo(endX - round, startY); // 绘制顶边
        mColumnPath.quadTo(endX, startY, endX, startY + round); // 绘制右上角圆角
        mColumnPath.lineTo(startX + width, endY); // 绘制右边
        mColumnPath.lineTo(startX, endY); // 绘制底边
        mColumnPath.lineTo(startX, startY + round); // 绘制左边
        mColumnPath.quadTo(startX, startY, startX + round, startY); // 绘制左上角圆角
        mColumnPath.close();
        canvas.drawPath(mColumnPath, mColumnPaint);
    }

    public int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    public float dp2px(Resources resources, float dp) {
        final float scale = resources.getDisplayMetrics().density;
        return dp * scale + 0.5f;
    }

    public float sp2px(Resources resources, float sp) {
        final float scale = resources.getDisplayMetrics().scaledDensity;
        return sp * scale;
    }

    private int getColumnColor(String code){
        return R.color.color_4D898989;
    }

    //    private Boolean isTouching = false;
    private float currentTouchX = -1;
    private float lastTouchX = -1;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (xyList == null) {
            return super.onTouchEvent(event);
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                return true;
            case MotionEvent.ACTION_MOVE:
                currentTouchX = event.getX();
                if (beforeIndicateIndex != -1) {
                    currentTouchX = Math.max(xyList.get(beforeIndicateIndex).x, currentTouchX);
                }
                if (afterIndicateIndex != -1) {
                    currentTouchX = Math.min(xyList.get(afterIndicateIndex).x, currentTouchX);
                }
                if (currentTouchX <= xyList.get(0).x) {
                    currentTouchX = xyList.get(0).x;
                } else if (currentTouchX >= xyList.get(xyList.size() - 1).x) {
                    currentTouchX = xyList.get(xyList.size() - 1).x;
                }

                touchLinePath.reset();
                touchLinePath.moveTo(currentTouchX, (sumHeight - (oneHeight * 0)) - buttomHeiht);
                touchLinePath.lineTo(currentTouchX, (sumHeight - (oneHeight * yDataList.size())) - buttomHeiht);
                touchLinePath.lineTo(currentTouchX + 3, (sumHeight - (oneHeight * yDataList.size())) - buttomHeiht);
                touchLinePath.moveTo(currentTouchX + 3, (sumHeight - (oneHeight * 0)) - buttomHeiht);
                touchLinePath.close();

                // 限制更新频率
                if (Math.abs(lastTouchX - currentTouchX) >= 6) {
                    lastTouchX = currentTouchX;
                    if (onTouchPositionChange != null) {
                        onTouchPositionChange.onChange((currentTouchX - xyList.get(0).x) / (xyList.get(xyList.size() - 1).x - xyList.get(0).x));
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                getParent().requestDisallowInterceptTouchEvent(false);
                currentTouchX = -1;
                if (onTouchPositionChange != null) {
                    onTouchPositionChange.onTouchUp();
                }
                invalidate();
                break;

        }
//        invalidate();
        return super.onTouchEvent(event);
    }

    public static class RainData {
        // 天气数据是否可用
        private List<Boolean> weatherIsEnableList;
        //降水
        private List<Float> precipitation;
        // 时间
        private List<String> dateTimeList;

        private List<String> codeList;
        // 天气图标
        private List<Integer> weatherImageResList;

        public List<Boolean> getWeatherIsEnableList() {
            return weatherIsEnableList;
        }

        public List<Float> getPrecipitation() {
            return precipitation;
        }

        public List<String> getDateTimeList() {
            return dateTimeList;
        }

        public List<Integer> getWeatherImageResList() {
            return weatherImageResList;
        }

        public void setWeatherIsEnableList(List<Boolean> weatherIsEnableList) {
            this.weatherIsEnableList = weatherIsEnableList;
        }

        public void setPrecipitation(List<Float> precipitation) {
            this.precipitation = precipitation;
        }

        public void setDateTimeList(List<String> dateTimeList) {
            this.dateTimeList = dateTimeList;
        }

        public void setWeatherImageResList(List<Integer> winImageResList) {
            this.weatherImageResList = winImageResList;
        }

        public List<String> getCodeList() {
            return codeList;
        }

        public void setCodeList(List<String> codeList) {
            this.codeList = codeList;
        }
    }

    private OnTouchPositionChange onTouchPositionChange;

    public void setOnTouchPositionChange(OnTouchPositionChange onTouchPositionChange) {
        this.onTouchPositionChange = onTouchPositionChange;
    }

    public interface OnTouchPositionChange {
        // 进度从0到1 0代表0点 1 代表24点
        void onChange(float position);

        void onTouchUp();
    }
}

三、风向代码

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.StringRes;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wangchang on 2019/4/29.
 */
public class WindWaveLine extends View {

    private float sumHeight;//总控件的高度
    private float sumWidth;// 总空间的宽度
    private float maxTime;//最大的时间 用来划分单位的 最小就是20 X1.2是为了给上方和下方预留空间
    private Paint linePaint;//网格横线线的画笔
    private Paint verticalDividerLinePaint;//网格线中垂直分割线的画笔
    private Paint verticalDividerLinePaint2;//网格线中第二条垂直分割线的画笔
    private Paint mPaint;//曲线画笔
    private Paint circlePaint;//第一个圆点画笔,该圆点不会动
    private Paint circlePaint2;//第二个圆点画笔, 长按后会动
    private Paint textPaint;//文字的画笔
    private Paint topImagePaint;//顶部风向图标的画笔
    private List<Float> mWindSpeedList;//风速
    private List<String> mDateTimeList;//底部的时间
    private float oneHeight; //每一个小段所要分成的高
    private float oneWidth;//每一个小段所要分成的宽
    private float buttomHeiht; //给底部一排日期预留出的时间
    private Path baseLinePath;//折线路径
    private float smoothness = 0.27f; //折线的弯曲率
    private Paint baseLeftShadow;//折线下的阴影的画笔
    private Paint baseShadow;//折线下的阴影的画笔
    private List<PointF> xyList;//储存定好的坐标点的集合

    private List<Integer> topImageList = new ArrayList<>();
    private List<Boolean> windEnableList = new ArrayList<>();
    // Y轴显示的列表 必须是9个
    private List<String> yDataList = new ArrayList<>();


    // 设置进来的数据是否有效
    // 例:windSpeedIsEnableList  false false  true true  true true true false false
    // firstIndicateIndex 为2  secondIndicateIndex = 6
    private int beforeIndicateIndex = -1;
    private int afterIndicateIndex = -1;

    private WindLWaveineData windLWaveineData;

    private String noDataDesc = "";// 左侧无数据的文字描述

    public WindWaveLine(Context context) {
        super(context);
        initPaint();
        initYDataList();
    }

    public WindWaveLine(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
        initYDataList();
    }


    private void initYDataList() {
        if (yDataList.isEmpty()) {
            for (int i = 0; i < 17; i++) {
                if (i % 2 == 0) {
                    yDataList.add(i + "");
                }
            }
        }
    }

    public void setYDataListMph() {
        yDataList.clear();
        for (int i = 0; i <= 40; i += 5) {
            yDataList.add(i + "");
        }
        invalidate();
    }

    public void setYDataListKmh() {
        yDataList.clear();
        for (int i = 0; i <= 80; i += 10) {
            yDataList.add(i + "");
        }
        invalidate();
    }

    /**
     * 设置纵坐标显示刻度
     * 必须是9个
     *
     * @param unit 风速单位,默认m/s
     */
    public void setYDataListUnit(String unit) {
        switch (unit) {
            case "km/h":
                setYDataListKmh();
                break;
            case "mph":
                setYDataListMph();
                break;
        }
    }

    public void setNoDataDesc(String noDataDesc){
        this.noDataDesc = noDataDesc;
        invalidate();
    }
    public void setNoDataDesc(@StringRes int noDataDescId){
        this.noDataDesc = getResources().getString(noDataDescId);
        invalidate();
    }
    /**
     * 初始化画笔
     *
     * @linpaint 线条画笔
     */
    private void initPaint() {
        //画线的画笔
        linePaint = new Paint();
        linePaint.setColor(Color.parseColor("#1AFFFFFF"));
        linePaint.setAntiAlias(true);
        linePaint.setTextSize(dp2px(getContext(), 12));
        linePaint.setStrokeWidth(dp2px(getContext(), 1));

        verticalDividerLinePaint = new Paint();
        verticalDividerLinePaint.setColor(Color.parseColor("#1AFFFFFF"));
        verticalDividerLinePaint.setAntiAlias(true);
        verticalDividerLinePaint.setStrokeWidth(dp2px(getContext(), 2));

        verticalDividerLinePaint2 = new Paint();
        verticalDividerLinePaint2.setColor(Color.parseColor("#FF2D9BFE"));
        verticalDividerLinePaint2.setAntiAlias(true);
        verticalDividerLinePaint2.setStrokeWidth(dp2px(getContext(), 2));


        //文字的画笔
        textPaint = new Paint();
        textPaint.setColor(Color.WHITE);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(dp2px(getContext(), 14));

        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setColor(Color.parseColor("#FF3EFFD4"));
        circlePaint.setStrokeWidth(dp2px(getContext(), 2));
        circlePaint.setStyle(Paint.Style.STROKE);

        circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint2.setColor(Color.parseColor("#FF0061E2"));
        circlePaint2.setStyle(Paint.Style.FILL);

        baseLeftShadow = new Paint();
        baseLeftShadow.setAntiAlias(true);
        baseLeftShadow.setColor((Color.WHITE & 0x40FFFFFF) | 0x10000000);
        baseLeftShadow.setStyle(Paint.Style.FILL);

        baseShadow = new Paint();
        baseShadow.setAntiAlias(true);
        baseShadow.setColor((Color.WHITE & 0x40FFFFFF) | 0x10000000);
        baseShadow.setStyle(Paint.Style.FILL);

        buttomHeiht = dp2px(35);//线距离底部高度

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.parseColor("#FF58FFBE"));
        mPaint.setStrokeWidth(dp2px(getContext(), 2));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
//        mPaint.setColor(Color.parseColor("#17CAAA"));
        topImagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        sumHeight = getMeasuredHeight();
        sumWidth = getMeasuredWidth();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

    }

    private void measure() {
        maxTime = 16;//最大分数为100
        for (int i = 0; i < mWindSpeedList.size(); i++) {
            if (maxTime <= mWindSpeedList.get(i)) {
                maxTime = mWindSpeedList.get(i);
            }
        }
//        if (maxTime < 20) {
//            maxTime = 20;
//        }
        String text = "min";
        Rect rect = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), rect);
        // 1个time几个高度
        oneHeight = ((sumHeight - buttomHeiht - 2 * rect.height()) / maxTime);
        TOP_MARGIN = rect.height();
        oneWidth = (sumWidth - RIGHT_MARGIN - LEFT_WIDTH) / HOR_SIZE;
    }

    int RIGHT_MARGIN = dp2px(40);
    int LEFT_WIDTH = dp2px(13);//距离右边宽度
    int TOP_MARGIN = dp2px(13);

    // 横向分成100份
    private int HOR_SIZE = 100;

    /**
     * 设置数据
     * WindLineData 里的所有集合数量必须是25个 从00点  到24点
     */
    public void setWindData(WindLWaveineData windLWaveineData) {
        this.mWindSpeedList = windLWaveineData.getWindSpeed();
        this.mDateTimeList = windLWaveineData.getDateTimeList();
        this.topImageList = windLWaveineData.getWinImageResList();
        this.windEnableList = windLWaveineData.getWindSpeedIsEnableList();
        beforeIndicateIndex = -1;
        afterIndicateIndex = -1;
        for (int i = 0; i < this.windEnableList.size(); i++) {
            Boolean thisValue = this.windEnableList.get(i);
            // 当前为false 下一个为true
            if (!thisValue && i != this.windEnableList.size() - 1 && this.windEnableList.get(i + 1)) {
                beforeIndicateIndex = i + 1;
            }
            // 当前为true 下一个为false
            if (thisValue && i != this.windEnableList.size() - 1 && !this.windEnableList.get(i + 1)) {
                afterIndicateIndex = i;
            }
        }
        if (this.mWindSpeedList != null && this.mWindSpeedList.size() > 0 && mDateTimeList != null && mDateTimeList.size() > 0) {
            invalidate();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mWindSpeedList == null || mDateTimeList == null) return;
        maxTime = getMaxTime(mWindSpeedList);
        measure();
        toGetXy();//获取x和y的坐标

        toDrawLine(canvas);
        drawWindImg(canvas);
        drawTouchLine(canvas);
        drawNoDataDesc(canvas);
    }

    private void toGetXy() {

        xyList = new ArrayList<>();
        for (int i = 0; i < mWindSpeedList.size(); i++) {
//            (HOR_SIZE*1.0f/timeList.size()  分成100份 每份有多长
            float x = oneWidth + i * (HOR_SIZE * 1.0f / (mWindSpeedList.size() - 1)) * oneWidth;
            float time = mWindSpeedList.get(i);//每点时间
            float y = (sumHeight - (oneHeight * time));
            xyList.add(new PointF(x + LEFT_WIDTH, y - buttomHeiht));
        }
    }


    // 左侧没有数据的区域画上蒙层
    LinearGradient mOverlyShader = new LinearGradient(0, sumHeight - maxTime * oneHeight - dp2px(4),
            0,
            sumHeight,
            new int[]{Color.parseColor("#00F1F9FE"), Color.parseColor("#FFC4E0F2")},
            new float[]{0f, 1f},
            Shader.TileMode.REPEAT);

    /**
     * 画线
     */
    private void toDrawLine(Canvas canvas) {
        if (xyList == null || xyList.size() == 0) {
            return;
        }
        drawLine(canvas);
        List<PointF> NewPoints = new ArrayList<>();
        NewPoints.addAll(xyList);
        float lX = 0;
        float lY = 0;
        baseLinePath = new Path();
        baseLinePath.moveTo(NewPoints.get(0).x, NewPoints.get(0).y);
        for (int i = 1; i < NewPoints.size(); i++) {
            PointF p = NewPoints.get(i);
            PointF firstPointF = NewPoints.get(i - 1);
            float x1 = firstPointF.x + lX;
            float y1 = firstPointF.y + lY;

            PointF secondPointF = NewPoints.get(i + 1 < NewPoints.size() ? i + 1 : i);
            lX = (secondPointF.x - firstPointF.x) / 2 * smoothness;
            lY = (secondPointF.y - firstPointF.y) / 2 * smoothness;
            float x2 = p.x - lX;
            float y2 = p.y - lY;
            if (y1 == p.y) {
                y2 = y1;
            }
            baseLinePath.cubicTo(x1, y1, x2, y2, p.x, p.y);
        }

        canvas.save();
//        drawLeftShadow(canvas);
        drawLeftMask(canvas);
        canvas.drawPath(baseLinePath, mPaint);

        drawArea(canvas, NewPoints);

        canvas.restore();

        //绘制底部文字
        for (int i = 0; i < NewPoints.size(); i++) {
            float x = NewPoints.get(i).x;
            float y = NewPoints.get(i).y;
            Float time = mWindSpeedList.get(i);
            String mThumbText = String.valueOf(time);
//            Rect rect = new Rect();
//            linePaint.getTextBounds(mThumbText, 0, mThumbText.length(), rect);
//            canvas.drawText(mThumbText, x - rect.width() / 2, y - dp2px(6), linePaint);//画最上面字体
//            canvas.drawCircle(x, y, dp2px(3), circlePaint);
//            canvas.drawCircle(x, y, dp2px(2), circlePaint2);
            Rect rectf = new Rect();
            textPaint.setColor(Color.WHITE);
            textPaint.getTextBounds(mDateTimeList.get(i), 0, mDateTimeList.get(i).length(), rectf);
            // 只画 00  06 12 18 这几个标志
            if (i % 6 == 0 && i != NewPoints.size() - 1) {
                canvas.drawText(mDateTimeList.get(i), x - rectf.width() / 2, sumHeight - dp2px(18), textPaint);//画最下方的字体
            }
        }
    }

    //绘制一个单层的遮罩层
    private void drawLeftMask(Canvas canvas){
        baseLeftShadow.setColor(Color.parseColor("#9A10232F"));
        //绘制左侧阴影渐变色
        if (beforeIndicateIndex != -1 && afterIndicateIndex != -1) {
            canvas.drawRect(xyList.get(0).x, (sumHeight - (oneHeight * 16)) - buttomHeiht,
                    xyList.get(beforeIndicateIndex).x,
                    (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            canvas.drawRect(xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)) - buttomHeiht,
                    xyList.get(xyList.size() - 1).x, (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            canvas.clipRect(xyList.get(beforeIndicateIndex).x, (sumHeight - (oneHeight * 0)) - buttomHeiht,
                    xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)));

        } else if (beforeIndicateIndex != -1) {
            canvas.drawRect(xyList.get(0).x, (sumHeight - (oneHeight * 16)) - buttomHeiht, xyList.get(beforeIndicateIndex).x,
                    (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            canvas.clipRect(xyList.get(beforeIndicateIndex).x, (sumHeight - (oneHeight * 0)) - buttomHeiht,
                    sumWidth, (sumHeight - (oneHeight * 16)));
        } else if (afterIndicateIndex != -1) {
            canvas.drawRect(xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)) - buttomHeiht,
                    xyList.get(xyList.size() - 1).x, (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            canvas.clipRect(0, (sumHeight - (oneHeight * 0)) - buttomHeiht,
                    xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)));
        }
    }

    private void drawLeftShadow(Canvas canvas){
        //绘制左侧阴影渐变色
        if (beforeIndicateIndex != -1 && afterIndicateIndex != -1) {
            mOverlyShader = new LinearGradient(xyList.get(0).x, 0f, xyList.get(beforeIndicateIndex).x, 0f,
                    new int[]{Color.parseColor("#00F1F9FE"), Color.parseColor("#FFC4E0F2")},
                    new float[]{0f, 1f},
                    Shader.TileMode.REPEAT);
            baseLeftShadow.setShader(mOverlyShader);
            canvas.drawRect(xyList.get(0).x, (sumHeight - (oneHeight * 16)) - buttomHeiht,
                    xyList.get(beforeIndicateIndex).x,
                    (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            mOverlyShader = new LinearGradient(xyList.get(0).x, 0f, xyList.get(beforeIndicateIndex).x, 0f,
                    new int[]{Color.parseColor("#FFC4E0F2"), Color.parseColor("#00F1F9FE")},
                    new float[]{0f, 1f},
                    Shader.TileMode.REPEAT);
            baseLeftShadow.setShader(mOverlyShader);
            canvas.drawRect(xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)) - buttomHeiht,
                    xyList.get(xyList.size() - 1).x, (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            canvas.clipRect(xyList.get(beforeIndicateIndex).x, (sumHeight - (oneHeight * 0)) - buttomHeiht,
                    xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)));

        } else if (beforeIndicateIndex != -1) {
            mOverlyShader = new LinearGradient(xyList.get(0).x, 0f, xyList.get(beforeIndicateIndex).x, 0f,
                    new int[]{Color.parseColor("#00F1F9FE"), Color.parseColor("#FFC4E0F2")},
                    new float[]{0f, 1f},
                    Shader.TileMode.REPEAT);
            baseLeftShadow.setShader(mOverlyShader);
            canvas.drawRect(xyList.get(0).x, (sumHeight - (oneHeight * 16)) - buttomHeiht, xyList.get(beforeIndicateIndex).x,
                    (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            canvas.clipRect(xyList.get(beforeIndicateIndex).x, (sumHeight - (oneHeight * 0)) - buttomHeiht,
                    sumWidth, (sumHeight - (oneHeight * 16)));
        } else if (afterIndicateIndex != -1) {
            mOverlyShader = new LinearGradient(xyList.get(0).x, 0f, xyList.get(beforeIndicateIndex).x, 0f,
                    new int[]{Color.parseColor("#FFC4E0F2"), Color.parseColor("#00F1F9FE")},
                    new float[]{0f, 1f},
                    Shader.TileMode.REPEAT);
            baseLeftShadow.setShader(mOverlyShader);
            canvas.drawRect(xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)) - buttomHeiht,
                    xyList.get(xyList.size() - 1).x, (sumHeight - (oneHeight * 0) - buttomHeiht), baseLeftShadow);

            canvas.clipRect(0, (sumHeight - (oneHeight * 0)) - buttomHeiht,
                    xyList.get(afterIndicateIndex).x, (sumHeight - (oneHeight * 16)));
        }
    }

    private Path touchLinePath = new Path();

    /**
     * 画触摸线
     *
     * @param canvas
     */
    private void drawTouchLine(Canvas canvas) {
        if (currentTouchX == -1) {
            return;
        }
        canvas.drawLine(currentTouchX,
                (sumHeight - (oneHeight * 0)) - buttomHeiht, currentTouchX,
                (sumHeight - (oneHeight * 16)) - buttomHeiht,
                verticalDividerLinePaint2);
        Path tempPath = new Path();
        RectF rectF = new RectF();
        boolean isIntersect = tempPath.op(touchLinePath,baseLinePath, Path.Op.INTERSECT);
        tempPath.computeBounds(rectF, true);
        if (isIntersect) {
            canvas.drawCircle(rectF.left, rectF.top, dp2px(3.5f), circlePaint2);
        }
    }

    /**
     * 网格分割线
     * 书写右侧垂直标注动文字
     *
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        textPaint.setColor(Color.parseColor("#B3FFFFFF"));
        int dividerTextStartX = (int) sumWidth - RIGHT_MARGIN + dp2px(12);
        //
        float y16 = (sumHeight - (oneHeight * 16));
        int startX = 0;
        int stopX = 0;
        if (xyList.size() != 0) {
            startX = (int) xyList.get(0).x;
            stopX = (int) xyList.get(xyList.size() - 1).x;
        }
//        String text = "0";
        String text = yDataList.get(0);
        float y0 = (sumHeight - (oneHeight * 0));
        canvas.drawText(text, dividerTextStartX, sumHeight - buttomHeiht, textPaint);
        canvas.drawLine(startX, y0 - buttomHeiht, stopX, y0 - buttomHeiht, linePaint);

        //60
//        text = "2";
        text = yDataList.get(1);
        float y2 = (sumHeight - (oneHeight * 2));
        canvas.drawText(text, dividerTextStartX, y2 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y2 - buttomHeiht, stopX, y2 - buttomHeiht, linePaint);

//        text = "4";
        text = yDataList.get(2);
        float y4 = (sumHeight - (oneHeight * 4));
        canvas.drawText(text, dividerTextStartX, y4 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y4 - buttomHeiht, stopX, y4 - buttomHeiht, linePaint);


//        text = "6";
        text = yDataList.get(3);
        float y6 = (sumHeight - (oneHeight * 6));
        canvas.drawText(text, dividerTextStartX, y6 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y6 - buttomHeiht, stopX, y6 - buttomHeiht, linePaint);


//        text = "8";
        text = yDataList.get(4);
        float y8 = (sumHeight - (oneHeight * 8));
        canvas.drawText(text, dividerTextStartX, y8 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y8 - buttomHeiht, stopX, y8 - buttomHeiht, linePaint);

//        text = "10";
        text = yDataList.get(5);
        float y10 = (sumHeight - (oneHeight * 10));
        canvas.drawText(text, dividerTextStartX, y10 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y10 - buttomHeiht, stopX, y10 - buttomHeiht, linePaint);

//        text = "12";
        text = yDataList.get(6);
        float y12 = (sumHeight - (oneHeight * 12));
        canvas.drawText(text, dividerTextStartX, y12 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y12 - buttomHeiht, stopX, y12 - buttomHeiht, linePaint);

//        text = "14";
        text = yDataList.get(7);
        float y14 = (sumHeight - (oneHeight * 14));
        canvas.drawText(text, dividerTextStartX, y14 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y14 - buttomHeiht, stopX, y14 - buttomHeiht, linePaint);

//        text = "16";
        text = yDataList.get(8);
        canvas.drawText(text, dividerTextStartX, y16 - buttomHeiht, textPaint);
        canvas.drawLine(startX, y16 - buttomHeiht, stopX, y16 - buttomHeiht, linePaint);


        // 画竖线
        for (int i = 0; i < xyList.size(); i++) {
            //  // 补充最后一条线
            if (i % 6 == 0 || i == xyList.size() - 1) {  // 一共出4条竖线 一共24个数据
                canvas.drawLine(xyList.get(i).x, y0 - buttomHeiht, xyList.get(i).x, y16 - buttomHeiht, linePaint);
            }

        }
        if (currentTouchX != -1){//手指滑动过程中,该分割线不显示
            return;
        }
        //这里绘制左侧阴影部分与右侧非阴影部分的分割线
        if (beforeIndicateIndex != -1) {
            canvas.drawLine(xyList.get(beforeIndicateIndex).x, y0 - buttomHeiht, xyList.get(beforeIndicateIndex).x, y16 - buttomHeiht, verticalDividerLinePaint);
            canvas.drawCircle(xyList.get(beforeIndicateIndex).x, xyList.get(beforeIndicateIndex).y, dp2px(1.5f), circlePaint);
        }
        if (afterIndicateIndex != -1) {
            canvas.drawLine(xyList.get(afterIndicateIndex).x, y0 - buttomHeiht, xyList.get(afterIndicateIndex).x, y16 - buttomHeiht, verticalDividerLinePaint);
            canvas.drawCircle(xyList.get(afterIndicateIndex).x, xyList.get(afterIndicateIndex).y, dp2px(1.5f), circlePaint);
        }
    }


    /**
     * 阴影层叠
     * 右侧折线底部阴影渐变
     * @param canvas
     * @param Points
     */
    private void drawArea(Canvas canvas, List<PointF> Points) {
        // sumHeight-maxTime*oneHeight-dp2px(4)  -dp4是为了调整误差无特殊用处
        LinearGradient mShader =
                new LinearGradient(0, sumHeight - maxTime * oneHeight - dp2px(4),
                0, sumHeight,
                new int[]{Color.parseColor("#FF58FFBE"),
                        Color.parseColor("#0058FFBE")},
                new float[]{0f, 1f}, Shader.TileMode.REPEAT);
        baseShadow.setShader(mShader);
        if (Points.size() > 0 && xyList != null && xyList.size() > 0) {
            baseLinePath.lineTo(xyList.get(Points.size() - 1).x, sumHeight - buttomHeiht);
            baseLinePath.lineTo(xyList.get(0).x, sumHeight - buttomHeiht);
            baseLinePath.close();
            canvas.drawPath(baseLinePath, baseShadow);
        }

    }

    public int dp2px(float dp) {
        final float scale = this.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    /**
     * 取出时间里面的最大的一个用来计算总长度
     *
     * @param timeList
     * @return
     */
    public float getMaxTime(List<Float> timeList) {
        maxTime = 0;
        for (int i = 0; i < timeList.size(); i++) {
            if (maxTime < timeList.get(i)) {
                maxTime = timeList.get(i);
            }
        }
        if (maxTime <= 10) {
            maxTime = 10;
        }
//        maxTime *= 1.2;
        return maxTime;
    }

    private Map<Integer, Bitmap> bitmaps = new HashMap<>();

    @SuppressLint("SuspiciousIndentation")
    public void drawWindImg(Canvas canvas) {
        for (int i = 0; i < topImageList.size(); i++) {
            if (i % 2 != 0) {
                // 隔一个画一个
                continue;
            }
            Bitmap bmp;
            if (bitmaps.containsKey(topImageList.get(i))) {
                bmp = bitmaps.get(topImageList.get(i));
            } else {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                bmp = BitmapFactory.decodeResource(getResources(), topImageList.get(i));
                bmp = Bitmap.createScaledBitmap(bmp, dp2px(24), dp2px(24), false);
                bitmaps.put(topImageList.get(i), bmp);
            }
            // 有效值才渲染风向图标
            if (i >= beforeIndicateIndex && (afterIndicateIndex == -1 || i <= afterIndicateIndex)) {
                canvas.drawBitmap(bmp, xyList.get(i).x - bmp.getWidth() / 2f, 0, topImagePaint);
            }
        }
    }

    //绘制无数据的描述,该描述会在18点后显示
    //当时间为18点之后且没有数据时,显示该描述
    private void drawNoDataDesc(Canvas canvas){
        if (beforeIndicateIndex < 18) {
            return;
        }
        float firstX =  xyList.get(0).x; //00点
        float secondX =  xyList.get(18).x; //18点
        Rect rect = new Rect();
        textPaint.getTextBounds(noDataDesc, 0, noDataDesc.length(), rect);
        float textWidth = rect.width();
        float startX = (secondX - firstX - textWidth) / 2 + firstX ;
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(dp2px(getContext(), 12));
        if (noDataDesc != null && !noDataDesc.isEmpty()) {
            canvas.drawText(noDataDesc, startX, getHeight() / 2f, textPaint);
        }
    }

    public int dp2px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dp * scale + 0.5f);
    }

    public float dp2px(Resources resources, float dp) {
        final float scale = resources.getDisplayMetrics().density;
        return dp * scale + 0.5f;
    }

    public float sp2px(Resources resources, float sp) {
        final float scale = resources.getDisplayMetrics().scaledDensity;
        return sp * scale;
    }

    //    private Boolean isTouching = false;
    private float currentTouchX = -1;
    private float lastTouchX = -1;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (xyList == null) {
            return super.onTouchEvent(event);
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
//                currentTouchX=-1;
//                isTouching=true;
                getParent().requestDisallowInterceptTouchEvent(true);
                return true;
            case MotionEvent.ACTION_MOVE:
                currentTouchX = event.getX();
                if (beforeIndicateIndex != -1) {
                    currentTouchX = Math.max(xyList.get(beforeIndicateIndex).x, currentTouchX);
                }
                if (afterIndicateIndex != -1) {
                    currentTouchX = Math.min(xyList.get(afterIndicateIndex).x, currentTouchX);
                }
                if (currentTouchX <= xyList.get(0).x) {
                    currentTouchX = xyList.get(0).x;
                } else if (currentTouchX >= xyList.get(xyList.size() - 1).x) {
                    currentTouchX = xyList.get(xyList.size() - 1).x;
                }

                touchLinePath.reset();
                touchLinePath.moveTo(currentTouchX, (sumHeight - (oneHeight * 0)) - buttomHeiht);
                touchLinePath.lineTo(currentTouchX, (sumHeight - (oneHeight * 16)) - buttomHeiht);
                touchLinePath.lineTo(currentTouchX + 3, (sumHeight - (oneHeight * 16)) - buttomHeiht);
                touchLinePath.moveTo(currentTouchX + 3, (sumHeight - (oneHeight * 0)) - buttomHeiht);
                touchLinePath.close();

                // 限制更新频率
                if (Math.abs(lastTouchX - currentTouchX) >= 6) {
                    lastTouchX = currentTouchX;
                    if (onTouchPositionChange != null) {
                        onTouchPositionChange.onChange((currentTouchX - xyList.get(0).x) / (xyList.get(xyList.size() - 1).x - xyList.get(0).x));
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                getParent().requestDisallowInterceptTouchEvent(false);
                currentTouchX = -1;
                if (onTouchPositionChange != null) {
                    onTouchPositionChange.onTouchUp();
                }
                invalidate();
                break;

        }
//        invalidate();
        return super.onTouchEvent(event);
    }

    public static class WindLWaveineData {
        // 风速数据是否可用
        private List<Boolean> windSpeedIsEnableList;
        //风速
        private List<Float> windSpeed;
        // 时间
        private List<String> dateTimeList;
        // 风向图标
        private List<Integer> winImageResList;

        public List<Boolean> getWindSpeedIsEnableList() {
            return windSpeedIsEnableList;
        }

        public List<Float> getWindSpeed() {
            return windSpeed;
        }

        public List<String> getDateTimeList() {
            return dateTimeList;
        }

        public List<Integer> getWinImageResList() {
            return winImageResList;
        }

        public void setWindSpeedIsEnableList(List<Boolean> windSpeedIsEnableList) {
            this.windSpeedIsEnableList = windSpeedIsEnableList;
        }

        public void setWindSpeed(List<Float> windSpeed) {
            this.windSpeed = windSpeed;
        }

        public void setDateTimeList(List<String> dateTimeList) {
            this.dateTimeList = dateTimeList;
        }

        public void setWinImageResList(List<Integer> winImageResList) {
            this.winImageResList = winImageResList;
        }
    }

    private OnTouchPositionChange onTouchPositionChange;

    public void setOnTouchPositionChange(OnTouchPositionChange onTouchPositionChange) {
        this.onTouchPositionChange = onTouchPositionChange;
    }

    public interface OnTouchPositionChange {
        // 进度从0到1 0代表0点 1 代表24点
        void onChange(float position);

        void onTouchUp();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/804000.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习伦理:探讨隐私保护、公平性和透明度

文章目录 &#x1f340;引言&#x1f340;隐私保护&#x1f340;公平性&#x1f340;透明度&#x1f340;结论 随着机器学习技术的不断发展和应用&#xff0c;我们必须面对伦理问题&#xff0c;以确保这些智能系统的发展和使用是符合道德和法律规范的。本文将就机器学习伦理的关…

Revit二次开发 插件加密、打包、发布、授权全套教程

目录 代码加密及授权 添加授权工具引用 添加授权验证代码段 使用VMProtect进行代码保护 代码加密标记 代码加密 发布产品 软件打包 软件发布 相关文件的获取地址 本教程基于mxbim.com所提供的服务。 Revit二次开发 插件加密、打包、发布、授权全套教程 本网站(www.…

实锤研究,ChatGPT能力掉线!

早在一个多月前&#xff0c;ChatGPT性能下降的传闻便开始在网上流行&#xff0c;不少订阅了Plus版的用户纷纷表示&#xff0c;感觉ChatGPT在经历了几轮更新后开始降智&#xff0c;甚至有时反应速度也会出现问题。而如今&#xff0c;这一传闻终于得到了证实。 就在本周&#xf…

如何学好Java并调整学习过程中的心态:学习之路的秘诀

文章目录 第一步&#xff1a;建立坚实的基础实例分析&#xff1a;选择合适的学习路径 第二步&#xff1a;选择合适的学习资源实例分析&#xff1a;参与编程社区 第三步&#xff1a;动手实践实例分析&#xff1a;开发个人项目 调整学习过程中的心态1. 不怕失败2. 持续学习3. 寻求…

ORA-38760: This database instance failed to turn on flashback database

早晨接一个任务&#xff0c;使用rman备份在虚拟化单机上恢复实例&#xff0c;恢复参数文件、控制文件和数据文件都正常&#xff0c;recover归档时报错如下&#xff1a; Starting recover at 2023-07-28 10:25:01 using channel ORA_DISK_1 starting media recovery media reco…

实时云渲染技术:VR虚拟现实应用的关键节点

近年来&#xff0c;虚拟现实&#xff08;Virtual Reality, VR&#xff09;技术在市场上的应用越来越广泛&#xff0c;虚拟现实已成为一个热门的科技话题。相关数据显示&#xff0c;2019年至2021年&#xff0c;我国虚拟现实市场规模不断扩大&#xff0c;从2019年的282.8亿元增长…

攻防世界-Reverse-simple-unpack

题目描述&#xff1a;菜鸡拿到了一个被加壳的二进制文件 1. 思路分析 提示很清楚了&#xff0c;加壳的二进制文件&#xff0c;正好对这一块知识点是残缺的&#xff0c;先了解下加壳到底是什么 通过这段描述&#xff0c;其实加壳的目的是使得逆向起来更难了&#xff0c;因此这里…

基于SSM实现个人随笔分享平台:创作心灵,分享自我

项目简介 本文将对项目的功能及部分细节的实现进行介绍。个人随笔分享平台基于 SpringBoot SpringMVC MyBatis 实现。实现了用户的注册与登录、随笔主页、文章查询、个人随笔展示、个人随笔查询、写随笔、草稿箱、随笔修改、随笔删除、访问量及阅读量统计等功能。该项目登录模…

十六章:可靠性确实重要:一种端到端的弱监督语义分割方法

0.摘要 弱监督语义分割是一项具有挑战性的任务&#xff0c;因为它只利用图像级别的信息作为训练的监督&#xff0c;但在测试时需要产生像素级别的预测。为了应对这样一个具有挑战性的任务&#xff0c;最近最先进的方法提出了采用两步解决方案&#xff0c;即&#xff1a;1&#…

自动上传git

自动上传git 执行脚本 保存为.bat文件 echo off title bat 交互执行git命令 D: cd D:/git/test git add . git commit -m %date:~0,4%年%date:~5,2%月%date:~8,2%日 git push教程如下 1、搜索任务计划程序&#xff08;最好管理员身份运行&#xff0c;普通用户可能无权限&am…

下载JMeter的历史版本——个人推荐5.2.1版本

官网地址&#xff1a;https://archive.apache.org/dist/jmeter/binaries/

【Git|项目管理】Git的常用命令以及使用场景

文章目录 1.前言2.工作区,暂存区,版本库简介3.Git的常用命令4.版本回退5.撤销修改6.删除文件7.总结 1.前言 在学习Git命令之前,需要先了解工作区,暂存区和版本库这三个概念 2.工作区,暂存区,版本库简介 在使用Git进行版本控制时&#xff0c;有三个重要的概念&#xff1a;工作…

机器学习——异常检测

异常点检测(Outlier detection)&#xff0c;⼜称为离群点检测&#xff0c;是找出与预期对象的⾏为差异较⼤的对象的⼀个检测过程。这些被检测出的对象被称为异常点或者离群点。异常点&#xff08;outlier&#xff09;是⼀个数据对象&#xff0c;它明显不同于其他的数据对象。异…

invalid use of incomplete type class ui(new Ui::MainWindow)报错,解决方案

invalid use of incomplete type class ui(new Ui::MainWindow报错&#xff0c;解决方案 原因解决方案 原因 就是在我改控件button的名字的时候&#xff0c;没有选中控件&#xff0c;导致吧mainwindow的名字改了。。。 解决方案 吧mainwindow的名字改回来 MainWindow 完美解…

【LeetCode】101.对称二叉树

题目 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false提示&#xff1a; 树中节点数…

java项目之社区疫情防控管理信息系统(ssm+mysql+jsp)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于ssm的社区疫情防控管理信息系统。技术交流和部署相关看文章末尾&#xff01; 开发环境&#xff1a; 后端&#xff1a; 开发语言&#xff1a;Ja…

小米新专利曝光:解决升降摄像头痛点,隐藏式摄像头指日可待

根据国家知识产权局官方网站的最新消息&#xff0c;小米移动软件公司的“摄像头隐藏式电子设备及隐藏式摄像头”的专利申请在今天获得了授权。 这款电子设备的主要组成部分包括壳体、摄像模组和可伸缩的反射组件。壳体上设有一个开口&#xff0c;可以让反射组件向外伸出。反射组…

数据可视化大屏拼接屏开发实录:屏幕分辨率测试工具

一、可视化大屏开发 在数据可视化大屏开发时&#xff0c;确定数据可视化大屏拼接屏的板块尺寸需要考虑以下几个因素&#xff1a; 屏幕分辨率&#xff1a;首先需要知道每个板块屏幕的分辨率&#xff0c;包括宽度和高度&#xff0c;这决定了每个板块上可以显示的像素数量。 数据…

Qt : day4

1.思维导图 2.服务器 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//给服务器指针实例化空间server new QTcpServer(this);}Widget::~Widget() {delete ui;…

Pytorch(一)

目录 一、基本操作 二、自动求导机制 三、线性回归DEMO 3.1模型的读取与保存 3.2利用GPU训练时 四、常见的Tensor形式 五、Hub模块 一、基本操作 操作代码如下: import torch import numpy as np#创建一个矩阵 x1 torch.empty(5,3)# 随机值 x2 torch.rand(5,3)# 初始化…