先来看下效果:
控件内容比较简单,就是一个普通的折线图,上下分别带有数字,点击的时候显示当天温度的差值。
创建一个类继承自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能有所帮助。
【2018.11】预见未来|NLP将迎来黄金十年(MSRA) https://www.toutiao.com/a6628158223692071427 - 今日头条 NLP 新的发展基础 来自各个行业的文本大数据将会更好地采集、加工、入库; 来自搜索引擎、客服、商业智能、语音助手、翻译、教育、法律、金融等领域对NLP的需求会大幅度上升,对NLP质量也提出更高的要求; 文本数据和语音、图像数据的多模
趋势显示当前值与上一个值相比呈上升或下降趋势,并且显示百分比变化。 图表属性 选择图表类型后,可以更改其属性来自定义图表: 选项 描述 常规 背景颜色 设置图表区域的背景颜色。 不透明度 设置背景颜色的不透明度。 显示边框 显示图表外部边框。 边界颜色 设置图表外部边框的颜色。 显示标题 显示图表的主要标题。 标题 指定图表的标题。 标题字体 设置标题的字体样式。 位置 设置标题的位置。 对齐 设
趋势显示当前值与上一个值相比呈上升或下降趋势,并且显示百分比变化。 图表属性 选择图表类型后,可以更改其属性来自定义图表: 选项 描述 常规 背景颜色 设置图表区域的背景颜色。 显示边框 显示图表外部边框。 边界颜色 设置图表外部边框的颜色。 显示标题 显示图表的主要标题。 标题 指定图表的标题。 标题字体 设置标题的字体样式。 位置 设置标题的位置。 对齐 设置标题的水平对齐方式。 数据 显示先
趋势显示当前值与上一个值相比呈上升或下降趋势,并且显示百分比变化。 图表属性 选择图表类型后,可以更改其属性来自定义图表: 选项 描述 常规 背景颜色 设置图表区域的背景颜色。 不透明度 设置背景颜色的不透明度。 显示边框 显示图表外部边框。 边界颜色 设置图表外部边框的颜色。 显示标题 显示图表的主要标题。 标题 指定图表的标题。 标题字体 设置标题的字体样式。 位置 设置标题的位置。 对齐 设
面试官全程无表情,但几次都快被我整乐了,为啥啥不会,问就是了解但不熟悉,啊不会,啊不知道 自我介绍 介绍项目 登录验证怎么实现 了解python装饰器嘛 怎么给github上的项目提pr,特别是很大的项目 了解linux命令吗,说几个常见的 如何使用linux命令查找英文文档中出现频率最高的10个单词 输出password文件所有的用户名机及其主机 题目: 给你一个数组,输出该数组的最长递增子序列
#24届软开秋招面试经验大赏#10月24号 面经:(1h) 1:介绍下实验室的深度学习的项目; 2:讲一讲java的集合,hashmap源码里面有哪些让你印象深刻的代码(我答的是扩容时候迁移节点时候的计算下标的代码,&上hashmap原来的长度) 3:打开一个网址之后会发生什么(查询DNS,TCP, HTTP请求一条龙) 4:ARP协议 5:端口映射,NAT转换 6:为什么想来趋势,从哪里了解趋势