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,下一步将要去简单理解自 ...
随机推荐
- ios开发swift学习第三天:逻辑分支
一. 分支的介绍 分支即if/switch/三目运算符等判断语句 通过分支语句可以控制程序的执行流程 二. if分支语句 和OC中if语句有一定的区别 判断句可以不加() 在Swift的判断句中必须有 ...
- 以Network Dataset(网络数据集)方式实现的最短路径分析
转自原文 以Network Dataset(网络数据集)方式实现的最短路径分析 构建网络有两种方式,分别是网络数据集NetworkDataset和几何网络Geometric Network,这个网络结 ...
- 【hdu 3032】Nim or not Nim?
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s) ...
- 第六章:任务执行——Java并发编程实战
任务:通常是一些抽象的且离散的工作单元.大多数并发应用程序都是围绕"任务执行"来构造的,把程序的工作分给多个任务,可以简化程序的组织结构便于维护 一.在线程中执行任务 任务的独立性 ...
- 【物理】概念的理解 —— Phase(相位)
Phase is the position of a point in time (an instant) on a waveform cycle. 相位指的是波形周期中点在某一时刻的位置.Phase ...
- 使用蒲公英来发布iOS内侧版本
1.生成ipa包 这里不介绍正规的打包方式(选择真机调试-编译成功以后-工具product-archive-....) 下面介绍快速打包方式: 1)选择真机调试-编译成功以后右击下图.app文件,选择 ...
- Java与C#的语法区别
1.作用域 在java中 { { int a=1; } int a=2;//以上a作用域外的以下,再声明同名的变量,是允许的: } 在C#中,以上是不允许的[只要在同一个作用域内,以上或以下的代码中 ...
- 改变浏览器中默认的ctrl+s方法
在一般的情况下,我们在浏览网页的时候按下ctrl+s,浏览器会弹出一个保存网页的框. 但是在一些特定的网页中,我们希望ctrl+s不是弹出默认的保存窗口,而是进行一下别的操作. 比如在我们使用简书的时 ...
- matlab 求解线性规划问题
线性规划 LP(Linear programming,线性规划)是一种优化方法,在优化问题中目标函数和约束函数均为向量变量的线性函数,LP问题可描述为: minf(x):待最小化的目标函数(如果问题本 ...
- 多线程编程 CreateThread(解释了TContext)
function CreateThread( lpThreadAttributes: Pointer; {安全设置} dwStackSize: DWORD; ...