Android开发学习之路-自定义控件(天气趋势折线图)
之前写了个天气APP,带4天预报和5天历史信息。所以想着要不要加一个折线图来显示一下天气变化趋势,难得有空,就写了一下,这里做些记录,脑袋不好使容易忘事。
先放一下效果:

控件内容比较简单,就是一个普通的折线图,上下分别带有数字,点击的时候显示当天温度的差值。
创建一个类继承自View,并添加两个构造方法:
public class TrendGraph extends View {
public TrendGraph(Context context) { // 在java代码中创建调用
super(context);
}
public TrendGraph(Context context, AttributeSet attrs) { // 在xml中创建调用
super(context, attrs);
}
}
因为这里不需要考虑wrap_content的情况,所以onMeasure方法不需重写,关键的是onDraw,而onDraw方法其实也不困难,只需要确定好每个点的具体位置就好,因为连线也是需要点的坐标,代码比较啰嗦,可以略过:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mElements == null || mElements.size() == 0) {
return;
}
double max_up = getMaxUp();
double min_down = getMinDown();
canvas.setDrawFilter(mDrawFilter);
mPaint.setStrokeWidth(lineWeith);
float width = getWidth();
float grap = width / mElements.size();
float textSize = mTextPaint.getTextSize();
int textMargin = circleRadius * 2;
float margin_top = textSize + 2 * textMargin;
Log.d(TAG, "onDraw: " + margin_top + "|" + textSize);
float height = getHeight() - 2 * margin_top; for (int i = 0; i < mElements.size() - 1; i++) {
float startX = i * grap + grap / 2;
float stopX = (i + 1) * grap + grap / 2;
float startY = (float) (max_up - mElements.get(i).getUp()) / (float) (max_up -
min_down) * height + margin_top;
float stopY = (float) (max_up - mElements.get(i + 1).getUp()) / (float) (max_up -
min_down) * height + margin_top; canvas.drawText((int) mElements.get(i).getUp() + "℃", startX - textSize, startY -
textMargin, mTextPaint);
canvas.drawCircle(startX, startY, circleRadius, mPaint);
canvas.drawLine(startX, startY, stopX, stopY, mPaint);
if (i == mElements.size() - 2) {
canvas.drawText((int) mElements.get(i + 1).getUp() + "℃", stopX - textSize, stopY
- textMargin, mTextPaint);
canvas.drawCircle(stopX, stopY, circleRadius, mPaint);
} startY = (float) (max_up - mElements.get(i).getDown()) / (float) (max_up - min_down) *
height + margin_top;
stopY = (float) (max_up - mElements.get(i + 1).getDown()) / (float) (max_up -
min_down) * height + margin_top;
canvas.drawText((int) mElements.get(i).getDown() + "℃", startX - textSize, startY +
textSize + textMargin, mTextPaint);
canvas.drawCircle(startX, startY, circleRadius, mPaint);
canvas.drawLine(startX, startY, stopX, stopY, mPaint);
if (i == mElements.size() - 2) {
canvas.drawText((int) mElements.get(i + 1).getDown() + "℃", stopX - textSize,
stopY + textSize + textMargin, mTextPaint);
canvas.drawCircle(stopX, stopY, circleRadius, mPaint);
}
}
}
考虑到需要允许用户进行简单的设置,例如点的大小,文字大小等等,所以定义一些自定义属性(res/values/attr.xml):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="TrendGraph">
<attr name="lineWidth" format="dimension"/>
<attr name="circleRadius" format="dimension" />
<attr name="textSize" format="dimension" />
<attr name="textColor" format="reference" />
</declare-styleable>
</resources>
format指该属性的格式,指定为dimension则是尺寸,取值单位是dp、sp或px等等,而reference则是引用,即一般在xml中引用其他资源的写法,如@string/app_name。还有其他类型,可以自行查找文档。
对自定义属性进行解析得到,这个解析需要在上面定义的第二个构造方法中进行,代码如下:
public TrendGraph(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.TrendGraph);
circleRadius = array.getDimensionPixelSize(R.styleable.TrendGraph_circleRadius, 5);
lineWeith = array.getDimensionPixelSize(R.styleable.TrendGraph_lineWidth, 3);
mTextPaint.setTextSize(array.getDimensionPixelSize(R.styleable.TrendGraph_textSize, 35));
mTextPaint.setColor(array.getColor(R.styleable.TrendGraph_textColor, Color.BLACK));
array.recycle();
}
getDimensionPixelSize方法则是通过传入的值,转换为具体的像素(px)值,也就免去我们手动转换的麻烦。但是要注意,其中的defaultValue依然是px。
接着,就可以通过xml指定这些属性,在布局中加入命名空间:
xmlns:app="http://schemas.android.com/apk/res-auto"
则Android Studio会自动引入,并且可以补全得到,具体使用:
<com.fndroid.byweather.views.TrendGraph
android:id="@+id/tg"
android:layout_width="match_parent"
app:textColor="@color/colorAccent"
app:textSize="22sp"
app:circleRadius="2dp"
android:layout_height="200dp"/>
最后,添加一个事件监听,在点击View的时候进行回调:
① 定义接口:
public interface onItemClickListener{
void onItemClick(View view, Element element);
}
② 在View中添加接口对象,并设置setter方法:
public class TrendGraph extends View {
private onItemClickListener mOnItemClickListener;
// 省略代码
public void setOnItemClickListener(onItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
}
③ 处理onTouchEvent,重写该方法,代码如下:
@Override
public boolean onTouchEvent(MotionEvent event) {
int viewWidth = getWidth();
int itemWidth = viewWidth / mElements.size();
int viewHeight = getHeight();
boolean isMove = false; // 界面中最外层为一个NestedScrollView,所以为了避免滑动时也触发,加入变量处理 switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
isMove = true;
break;
case MotionEvent.ACTION_UP:
if (!isMove){ // 判断只有点击时进行回调
int position = (int) (event.getX() / itemWidth); // 取得点击的位置
mOnItemClickListener.onItemClick(this, mElements.get(position)); // 回调
}
break;
} return true;
}
④ 在Activity中,进行监听设置,并处理:
historyGraph.setOnItemClickListener(this);
@Override
public void onItemClick(View view, TrendGraph.Element element) {
int dt = (int) (element.getUp() - element.getDown());
Snackbar.make(root, "当天温差为:" + dt + "℃", Snackbar.LENGTH_SHORT).show();
}
效果完成!欢迎大家关注交流。
Android开发学习之路-自定义控件(天气趋势折线图)的更多相关文章
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android开发学习之路--基于vitamio的视频播放器(二)
终于把该忙的事情都忙得差不多了,接下来又可以开始good good study,day day up了.在Android开发学习之路–基于vitamio的视频播放器(一)中,主要讲了播放器的界面的 ...
- Android开发学习之路--Android Studio cmake编译ffmpeg
最新的android studio2.2引入了cmake可以很好地实现ndk的编写.这里使用最新的方式,对于以前的android下的ndk编译什么的可以参考之前的文章:Android开发学习之路– ...
- Android开发学习之路--网络编程之xml、json
一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...
- Android开发学习之路--Activity之初体验
环境也搭建好了,android系统也基本了解了,那么接下来就可以开始学习android开发了,相信这么学下去肯定可以把android开发学习好的,再加上时而再温故下linux下的知识,看看androi ...
- Android开发学习之路--Android系统架构初探
环境搭建好了,最简单的app也运行过了,那么app到底是怎么运行在手机上的,手机又到底怎么能运行这些应用,一堆的电子元器件最后可以运行这么美妙的界面,在此还是需要好好研究研究.这里从芯片及硬件模块-& ...
- Android开发学习之路--MAC下Android Studio开发环境搭建
自从毕业开始到现在还没有系统地学习android应用的开发,之前一直都是做些底层的驱动,以及linux上的c开发.虽然写过几个简单的app,也对android4.0.3的源代码做过部分的分析,也算入门 ...
- Android开发学习之路-记一次CSDN公开课
今天的CSDN公开课Android事件处理重难点快速掌握中老师讲到一个概念我觉得不正确. 原话是这样的:点击事件可以通过事件监听和回调两种方法实现. 我一听到之后我的表情是这样的: 这跟我学的看的都不 ...
- Android开发学习之路-Android Studio开发小技巧
上一次发过了一个介绍Studio的,这里再发一个补充下. 我们都知道,Android Studio的功能是非常强大的,也是很智能的.如果有人告诉你学Android开发要用命令行,你可以告诉他Andro ...
随机推荐
- 学习AOP之深入一点Spring Aop
上一篇<学习AOP之认识一下SpringAOP>中大体的了解了代理.动态代理及SpringAop的知识.因为写的篇幅长了点所以还是再写一篇吧.接下来开始深入一点Spring aop的一些实 ...
- DynamicObject - 代理对象的种类
开箱即用,DynamicProxy提供了多种代理对象,主要分成两个大类: 基于继承(Inheritance-based) 基于继承的代理是通过继承一个代理类来实现,代理拦截对类的虚(virtual)成 ...
- Enterprise Solution 3.1 企业应用开发框架 .NET ERP/CRM/MIS 开发框架,C/S架构,SQL Server + ORM(LLBL Gen Pro) + Infragistics WinForms
行业:基于数据库的制造行业管理软件,包含ERP.MRP.CRM.MIS.MES等企业管理软件 数据库平台:SQL Server 2005或以上 系统架构:C/S 开发技术 序号 领域 技术 1 数据库 ...
- AutoFac在项目中的应用
技能大全:http://www.cnblogs.com/dunitian/p/4822808.html#skill 完整Demo:https://github.com/dunitian/LoTCode ...
- Python-Jenkins API使用 —— 在后端代码中操控Jenkins
最近在工作中需要用到在后台代码中触发Jenkins任务的构建,于是想到Jenkins是否有一些已经封装好的API类库提供,用于处理跟Jenkins相关的操作.下面就简单介绍下我的发现. Linux C ...
- 星浩资本快速发展引擎:IT就是生产力
星浩资本成立于2010年,是一家涵盖私募基金.开发管理.商业与现代服务业三大业务范围的综合性管理公司,专注于投资中国首创.高成长性.高回报率的创新型城市综合体. 年轻的星浩资本在商业投资上有其独到的商 ...
- OpenDigg前端开源项目周报1219
由OpenDigg 出品的前端开源项目周报第二期来啦.我们的前端开源周报集合了OpenDigg一周来新收录的优质的前端开发方面的开源项目,方便前端开发人员便捷的找到自己需要的项目工具等.react-f ...
- Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define ...
Conversion to Dalvik format failed: Unable to execute dex: Multiple dex files define ... 这个错误是因为有两个相 ...
- IOS开发基础知识--碎片51
1:https关闭证书跟域名的验证 AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy]; securityPolic ...
- uboot环境配置
uboot环境配置 通过配置uboot让它在启动过程中从tftp获取内核和设备树,并从在加载内核之后把通过启动参数将"从nfs挂载根文件系统"传入内核.这个配置主要是通过uboot ...