自定义view,天气折线图

公司能用到自定义view的不多,有个简单的折线图练手,花了大半天时间基本搞定,嗯,代码放这了。

image.png

放代码前先放我平时学习做的一些记录
Path.addCircle
canvas
Point
Rect类和RectF类
Paint类
onMeasure

代码放这,核心注释已经写的很清楚了。时间关系,一些简单方法的注释我后面再补充。

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.View;

import java.util.ArrayList;

import xzh.com.mojiweather.R;
import xzh.com.mojiweather.model.ChartItem;
import xzh.com.mojiweather.utils.Utils;

/**
 * 绘制天气折线图的View
 */
public class ChartView extends View {

    // 上下文
    private Context context;
    // 早上数据源
    private ArrayList<ChartItem> items;
    // 晚上数据源
    private ArrayList<ChartItem> items2;
    // 早上的x坐标数据源
    ArrayList<Integer> xlist;
    // 晚上的x坐标数据源
    ArrayList<Integer> xlist2;
    // 提示文字
    private String unit;
    // 格式化
    private String yFormat = "0.#°";

    int mSplit;
    // 顶部间距
    int mYMargintTop;
    int mMargint2;

    // 线条的油漆
    Paint mPaint;
    // 单位文字的油漆
    Paint mPaintUnit;

    /**
     * 赋值
     *
     * @param list     数据源
     * @param unitInfo 提示文字
     */
    public void setView(ArrayList<ChartItem> list, ArrayList<ChartItem> list2, String unitInfo) {
        this.items = list;
        this.items2 = list2;
        this.unit = unitInfo;
    }

    public ChartView(Context ct) {
        super(ct);
        this.context = ct;
        init();
    }

    public ChartView(Context ct, AttributeSet attrs) {
        super(ct, attrs);
        this.context = ct;
        init();
    }

    public ChartView(Context ct, AttributeSet attrs, int defStyle) {
        super(ct, attrs, defStyle);
        this.context = ct;
        init();
    }

    /**
     * 初始化工具
     */
    private void init() {
        // 初始化所有单位
        mSplit = Utils.dip2px(context, 20);
        mYMargintTop = Utils.dip2px(context, 60);
        mMargint2 = Utils.dip2px(context, 25);

        // x坐标的数据源
        xlist = new ArrayList<>();
        xlist2 = new ArrayList<>();

        // 初始化 油漆
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(context.getResources().getColor(R.color.weather_line));
        mPaint.setStrokeWidth(4);
        mPaint.setStyle(Paint.Style.STROKE);

        // 初始化单位 的油漆
        mPaintUnit = new Paint();
        mPaintUnit.setTextSize(Utils.sp2px(context, 12));
        mPaintUnit.setColor(context.getResources().getColor(R.color.weather_line));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (items == null || items2 == null) {
            return;
        }

        int height = getHeight();
        int width = getWidth();
        // 左右间距
        int marginl = 0;
        int bheight = height - mYMargintTop - 2 * mSplit;

        // 画单位
//        canvas.drawText(unit, mSplit + Utils.dip2px(context, 30f), mMargint2 + mSplit * 2, mPaintUnit);

        // 画早上的X坐标值
        mPaint.setColor(Color.GRAY);
        mPaint.setTextSize(15);//设置字体大小
        for (int i = 0; i < items.size(); i++) {
            // 获取跨度,比如3条数据就平均3块
            int span = (width - 2 * marginl) / items.size();
            // 算出每个跨度的中间值,该值就是x了
            int x = marginl + span / 2 + span * i;
            xlist.add(x);
            drawText(items.get(i).getX(), x, mSplit * 2, canvas);
        }
        // 画晚上的X坐标值
        for (int i = 0; i < items2.size(); i++) {
            // 获取跨度,比如3条数据就平均3块
            int span = (width - 2 * marginl) / items2.size();
            // 算出每个跨度的中间值,该值就是x了
            int x = marginl + span / 2 + span * i;
            xlist2.add(x);
            drawText(items2.get(i).getX(), x, mSplit * 2, canvas);
        }

        // 获取最大值和最小值
        float max = Float.MIN_VALUE;
        float min = Float.MAX_VALUE;
        for (int i = 0; i < items.size(); i++) {
            float y = items.get(i).getY();
            if (y > max) {
                max = y;
            }
            if (y < min) {
                min = y;
            }
        }
        for (int i = 0; i < items2.size(); i++) {
            float y = items2.get(i).getY();
            if (y > max) {
                max = y;
            }
            if (y < min) {
                min = y;
            }
        }

        // 最大跟最小的差距为0,则设置默认值6给它们
        float span = max - min;
        if (span == 0) {
            span = 6.0f;
        }
        // 添加差距,这样即使span为6,最大的也+1,最小的也-1。他们相差就2点了
        max = max + span / 6.0f;
        min = min - span / 6.0f;

        // 获取点集合
        Point[] mPoints = getPoints(items, xlist, max, min, bheight, mYMargintTop);
        Point[] mPoints2 = getPoints(items2, xlist2, max, min, bheight, mYMargintTop);

        // 画线
        mPaint.setColor(context.getResources().getColor(R.color.morning_color));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(8);
        drawLine(mPoints, canvas, mPaint);
        mPaint.setColor(context.getResources().getColor(R.color.night_color));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(8);
        drawLine(mPoints2, canvas, mPaint);

        // 画点
        mPaint.setColor(context.getResources().getColor(R.color.morning_color));
        mPaint.setStyle(Paint.Style.FILL);
        for (int i = 0; i < mPoints.length; i++) {
            canvas.drawCircle(mPoints[i].x, mPoints[i].y, 12, mPaint);
            @SuppressLint("DrawAllocation")
            String yText = new java.text.DecimalFormat(yFormat).format(items.get(i).getY());
            drawText(yText, mPoints[i].x,
                    mPoints[i].y - Utils.dip2px(context, 12), canvas);
        }
        mPaint.setColor(context.getResources().getColor(R.color.night_color));
        for (int i = 0; i < mPoints2.length; i++) {
            canvas.drawCircle(mPoints2[i].x, mPoints2[i].y, 12, mPaint);
            @SuppressLint("DrawAllocation")
            String yText = new java.text.DecimalFormat(yFormat).format(items2.get(i).getY());
            drawText(yText, mPoints2[i].x,
                    mPoints2[i].y - Utils.dip2px(context, 12), canvas);
        }
    }

