炫酷的空调调节器控件
来源:互联网 发布:古罗马 实力 知乎 编辑:程序博客网 时间:2024/04/28 14:41
介绍
最近工作有一些闲暇时间,本身项目并不是自己的,看着控件挺有意思的,而且效果市面上比较少见.抱着学习态度,撸了这个View.
转载请标明出处:
http://blog.csdn.net/u012401802/article/details/78543322来自卡斯迪奥-北京的博客
github下载地址:https://github.com/liujiayu5566/AirConditionerView
看一下UI提供的效果
先看一下最终效果
代码实现
1.res/values/attr.xml
自定义属性: #
<?xml version="1.0" encoding="utf-8"?><resources><declare-styleable name="CirqueView"> <attr name="temperature_min" format="integer">10</attr> <attr name="temperature_max" format="integer">30</attr> <attr name="time_left" format="integer">10</attr> <attr name="time_right" format="integer">30</attr> </declare-styleable></resources>
简单介绍一下:
temperature_min: 温度范围最小值.
temperature_max: 温度范围最大值.
time_left: 时间范围左侧数值.
time_right: 时间范围右侧数值.
CirqueView
2.获取自定义属性
public CirqueView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.context = context; initPaint(); startAnim(); TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CirqueView, defStyleAttr, 0); minTxt = ta.getInteger(R.styleable.CirqueView_temperature_min, 10); maxTxt = ta.getInteger(R.styleable.CirqueView_temperature_max, 30); timeMinTxt = ta.getInteger(R.styleable.CirqueView_time_left, 10); timeMaxTxt = ta.getInteger(R.styleable.CirqueView_time_right, 30); ta.recycle(); timeInitial = 165;}
3.重写onMeasure方法
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(measure(widthMeasureSpec), measure(heightMeasureSpec));}private int measure(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { //这样,当时用wrap_content时,View就获得一个默认值200px,而不是填充整个父布局。 result = DensityUtil.dip2px(context, 200); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result;}
很通用的代码.
4.重写onSizeChanged方法
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); radius = (getWidth() / 2) - DensityUtil.dip2px(context, 20); oval.set(getWidth() / 2 - radius - defaultValue, getHeight() / 2 - radius - defaultValue, getWidth() / 2 + radius + defaultValue, getHeight() / 2 + radius + defaultValue); mSweepGradient = new SweepGradient(getWidth() / 2, getHeight() / 2, colors, floats); cirque.setShader(mSweepGradient);}
radius计算半径,oval.set()设置绘制的范围,设置渐变颜色.
5.重写onDraw方法
@Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); //背景圆以及背景虚化 canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius + DensityUtil.dip2px(context, 5), circularBackground); canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius, circularPaint); //弧形背景 canvas.drawArc(oval, 195, 150, false, cirqueBackground);//小弧形 canvas.drawArc(oval, 15, 150, false, cirqueBackground);//小弧形 //温度绘制弧形 if (mCurrentAngle != 0) { canvas.drawArc(oval, 195, mCurrentAngle, false, cirque);//小弧形 } //时间绘制弧形 if (mTimeCurrentAngle != 0) { canvas.drawArc(oval, 165, -mTimeCurrentAngle, false, timecirque);//小弧形 } //文字以及图标绘制 int v = maxTxt - minTxt; int timeV = timeMaxTxt - timeMinTxt; text = Math.round(mCurrentAngle / (150f / v)) + minTxt + "℃"; timeText = Math.round(mTimeCurrentAngle / (150f / timeV)) + timeMinTxt + "min"; textPaint.setColor(colors[(int) (mCurrentAngle / (15 + 0.5f))]); canvas.drawText(text, getWidth() / 2 - textPaint.measureText(text) / 2, getHeight() / 2 - DensityUtil.dip2px(context, 5), textPaint); canvas.drawText(timeText, getWidth() / 2 - timeTextPaint.measureText(timeText) / 2, getHeight() / 2 + DensityUtil.dip2px(context, 20), timeTextPaint); canvas.drawText(timeMinTxt + "", getWidth() / 2 - radius - defaultValue - timeTextPaint.measureText(timeMinTxt + "") * 1 / 3 + value, getHeight() / 2 + DensityUtil.dip2px(context, 17), unitPaint); canvas.drawText(timeMaxTxt + "", getWidth() / 2 + radius + defaultValue - timeTextPaint.measureText(timeMaxTxt + "") * 1 / 3 - value, getHeight() / 2 + DensityUtil.dip2px(context, 17), unitPaint); canvas.drawBitmap(snowflake, getWidth() / 2 - radius - defaultValue - snowflake.getWidth() / 2 + value, getHeight() / 2 - DensityUtil.dip2px(context, 17), bitmapPaint); canvas.drawBitmap(sun, getWidth() / 2 + radius + defaultValue - sun.getWidth() / 2 - value, getHeight() / 2 - DensityUtil.dip2px(context, 17), bitmapPaint); //刻度绘制竖线 linePaint.setColor(colors[(int) (mCurrentAngle / (15 + 0.5f))]); canvas.rotate(mCurrentAngle + 15f, getWidth() / 2, getHeight() / 2); canvas.drawLine(getWidth() / 2 - radius - defaultValue - DensityUtil.dip2px(context, 1), getHeight() / 2 - DensityUtil.dip2px(context, 1), getWidth() / 2 - radius * 3 / 4, getHeight() / 2, linePaint); canvas.rotate(-mTimeCurrentAngle - 30f - mCurrentAngle, getWidth() / 2, getHeight() / 2); canvas.drawLine(getWidth() / 2 - radius - defaultValue - DensityUtil.dip2px(context, 1), getHeight() / 2 + DensityUtil.dip2px(context, 1), getWidth() / 2 - radius * 3 / 4, getHeight() / 2, timeLinePaint); if (txtFinishListener != null) { //监听 txtFinishListener.onFinish(text, timeText); }}
onDraw方法中,绘制整个View.
第5行根据UI图背景边缘有蓝色的虚化效果,绘制一个比背景大5dp的实体圆,通过Paint.setMaskFilter(new BlurMaskFilter(30, BlurMaskFilter.Blur.NORMAL))方法实现边缘虚幻.
第23/32行,计算温度展示以及圆弧垂直直线的颜色,加入0.5f防止越界.
6.重写onTouchEvent方法
@Overridepublic boolean onTouchEvent(MotionEvent event) { /*获取点击位置的坐标*/ float Action_x = event.getX(); float Action_y = event.getY(); /*根据坐标转换成对应的角度*/ float get_x0 = Action_x - getWidth() / 2; float get_y0 = Action_y - getHeight() / 2; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (Action_y < getHeight() / 2) { //温度 tag = true; } else {//时间 tag = false; } break; case MotionEvent.ACTION_MOVE: if (tag) { //温度 /*02:左上角区域*/ if (get_x0 <= 0 & get_y0 <= 0) { float tan_x = get_x0 * (-1); float tan_y = get_y0 * (-1); double atan = Math.atan(tan_y / tan_x); mCurrentAngle = (int) Math.toDegrees(atan); } /*03:右上角区域*/ if (get_x0 >= 0 & get_y0 <= 0) { float tan_x = get_x0; float tan_y = get_y0 * (-1); double atan = Math.atan(tan_x / tan_y); mCurrentAngle = (int) Math.toDegrees(atan) + 90f; } if (Math.abs(mCurrentAngle) <= 1 || (get_x0 <= 0 & get_y0 >= 0)) mCurrentAngle = 0; if (Math.abs(mCurrentAngle) >= 149 || (get_x0 >= 0 & get_y0 >= 0)) mCurrentAngle = 150; } else if (!tag) { //时间 /*01:左下角区域*/ if (get_x0 <= 0 & get_y0 >= 0) { float tan_x = get_x0 * (-1); float tan_y = get_y0; double atan = Math.atan(tan_x / tan_y); mTimeCurrentAngle = -(int) Math.toDegrees(atan) + 90f; } /*04:右下角区域*/ if (get_x0 >= 0 & get_y0 >= 0) { float tan_x = get_x0; float tan_y = get_y0; double atan = Math.atan(tan_y / tan_x); mTimeCurrentAngle = -(int) Math.toDegrees(atan) + 180f; } if (Math.abs(mTimeCurrentAngle) <= 1 || (get_x0 <= 0 & get_y0 <= 0)) mTimeCurrentAngle = 0; if (Math.abs(mTimeCurrentAngle) >= 149 || (get_x0 >= 0 & get_y0 <= 0)) mTimeCurrentAngle = 150; } break; case MotionEvent.ACTION_POINTER_UP: break; } /*得到点的角度后进行重绘*/ invalidate(); return true;}
onTouchEvent方法控制触摸事件.
但是原理很简单,简单看一下.
第4-8行:计算触摸点的位置,get_x0<0在触摸点控件左侧,反之在控件右侧.通过这种方式,将整个控件根据圆心分为4个区域.
第10行:根据按下操作,确定控制上下哪个方向.
第17行:计算滑动位置的角度.注意:滑动范围150°不能超出范围,以及滑动另一区域,也需要做对应处理.(34-37行,55-58行)
调用方法
CirqueView mCv = (CirqueView) findViewById(R.id.cv);// mCv.setTemperaturemin(-30, 30); //设置温度范围 默认10-30// mCv.setTime(0, 60); //设置时间范围 默认10-30 mCv.setDefault(27, 22); //添加默认数据--注:不能超出范围 mCv.setTxtFinishListener(new CirqueView.txtFinishListener() { @Override public void onFinish(String temperature, String time) { Util.showToast(MainActivity.this, temperature + "//" + time); } });
回调返回String类型数值.
总结
完成时间大概是3天时间,之前对自定义View这块很薄弱,相关API记忆不是很深,查了一些相关博客,对自己有很大帮助.还是多撸撸代码成长很大(撸代码上班时间过的非常快^_^!).
相关博客:
http://blog.csdn.net/rhljiayou/article/details/7212620
http://blog.csdn.net/yanbober/article/details/50577855?locationNum=1&fps=1
- 炫酷的空调调节器控件
- 炫酷的环形调节器控件 : RegulatorView
- smarty的变量调节器
- MATLAB的PID调节器
- Smarty的变量调节器
- 调节器
- android自定义调节器控件 —— RegulatorView
- 高效的使用空调
- 亲历史上最棒的空调
- 正确的开关空调
- 正确的开关空调
- 正确的开关空调
- 正确的开关空调
- Smarty变量调节器的使用
- Smarty变量调节器的使用
- 机房空调的负荷计算
- 空调为什么变频的好
- 分体式空调的特点
- Spring依赖注入
- Ueditor 表情本地化
- UTC 与 Unix时间戳
- 【Android training】应用程序内容共享
- vim基本快捷键
- 炫酷的空调调节器控件
- 移植内核到s3c2440
- Ubuntu 16.04下Redis Cluster集群搭建
- java集合框架概述
- Mac上传项目源代码至GitHub
- 1128. N Queens Puzzle (20)
- netstat和telnet命令在Windows7中的用法
- CentOS crontab 定时任务不执行的常见解决方法
- StringBuffer的append拼接和String的+连接效率对比