Android 自定义view之24小时天气

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34476727/article/details/81148956

这里写图片描述

项目中需求:所以自己写了一个。
重写HorizontalScrollView自定义,然后嵌套自定义的view。

public class Today24HourView extends View{



    private static final String TAG = "Today24HourView";
    private static final int ITEM_SIZE = 9;  //24小时
    private static final int ITEM_WIDTH = 200; //每个Item的宽度   曲线上方移动模块
    private static final int MARGIN_LEFT_ITEM = 50; //左边预留宽度    距离左边的距离
    private static final int MARGIN_RIGHT_ITEM = 120; //右边预留宽度

    private static final int windyBoxAlpha = 200;  //风力盒子透明度
    private static final int windyBoxMaxHeight = 80;
    private static final int windyBoxMinHeight = 20;
    private static final int windyBoxSubHight = windyBoxMaxHeight - windyBoxMinHeight;
    private static final int bottomTextHeight = 60;

    private int mHeight, mWidth;
    private int tempBaseTop;  //温度折线的上边Y坐标
    private int tempBaseBottom; //温度折线的下边Y坐标
    private Paint bitmapPaint, windyBoxPaint, linePaint, pointPaint, dashLinePaint;
    private TextPaint textPaint;

    private Context context;
    private List<HourItem> listItems;
    private int maxScrollOffset = 0;//滚动条最长滚动距离
    private int scrollOffset = 0; //滚动条偏移量
    private int currentItemIndex = 0; //当前滚动的位置所对应的item下标
    private int currentWeatherRes = -1;

    private RealTimeWeatherBean realTimeWeatherBean;//实体类
    private  List<String> listTime;
    private List<String> listurl;
    private  List<String> listwind;
    private List<Integer> listTemperature;
    private   List<Integer>  weatherPicturelist;
    private int maxTemp = 26;
    private int minTemp = 10;
    private int maxWindy = 5;
    private int minWindy = 2;
    private static final int TEMP[] = { 2, 5, 6, 3, 2,
            1, -1, -2, -4};
    private static final int WINDY[] = {4, 4, 4, 4, 4,
            4, 4, 4, 4};
    private static final int WEATHER_RES[] ={R.mipmap.w0, R.mipmap.w1, R.mipmap.w3,  R.mipmap.w15,  R.mipmap.w15
            ,R.mipmap.w5, R.mipmap.w7, R.mipmap.w9,  R.mipmap.w15,  R.mipmap.w15
            , R.mipmap.w15, R.mipmap.w10, R.mipmap.w15, R.mipmap.w15,  R.mipmap.w15
            , R.mipmap.w15,  R.mipmap.w15,  R.mipmap.w15,  R.mipmap.w15,  R.mipmap.w15
            ,R.mipmap.w18,  R.mipmap.w15,  R.mipmap.w15, R.mipmap.w19};


    public Today24HourView(Context context,RealTimeWeatherBean realTimeWeatherBean) {
        super(context, null);
        this.context=context;
        this.realTimeWeatherBean=realTimeWeatherBean;
        listTime = realTimeWeatherBean.getListTime();
        listurl = realTimeWeatherBean.getListurl();
        listwind = realTimeWeatherBean.getListwind();
        maxTemp=realTimeWeatherBean.getMaxTemp();
        minTemp=realTimeWeatherBean.getMinTemp();
        listTemperature = realTimeWeatherBean.getListTemperature();
        weatherPicturelist= realTimeWeatherBean.getWeatherPicturelist();
        init();
    }

    public Today24HourView(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
        init();
    }

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

    private void init() {
        mWidth = MARGIN_LEFT_ITEM + MARGIN_RIGHT_ITEM + ITEM_SIZE * ITEM_WIDTH;
        mHeight = 600; //暂时先写死
        tempBaseTop = (500 - bottomTextHeight)/3;   //曲线距离顶部
        tempBaseBottom = (500 - bottomTextHeight)*2/3;  //曲线距离底部

        initHourItems();
        initPaint();
    }

