自定义View实战
PS:上一篇从0开始学自定义View有博友给我留言说要看实战,今天我特意写了几个例子,供大家参考,所画的图案加上动画看着确实让人舒服,喜欢的博友可以直接拿到自己的项目中去使用,由于我这个写的是demo,代码格式写的有些乱,所以,要自己封装一下才可以使用,当然你如果真的不想封装,可以直接使用,也可以给我留言,我封装好放在github上供大家参考,也会做成依赖让大家直接添加即可
先上图再分析

可以看出图中有三种样式
- 第一种是普通的一个label,使用场景:商品过期,促销等展示。
- 第二种是圆形进度条, 使用场景:下载文件进度,加载视频进度,耗电量进度.....
- 第三种是条形进度条, 使用场景:滑动调值,手机音效大小...
上面的三种,均是demo,考虑使用场景并不完善,比如说第三种条形进度条还可以加上刻度,滑动到两边需要判断越界等。那就先拿第三个来吧
条形进度条-可拖动
分析:我们想要做一个类似的控件,需要考虑的问题不只是眼睛看的到的,看不到的就好比我只能点击小红球才可以滑动,我点击其他区域是不能有任何操作的,这个时候就要判断手指down的时候是否落在了小球上。
- 线条 : 渐变颜色,线帽格式,长度,宽度设置,父布局宽高格式设置格式设置,子view宽高格式设置
- 球 :颜色,起始位置和终止位置要在线上,尺寸
在做之前我们先一个一个知识点解析,首先是线的渐变颜色,单独拿出
/**
* 设置进度圆环颜色(支持渐变色)
*
* @param colorArray 渐变色集合
*/
private int[] mColorArray; // 圆环渐变色
public void setProgColor(@ColorRes int[] colorArray) {
if (colorArray == null || colorArray.length < 2) return;
mColorArray = new int[colorArray.length];
for (int index = 0; index < colorArray.length; index++)
mColorArray[index] = ContextCompat.getColor(getContext(), colorArray[index]);
paint.setShader(new LinearGradient(0, 0, getMeasuredWidth(),0 , mColorArray, new float[]{0,.3F,.6F,.9F}, Shader.TileMode.MIRROR));
invalidate();
}
我们可以看到我封装成了一个方法,通过paint.setShader进行着色,方法传入的是LinearGradient对象,我们看源码,解释参数
/**
* Create a shader that draws a linear gradient along a line.
*
* @param x0 The x-coordinate for the start of the gradient line
* @param y0 The y-coordinate for the start of the gradient line
* @param x1 The x-coordinate for the end of the gradient line
* @param y1 The y-coordinate for the end of the gradient line
* @param colors The colors to be distributed along the gradient line
* @param positions May be null. The relative positions [0..1] of
* each corresponding color in the colors array. If this is null,
* the the colors are distributed evenly along the gradient line.
* @param tile The Shader tiling mode
*/
public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
@Nullable float positions[], @NonNull TileMode tile) {
if (colors.length < 2) {
throw new IllegalArgumentException("needs >= 2 number of colors");
}
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException("color and position arrays must be of equal length");
}
mType = TYPE_COLORS_AND_POSITIONS;
mX0 = x0;
mY0 = y0;
mX1 = x1;
mY1 = y1;
mColors = colors.clone();
mPositions = positions != null ? positions.clone() : null;
mTileMode = tile;
}
参数
- x0,y0着色的起始位置
- x1,y1终止位置
- colors区域内着色的颜色集
- positions区域内部划分模块,逐一着色,如:区域1-100划分为4块,第一块1-25红色,26-50蓝色..。系统会默认在两种颜色不一样的情况下进行颜色过度渲染,达到渐变的效果,所以我们不用担心出现红蓝划分明显的情况。
- TileMode 模式选择
- CLAMP:当图片小于绘制尺寸时要进行边界拉伸来填充
- REPEAT:当图片小于绘制尺寸时重复平铺
- MIRROR:当图片小于绘制尺寸时镜像平铺
好了,下面我们就先画线和红点,如果有看不懂的博友,可以先看上一篇从0开始学自定义View。
public void init(){
paint = new Paint();
paint.setStrokeWidth(20);//画笔宽度
paint.setStyle(Paint.Style.FILL);//填充类型
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);//线帽,半圆
paintCircle2 = new Paint();
paintCircle2.setColor(Color.RED);
paintCircle2.setStrokeWidth(5);
paintCircle2.setStyle(Paint.Style.FILL);
paintCircle2.setAntiAlias(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
heigth = MeasureSpec.getSize(heightMeasureSpec);
Log.e("heigth -- width ",heigth+" -- "+width);
circleX=width/2;//初始化红点坐标位置
circleY=20;
}
int[] colors={R.color.colorPrimary,R.color.colorAccent,R.color.color_environment_serious,R.color.color_environment_mild};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setProgColor(colors);
//绘制一条线,线帽为半圆
canvas.drawLine(50,20,width-50,20,paint);
canvas.drawCircle(circleX,circleY,15,paintCircle2);
}
到这里,线和点就已经做好了,只是静态的,下面是如何拖动,就要在onTouchEvent方法中去写了,代码都已经添加了注释 Math.abs(dhx)<50&&Math.abs(dhy)<50 是证明down的坐标点和原始球的坐标点相差范围在50内,如果小球在屏幕200,200的位置,而我们手指down的点在800,800,那么相差如此巨大,肯定不是我们想要的结果,所以,我们就认为down的坐标减去球的坐标差值最小(50内)才是我们想要的结果,这个时候我们再设置小球move的坐标(让小球跟随手指移动)。
/**
* 判断所按压的坐标和红点坐标的关系
* 如果手指按在了红点的下方,那么down-红点y坐标的绝对值如果等于或者小于半径,也就是说目前按压的就是红点,
*
* */
private boolean clickCircle(int downHeigth,int downWidth){
int dhy = downHeigth - circleY;
int dhx = downWidth - circleX;
//证明按压的是原点
if(Math.abs(dhx)<50&&Math.abs(dhy)<50){
return true;
}
return false;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
boolean isFlag=false;
@Override
public boolean onTouchEvent(MotionEvent event) {
int rawX = (int) event.getX();
int rawY = (int) event.getY();//获取到视图坐标,想对于外部viewgroup来说。
Log.e("raw",rawX+" --- "+rawY);
Log.e("rawwww",circleX+" --- "+circleY);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
//如果是手指down在了红点上,move时可重绘
if(clickCircle(rawY,rawX)) {
circleX = rawX;
isFlag=true;
}else{
isFlag=false;
}
break;
case MotionEvent.ACTION_MOVE:
if(isFlag){
circleX = rawX;
invalidate();
}
break;
case MotionEvent.ACTION_UP:
isFlag=false;//抬起手指,状态恢复
break;
}
return true;//自己消费
}
下一篇是圆形进度条
自定义View实战的更多相关文章
- Android自定义View实战(SlideTab-可滑动的选择器)
转载请标明出处: http://blog.csdn.net/xmxkf/article/details/52178553 本文出自:[openXu的博客] 目录: 初步分析重写onDraw绘制 重写o ...
- 自定义View实战--实现一个清新美观的加载按钮
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 在 Dribble 上偶然看到了一组交互如下: 当时在心里问自己能不能做,答案肯定是能做的,不过我比较懒,觉得中间那个伸缩变化要编写 ...
- 高级UI晋升之自定义View实战(九)
更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680 1.前言: 本文采用自定义view的方法来实现一键清除的动画这个功能. 2.效果 ...
- 高级UI晋升之自定义View实战(六)
更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章将从Android 自定义属性动画&Camera动画来介绍自定义V ...
- 高级UI晋升之自定义View实战(五)
更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章将从自定义View利器Canvas和Paint来进行详解 一.Canvas ...
- 高级UI晋升之自定义view实战(七)
更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章自定义ViewGroup实现瀑布流效果来进行详解dispatchTouch ...
- 转载:android自定义view实战(温度控制表)!
效果图 package cn.ljuns.temperature.view; import com.example.mvp.R; import android.content.Context;impo ...
- Android为TV端助力 转载:android自定义view实战(温度控制表)!
效果图 package cn.ljuns.temperature.view; import com.example.mvp.R; import android.content.Context;impo ...
- 高级UI晋升之自定义View实战(八)
更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680本篇文章自定义流式布局来进行介绍: 一般常见的流式布局由两种,一种是横向的个数固定 ...
随机推荐
- CH5E07 划分大理石(背包dp+二进制拆分)
传送门 大意: 有价值分别为1..6的大理石各a[1..6]块,现要将它们分成两部分,使得两部分价值之和相等,问是否可以实现.其中大理石的总数不超过20000. 解题思路: 妥妥的多重背包+二 ...
- Jbox弹窗控件无法获取子页面元素值得问题
top.$.jBox.open("iframe:${ctx}/report/reportSubjectDatabase/toChildWindow", "请选择重构快照表 ...
- SQL Server 2005 sa登录失败。已成功与服务器建立连接 但是在登录过程中发生错误。 provider 共享内存提供程序 error 0 管道的另一端上无任何进程。
SQL Server 2005 Express版 用户 'sa' 登录失败.该用户与可信 SQL Server 连接无关联.提示错误:已成功与服务器建立连接 但是在登录过程中发生错误. provide ...
- 干货:python面对对象类继承简介
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:python视觉算法 PS:如有需要Python学习资料的小伙伴可以加 ...
- 联通友华通信光纤猫PT952G设置无线路由光猫桥接拨号
#0x1 登陆后台,点击网络,点击宽带设置.选择第二个接口. 0x2 只修改模式,改成Bridge,其他无需修改.然后直接接路由器拨号就行,或者电脑都行. 0x4 恢复默认拨号,这样修改以后,直接连 ...
- ChaosBlade--动态脚本实现 Java 实验场景
动态脚本实现 : 参考文档:https://github.com/chaosblade-io/chaosblade/wiki/%E5%8A%A8%E6%80%81%E8%84%9A%E6%9C%AC% ...
- BUU刷题01
[安洵杯 2019]easy_serialize_php 直接给了源代码 <?php $function = @$_GET['f']; function filter($img){ $filte ...
- 小知识点:session的存放位置
在php.ini里的配置session.save_path是注释掉的,那么Seesion保存的路径在不同类型操作系统保存在什么位置? Linux: /tmp 或 /var/lib/php/sessio ...
- [YII2] 增删改查2
一.新增 使用model::save()操作进行新增数据 $user= new User; $user->username =$username; $user->password =$pa ...
- Ansible playbook 编程
Ansible playbook 编程详解与各种小案例 主机规划 添加用户账号 说明: 1. 运维人员使用的登录账号: 2. 所有的业务都放在 /app/ 下「yun用户的家目录」,避免业务数据乱放: ...