    /**
     * @param items items数据源
     * @param xlist 横轴坐标
     * @param max   最大的max
     * @param min   最小的min
     * @param h     仅仅该折现图的高度
     * @param top   顶部间距
     * @return Point
     */
    private Point[] getPoints(ArrayList<ChartItem> items, ArrayList<Integer> xlist, float max, float min,
                              int h, int top) {
        // 实例化point
        Point[] points = new Point[items.size()];
        // 获取y轴位置,这个逻辑还是很好理解的
        for (int i = 0; i < items.size(); i++) {
            // (1)items.get(i).getY() - min 首先是y的实际天气度数数据 - 最小的y轴数据获取他们的差距作为分子
            double molecule = items.get(i).getY() - min;
            // (2)max - min 获取y轴的范围作为分母
            double denominator = max - min;
            // (3)然后是h具体高度*(1)(2)步骤组成的分子分母获取 具体的 y轴差异 数据
            int yDifference = (int) (h * (molecule / denominator));
            //  (4)最后 h+顶部间距 - y轴差异 就获得y轴数据了
            int y = top + h - yDifference;
            points[i] = new Point(xlist.get(i), y);
        }
        return points;
    }

    private void drawLine(Point[] ps, Canvas canvas, Paint paint) {
        Point startp;
        Point endp;
        for (int i = 0; i < ps.length - 1; i++) {
            startp = ps[i];
            endp = ps[i + 1];
            canvas.drawLine(startp.x, startp.y, endp.x, endp.y, paint);
        }
    }

    private void drawText(String text, int x, int y, Canvas canvas) {
        Paint p = new Paint();
        p.setAlpha(context.getResources().getColor(R.color.color_write));
        p.setTextSize(Utils.sp2px(context, 14));
        p.setTextAlign(Paint.Align.CENTER);
        p.setColor(Color.WHITE);
        canvas.drawText(text, x, y, p);
    }


}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,692评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,482评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,995评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,223评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,245评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,208评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,091评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,929评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,346评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,570评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,739评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,437评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,037评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,677评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,833评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,760评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,647评论 2 354

推荐阅读更多精彩内容