    private void initPaint() {
        pointPaint = new Paint();
        //温度 点
        pointPaint.setColor(getResources().getColor(R.color.color_4bc8fc));
        pointPaint.setStyle(Paint.Style.STROKE);
        pointPaint.setStrokeWidth(5);//圆壁厚度
        pointPaint.setAntiAlias(true);
        pointPaint.setTextSize(20);

        linePaint = new Paint();
        //底部水平的白线
        linePaint.setColor(new Color().WHITE);
        linePaint.setAntiAlias(true);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeWidth(5);

        dashLinePaint = new Paint();
        dashLinePaint.setColor(new Color().RED);
        PathEffect effect = new DashPathEffect(new float[]{5, 5, 5, 5}, 1);
        dashLinePaint.setPathEffect(effect);
        dashLinePaint.setStrokeWidth(3);
        dashLinePaint.setAntiAlias(true);
        dashLinePaint.setStyle(Paint.Style.STROKE);

        windyBoxPaint = new Paint();
        //风力下面的 方块
        windyBoxPaint.setTextSize(1);
        windyBoxPaint.setColor(getResources().getColor(R.color.color_bbd7f8));
        windyBoxPaint.setAlpha(windyBoxAlpha);
        windyBoxPaint.setAntiAlias(true);

        textPaint = new TextPaint();
        textPaint.setTextSize(DisplayUtil.sp2px(getContext(), 12));
        textPaint.setColor(new Color().WHITE);
        textPaint.setAntiAlias(true);

        bitmapPaint = new Paint();
        bitmapPaint.setAntiAlias(true);
    }

    //简单初始化下,后续改为由外部传入
    private void initHourItems(){
        listItems = new ArrayList<>();
        for(int i=0; i<ITEM_SIZE; i++){
            String time;

            time=listTime.get(i);//时间

            int left =MARGIN_LEFT_ITEM  +  i * ITEM_WIDTH;
            int right = left + ITEM_WIDTH - 1;
            int top = (int)(mHeight -bottomTextHeight +
                    (maxWindy - WINDY[i])*1.0/(maxWindy - minWindy)*windyBoxSubHight
                    - windyBoxMaxHeight);
            int bottom =  mHeight - bottomTextHeight;
            Rect rect = new Rect(left, top, right, bottom);
            Point point = calculateTempPoint(left, right, listTemperature.get(i));

            HourItem hourItem = new HourItem();
            hourItem.windyBoxRect = rect;
            hourItem.time = time;
            hourItem.windy = WINDY[i];
            hourItem.temperature = listTemperature.get(i);
            hourItem.tempPoint = point;
            hourItem.res = WEATHER_RES[i];
            listItems.add(hourItem);
        }
    }

    //计算温度点
    private Point calculateTempPoint(int left, int right, int temp){
        double minHeight = tempBaseTop;
        double maxHeight = tempBaseBottom;
        double tempY = maxHeight - (temp - minTemp)* 1.0/(maxTemp - minTemp) * (maxHeight - minHeight);
        Point point = new Point((left + right)/2, (int)tempY);
        return point;
    }

