android自定义View绘制天气温度曲线
转载请注明出处:http://blog.csdn.net/kaku2013/article/details/50091387
大家好,不久前写了个绘制天气温度曲线功能的demo今天抽空来和大家分享一下。
效果图如下:
运用到我的项目效果图:https://github.com/kaku2015/WeatherAlarmClock
一般绘制图表,可以直接使用类库来实现,类似的类库还是很多的。
例如:
hellocharts-android

WilliamChart

MPAndroidChart

上述类库功能很多,可以实现很多种图表,效果也是非常绚丽的。
那么为什么这里我还要尝试自己绘制天气温度曲线呢,原因如下:
1.由于类库实现的功能比较多相对代码也稍显复杂,要想实现需求要改的地方还是挺多的,尝试的修改了几个类库代码,最终还是没能完全符合自己的需求,有的改后也莫名其妙的出现了细微的bug,不知道是不是修改的原因…因此也浪费了挺长的时间,不过也学到了很多的东西。
2.这么强大类库用来实现功能、效果相对单一的天气温度曲线是不是有点杀鸡焉用宰牛刀呢!
主要还是没能通过类库完全实现自己的需求,无奈逼迫自己走上了自定义View这条荆棘之路…
由于本人接触安卓时间并不算长,还没有自定义过组件,所以对于自定义View还是比较畏惧的…
经过方案的思考与整理,发现自定义View来实现天气温度曲线并没有想象中的那么难,相反还是挺简单的,而且代码量也是相当的少,只需要覆写View类的 onDraw(Canvas canvas) 方法即可实现需求。
自定义View代码如下:
/*
* Copyright (c) 2016 Kaku咖枯 <kaku201313@163.com | 3772304@qq.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.kaku.wcv;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
import com.kaku.library.R;
// The plan
//*-----------------------------------------*
// SPACE *
//*-----------------------------------------*
// TEXT *
//*-----------------------------------------*
// TEXT SPACE *
//*-----------------------------------------*
// RADIUS *
//*-----------------------------------------*
// | *
// | *
// | *
// ---------(x,y)-------- *
// | *
// | *
// | *
//*-----------------------------------------*
// RADIUS *
//*-----------------------------------------*
// TEXT SPACE *
//*-----------------------------------------*
// TEXT *
//*-----------------------------------------*
// SPACE *
//*-----------------------------------------*
/**
* 折线温度双曲线
*
* @author 咖枯
* @version 1.0 2015/11/06
*/
public class WeatherChartView extends View {
/**
* x轴集合
*/
private float mXAxis[] = new float[6];
/**
* 白天y轴集合
*/
private float mYAxisDay[] = new float[6];
/**
* 夜间y轴集合
*/
private float mYAxisNight[] = new float[6];
/**
* x,y轴集合数
*/
private static final int LENGTH = 6;
/**
* 白天温度集合
*/
private int mTempDay[] = new int[6];
/**
* 夜间温度集合
*/
private int mTempNight[] = new int[6];
/**
* 控件高
*/
private int mHeight;
/**
* 字体大小
*/
private float mTextSize;
/**
* 圓半径
*/
private float mRadius;
/**
* 圓半径今天
*/
private float mRadiusToday;
/**
* 文字移动位置距离
*/
private float mTextSpace;
/**
* 线的大小
*/
private float mStokeWidth;
/**
* 白天折线颜色
*/
private int mColorDay;
/**
* 夜间折线颜色
*/
private int mColorNight;
/**
* 字体颜色
*/
private int mTextColor;
/**
* 屏幕密度
*/
private float mDensity;
/**
* 控件边的空白空间
*/
private float mSpace;
@SuppressWarnings("deprecation")
public WeatherChartView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WeatherChartView);
float densityText = getResources().getDisplayMetrics().scaledDensity;
mTextSize = a.getDimensionPixelSize(R.styleable.WeatherChartView_textSize,
(int) (14 * densityText));
mColorDay = a.getColor(R.styleable.WeatherChartView_dayColor,
getResources().getColor(R.color.colorAccent));
mColorNight = a.getColor(R.styleable.WeatherChartView_nightColor,
getResources().getColor(R.color.colorPrimary));
mTextColor = a.getColor(R.styleable.WeatherChartView_textColor, Color.WHITE);
a.recycle();
mDensity = getResources().getDisplayMetrics().density;
mRadius = 3 * mDensity;
mRadiusToday = 5 * mDensity;
mSpace = 3 * mDensity;
mTextSpace = 10 * mDensity;
mStokeWidth = 2 * mDensity;
}
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) {
// 线画笔
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 == 0) {
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 {
linePaint.setAlpha(alpha2);
linePaint.setPathEffect(null);
canvas.drawLine(mXAxis[i], yAxis[i], mXAxis[i + 1], yAxis[i + 1], linePaint);
}
}
// 画点
if (i != 1) {
// 昨天
if (i == 0) {
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) {
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();
// 每一份宽
float w = width / 12;
mXAxis[0] = w;
mXAxis[1] = w * 3;
mXAxis[2] = w * 5;
mXAxis[3] = w * 7;
mXAxis[4] = w * 9;
mXAxis[5] = w * 11;
}
/**
* 设置白天温度
*
* @param tempDay 温度数组集合
*/
public void setTempDay(int[] tempDay) {
mTempDay = tempDay;
}
/**
* 设置夜间温度
*
* @param tempNight 温度数组集合
*/
public void setTempNight(int[] tempNight) {
mTempNight = tempNight;
}
}
布局文件中只需引入上面自定义的View即可:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:wcv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black">
<com.kaku.wcv.WeatherChartView
android:id="@+id/line_char"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_centerInParent="true"
wcv:dayColor="@color/colorAccent"
wcv:nightColor="@color/colorPrimary"
wcv:textColor="@android:color/white"
wcv:textSize="14sp"/>
</RelativeLayout>
Activity中动态设置白天夜间温度集合数值:
WeatherChartView chartView = (WeatherChartView) findViewById(R.id.line_char);
// 设置白天温度曲线
chartView .setTempDay(new int[]{tempDay1,tempDay2,tempDay3,tempDay4,tempDay5,tempDay6});
// 设置夜间温度曲线
chartView .setTempNight(new int[]{tempNight1,tempNight2,tempNight3,tempNight4,tempNight5,tempNight6});
chartView .invalidate();
好了,到这里大功告成,是不是觉得还是很简单的吧。
写的不好的地方希望大家指出,谢谢!
android自定义View绘制天气温度曲线的更多相关文章
- Android 自定义 View 绘制
在 Android 自定义View 里面,介绍了自定义的View的基本概念.同时在 Android 控件架构及View.ViewGroup的测量 里面介绍了 Android 的坐标系 View.Vie ...
- android自定义View绘制圆形头像与椭圆头像
要实现这两种效果,需要自定义View,并且有两种实现方式. 第一种: public class BitmapShaders extends View { private BitmapSh ...
- Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解
Android绘图机制(二)--自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解 我们要想画好一些炫酷的View,首先我们得知道怎么去画一些基础的图案,比如矩形,圆 ...
- Android自定义View 画弧形,文字,并增加动画效果
一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类 B ...
- (转)[原] Android 自定义View 密码框 例子
遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...
- Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
- Android 自定义View (五)——实践
前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...
- Android 自定义 view(四)—— onMeasure 方法理解
前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...
- Android 自定义 view(三)—— onDraw 方法理解
前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...
随机推荐
- jquery画图插件jPainter
jquery画图插件jPainter 一.总结 一句话总结:四年前的项目,四年无更新,不好用. 二.基于HTML5 Canvas和jQuery 的画图工具的实现 简介 HTML5 提供了强大的Canv ...
- 【76.83%】【codeforces 554A】Kyoya and Photobooks
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- 深入理解JVM:垃圾收集器与内存分配策略
堆里面存放着Java世界差点儿全部的对象实例,垃圾收集器在对堆进行回收前.第一件事情就是要确定这些对象之中哪些还存活,哪些已经死去.推断对象的生命周期是否结束有下面几种方法 引用计数法 详细操作是给对 ...
- POJ 3252 Round Numbers(数位dp&记忆化搜索)
题目链接:[kuangbin带你飞]专题十五 数位DP E - Round Numbers 题意 给定区间.求转化为二进制后当中0比1多或相等的数字的个数. 思路 将数字转化为二进制进行数位dp,由于 ...
- 基于 Android NDK 的学习之旅-----资源释放
做上一个项目的时候因为与C引擎交互频繁,有时候会突然莫名其妙的的整个应用程序直接挂掉.因为我是学Java 开始的,所以对主动释放内存没多大概念(GC直接帮忙回收),后查询原因才知道是因为JNI 有些对 ...
- JS实现鼠标经过用户头像显示资料卡的效果,可点击
基于项目的须要.须要制作出例如以下的一种页面效果:当用户鼠标经过好友列表中好友头像时,显示该好友的基本资料.事实上也就是类似QQclient的那种功能. 网上找了非常多代码,基本都实现了鼠标悬浮之后弹 ...
- 高CPU、数据库无法读写
高CPU.数据库无法读写的真凶 有兴趣的同学可以参考如下系列文章,都是针对dump分析的实战和总结: Windbg DUMP分析(原创汇总) http://www.cnblogs.com/Love ...
- LaunchImage启动黑屏-模拟器可以,但是真机黑屏
名称和尺寸都是对的,就是不显示.结果美工重新做一张图片就行了,想了半天都没想到是图片本身等问题啊
- Erlang游戏开发-协议
Erlang游戏开发-协议 选择什么协议? 协议包含通讯协议和数据格式. 通讯协议 通讯协议目前常用的是:HTTP 和TCP .其有各自的特点根据游戏的特点而进行选择. HTTP HTTP比较成熟,使 ...
- 如何在CSDN博客自定义栏目中添加“给我写信”
在"自定义栏目"中添加"连接"(将自己的微博,QQ空间和CSDN博客关联起来)很多人都做过.但是添加"给我写信"这个功能,用的好像不太多.此 ...