    /**
     * 图形的宽度,根据左边预留宽度+右边预留宽度+温度文字大小+每个item的宽度
     *  高根据我们具体的需求设置
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(mWidth, mHeight);
    }

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

    @Override
    protected void onDraw(final Canvas canvas) {
        super.onDraw(canvas);
        for(int i=0; i<listItems.size(); i++){
            Rect rect = listItems.get(i).windyBoxRect;
            final Point point = listItems.get(i).tempPoint;

            onDrawLine(canvas, i);//曲线

            //画风力的box和提示文字
            onDrawBox(canvas, rect, i);
            //画表示天气图片   曲线上的天气
            if(listItems.get(i).res != -1 && i != currentItemIndex){

                Drawable drawable = ContextCompat.getDrawable(getContext(), weatherPicturelist.get(i));
                drawable.setBounds(point.x - DisplayUtil.dip2px(getContext(), 10),
                        point.y - DisplayUtil.dip2px(getContext(), 25),
                        point.x + DisplayUtil.dip2px(getContext(), 10),
                        point.y - DisplayUtil.dip2px(getContext(), 5));
                drawable.draw(canvas);

            }
            //画温度的点
            onDrawTemp(canvas, i);
            onDrawText(canvas, i);  //时间



        }
        //底部水平的白线
        linePaint.setColor(new Color().WHITE);
    }

    /**
     *    曲线上的温度
     * @param canvas
     * @param i
     */
    private void onDrawTemp(final Canvas canvas, int i) {
        HourItem item = listItems.get(i);
        Point point = item.tempPoint;
        //数字是圆的半径
        canvas.drawCircle(point.x, point.y, 8, pointPaint);

        if(currentItemIndex == i) {
            //计算提示文字的运动轨迹
            final int Y = getTempBarY();

            //虚线
            Drawable drawablew = ContextCompat.getDrawable(getContext(), R.mipmap.dotted_lines);
            drawablew.setBounds(getScrollBarX()+95,
                    Y - DisplayUtil.dip2px(getContext(), 70),
                    getScrollBarX() +100,
                    450-Y/5);
            drawablew.draw(canvas);

            //画出背景图片
            Drawable drawable = ContextCompat.getDrawable(getContext(), R.drawable.weather_text_bg);
            drawable.setBounds(getScrollBarX(),
                    Y - DisplayUtil.dip2px(getContext(), 34),
                    getScrollBarX() + ITEM_WIDTH,
                    Y - DisplayUtil.dip2px(getContext(), 2));
            drawable.draw(canvas);
            //画天气
            final int res = findCurrentRes(i);
            Integer integer = weatherPicturelist.get(i);
            if(res != -1) {

                Drawable drawTemp = ContextCompat.getDrawable(getContext(), integer);
                drawTemp.setBounds(getScrollBarX()+ITEM_WIDTH/2 + (ITEM_WIDTH/2 - DisplayUtil.dip2px(getContext(), 30))/2,
                        Y - DisplayUtil.dip2px(getContext(), 30),
                        getScrollBarX()+ITEM_WIDTH - (ITEM_WIDTH/2 - DisplayUtil.dip2px(getContext(), 20))/2,
                        Y - DisplayUtil.dip2px(getContext(), 6));
                drawTemp.draw(canvas);



            }
            //画出温度提示
            int offset = ITEM_WIDTH/2;
            if(res == -1){
                offset = ITEM_WIDTH;
            }
            Rect targetRect = new Rect(getScrollBarX(), Y - DisplayUtil.dip2px(getContext(), 34)
                    , getScrollBarX() + offset, Y - DisplayUtil.dip2px(getContext(), 4));
            Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
            int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
            textPaint.setTextAlign(Paint.Align.CENTER);
            textPaint.setColor(getResources().getColor(R.color.tvtitle));
            textPaint.setTextSize(DisplayUtil.dip2px(getContext(), 16));
            canvas.drawText(item.temperature + "°", targetRect.centerX(), baseline, textPaint);
        }
    }

    private int findCurrentRes(int i) {
        if(listItems.get(i).res != -1){
            return listItems.get(i).res;
        }
        for(int k=i; k>=0; k--){
            if(listItems.get(k).res != -1){
                return listItems.get(k).res;
            }
        }
        return -1;
    }

    //画底部风力的BOX
    private void onDrawBox(Canvas canvas, Rect rect, int i) {
        // 新建一个矩形
        RectF boxRect = new RectF(rect);
        HourItem item = listItems.get(i);
        String s = listwind.get(i);
        String wind = s.replace("级", "");
        if(i == currentItemIndex) {
            windyBoxPaint.setAlpha(255);
            canvas.drawRoundRect(boxRect, 4, 4, windyBoxPaint);
            //画出box上面的风力提示文字
            Rect targetRect = new Rect(getScrollBarX(), rect.top - DisplayUtil.dip2px(getContext(), -16)
                    , getScrollBarX() + ITEM_WIDTH, rect.top - DisplayUtil.dip2px(getContext(), 0));
            Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
            int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
            textPaint.setTextAlign(Paint.Align.CENTER);
            textPaint.setColor(getResources().getColor(R.color.oa_all_title_bg));
            textPaint.setTextSize(DisplayUtil.dip2px(getContext(), 12));
            canvas.drawText(wind + "级", targetRect.centerX(), baseline, textPaint);
        } else {
            windyBoxPaint.setAlpha(windyBoxAlpha);
            canvas.drawRoundRect(boxRect, 4, 4, windyBoxPaint);
        }
    }

    //温度的折线,为了折线比较平滑,做了贝塞尔曲线
    private void onDrawLine(Canvas canvas, int i) {
        //曲线渐变色
        LinearGradient linearGradient = new LinearGradient(0, 0, 500, 500, new int[] {
                getResources().getColor(R.color.color_2491eb), getResources().getColor(R.color.color_50d0fe),
                getResources().getColor(R.color.color_2490eb)}, null,
                Shader.TileMode.REPEAT);

        linePaint.setColor(getResources().getColor(R.color.color_4bc8fc));
        linePaint.setStrokeWidth(4);
        Point point = listItems.get(i).tempPoint;
        if(i != 0){
            Point pointPre = listItems.get(i-1).tempPoint;
            Path path = new Path();
            path.moveTo(pointPre.x, pointPre.y);
            if(i % 2 == 0){
                path.cubicTo((pointPre.x+point.x)/2, (pointPre.y+point.y)/2-7, (pointPre.x+point.x)/2, (pointPre.y+point.y)/2+7, point.x, point.y);
            } else{
                path.cubicTo((pointPre.x+point.x)/2, (pointPre.y+point.y)/2+7, (pointPre.x+point.x)/2, (pointPre.y+point.y)/2-7, point.x, point.y);
            }
            linePaint.setShader(linearGradient);
            canvas.drawPath(path, linePaint);
        }
    }

    //绘制底部时间
    private void onDrawText(Canvas canvas, int i) {
        //此处的计算是为了文字能够居中
        Rect rect = listItems.get(i).windyBoxRect;
        Rect targetRect = new Rect(rect.left, rect.bottom, rect.right, rect.bottom - bottomTextHeight-130);//时间显示位置
        Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
        int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setColor(getResources().getColor(R.color.title_text_color));
        textPaint.setTextSize(DisplayUtil.dip2px(getContext(), 14));
        String text = listItems.get(i).time;
        canvas.drawText(text, targetRect.centerX(), baseline, textPaint);
    }


    public void drawLeftTempText(Canvas canvas, int offset){
        //画最左侧的文字
        textPaint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText(maxTemp + "°", DisplayUtil.dip2px(getContext(), 6) + offset, tempBaseTop, textPaint);
        canvas.drawText(minTemp + "°", DisplayUtil.dip2px(getContext(), 6) + offset, tempBaseBottom, textPaint);
    }

    //设置scrollerView的滚动条的位置,通过位置计算当前的时段
    public void setScrollOffset(int offset, int maxScrollOffset){
        this.maxScrollOffset = maxScrollOffset;
        scrollOffset = offset;
        int index = calculateItemIndex(offset);
        currentItemIndex = index;
        invalidate();
    }

    //通过滚动条偏移量计算当前选择的时刻
    private int calculateItemIndex(int offset){
//        Log.d(TAG, "maxScrollOffset = " + maxScrollOffset + "  scrollOffset = " + scrollOffset);
        int x = getScrollBarX();
        int sum = MARGIN_LEFT_ITEM  - ITEM_WIDTH/2;
        for(int i=0; i<ITEM_SIZE; i++){
            sum += ITEM_WIDTH;
            if(x < sum){
                return i;
            }

        }
        return ITEM_SIZE - 1;
    }

    private int getScrollBarX(){
        int x = 0;
        if (maxScrollOffset!=0){
            x = (ITEM_SIZE - 1) * ITEM_WIDTH * scrollOffset / maxScrollOffset;
        }
        x = x + MARGIN_LEFT_ITEM;
        return x;
    }

    //计算温度提示文字的运动轨迹
    private int getTempBarY(){
        int x = getScrollBarX();
        int sum = MARGIN_LEFT_ITEM ;
        Point startPoint = null, endPoint;
        int i;
        for(i=0; i<ITEM_SIZE; i++){
            sum += ITEM_WIDTH;
            if(x < sum) {
                startPoint = listItems.get(i).tempPoint;
                break;
            }
        }
        if(i+1 >= ITEM_SIZE || startPoint == null){
            return listItems.get(ITEM_SIZE-1).tempPoint.y;
        }
        endPoint = listItems.get(i+1).tempPoint;

        Rect rect = listItems.get(i).windyBoxRect;
        int y = (int)(startPoint.y + (x - rect.left)*1.0/ITEM_WIDTH * (endPoint.y - startPoint.y));
        return y;
    }
}

HorizontalScrollView中的代码:

public class IndexHorizontalScrollView extends HorizontalScrollView {

    private static final String TAG = "IndexHorizontal";
    private Paint textPaint;
    private Today24HourView today24HourView;

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

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

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

    private void init() {
        textPaint = new Paint();
        textPaint.setTextSize(DisplayUtil.sp2px(getContext(), 12));
        textPaint.setAntiAlias(true);
        textPaint.setColor(new Color().WHITE);

    }

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

        int offset = computeHorizontalScrollOffset();
        int maxOffset = computeHorizontalScrollRange() - DisplayUtil.getScreenWidth(getContext());
        if(today24HourView != null){
//            today24HourView.drawLeftTempText(canvas, offset);
            today24HourView.setScrollOffset(offset, maxOffset);
        }
    }

    public void setToday24HourView(Today24HourView today24HourView){
        this.today24HourView = today24HourView;
    }
}

源码地址:github

猜你喜欢

转载自blog.csdn.net/qq_34476727/article/details/81148956
今日推荐