Android自定义控件:带你掌握一款多特效的智能loadingView
/**
简单的绘制文字,没有考虑文字长度超过控件长度
*/
//这里是居中显示
textPaint.setTextAlign(Paint.Align.CENTER);
//参数1:文字
//参数2,3:绘制文字的中心点
//参数4:画笔
canvas.drawText(buttonString, textRect.centerX(), baseline, textPaint);
}
4、【自定义控件属性】
<?xml version="1.0" encoding="utf-8"?>
这里以,文案为例, textStr。比如你再布局种用到app:txtStr=“文案内容”。在自定义控件里获取如下:
public SmartLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//自定义控件的3参方法的attrs就是我们设置自定义属性的关键
//比如我们再attrs.xml里自定义了我们的属性,
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SmartLoadingView);
//这里是获取用户有没有设置整个属性
//这里是从用户那里获取有没有设置文案
String title = typedArray.getString(R.styleable.SmartLoadingView_textStr);
if (TextUtils.isEmpty(title)){
//如果获取来的属性是空,那么可以默认一个属性
//(作者忘记设置了!因为已经发布后期优化,老尴尬了)
buttonString =“默认文案”;
}else{
//如果有设置文案
buttonString = title;
}
}
5、【设置点击事件,启动动画】
为了点击事件的直观,也可以把处理防止重复点击事件封装在里面
//这是我自定义登录点击的接口
public interface LoginClickListener {
void click();
}
public void setLoginClickListener(final LoginClickListener loginClickListener) {
this.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (loginClickListener != null) {
//防止重复点击
if (!isAnimRuning) {
start();
loginClickListener.click();
}
}
}
});
}
6、【动画讲解】
6.1、第一个动画,矩形到正方形,以及矩形到圆角矩形(这里是2个动画,只是同时进行)
矩形到正方形(为了简化,我把源码一些其他属性去掉了,这样方便理解)
//其中 default_all_distance = (w - h) / 2;除以2是因为2遍都往中间缩短
private void set_rect_to_circle_animation() {
//这是一个属性动画,current_left 会在duration时间内,从0到default_all_distance匀速变化
//想添加多样化的话 还可以加入插值器。
animator_rect_to_square = ValueAnimator.ofInt(0, default_all_distance);
animator_rect_to_square.setDuration(duration);
animator_rect_to_square.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//这里的current_left跟onDraw相关,还记得吗
//onDraw里的控件区域
//控件左边区域 rectf.left = current_left;
//控件右边区域 rectf.right = width - current_left;
current_left = (int) animation.getAnimatedValue();
//刷新绘制
invalidate();
}
});
矩形到圆角矩形。就是从一个没有圆角的变成完全圆角的矩形,当然我展示的时候只有第三个图,最后一个按钮才明显了。
其他的我直接设置成了圆角按钮,因为我把圆角做成了一个属性。
还记得onDraw里的canvas.drawRoundRect(rectf, circleAngle, circleAngle, paint);circleAngle就是圆角的半径
可以想象一下如果全是圆角,那么circleAngle会是多少,当然是height/2;没错吧,所以
因为我把圆角做成了属性obtainCircleAngle是从xml文件获取的属性,如果不设置,则为0,就没有任何圆角效果
animator_rect_to_angle = ValueAnimator.ofInt(obtainCircleAngle, height / 2);
animator_rect_to_angle.setDuration(duration);
animator_rect_to_angle.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//这里试想下如果是一个正方形,刚好是圆形的圆角,那就是一个圆
circleAngle = (int) animation.getAnimatedValue();
//刷新绘画
invalidate();
}
});
2个属性动画做好后,用 private AnimatorSet animatorSet = new AnimatorSet();把属性动画加进去,可以设置2个动画同时进行,还是先后顺序 这里是同时进行所用用with
animatorSet
.play(animator_rect_to_square).with(animator_rect_to_angle);
6.2、变成圆形后,有一个loading加载动画
这里就是画圆弧,只是不断改变,圆弧的起始点和终点,最终呈现loading状态,也是在onDraw里
//绘制加载进度
if (isLoading) {
//参数1:绘制圆弧区域
//参数2,3:绘制圆弧起始点和终点
canvas.drawArc(new RectF(width / 2 - height / 2 + height / 4, height / 4, width / 2 + height / 2 - height / 4, height / 2 + height / 2 - height / 4), startAngle, progAngle, false, okPaint);
//这里是我通过实践,实现最佳loading动画
//当然这里有很多方式,因为我自定义这个view想把所有东西都放在这个类里面,你也可以有你的方式
//如果有更好的方式,欢迎留言,告知我一下
startAngle += 6;
if (progAngle >= 270) {
progAngle -= 2;
isAdd = false;
} else if (progAngle <= 45) {
progAngle += 6;
isAdd = true;
} else {
if (isAdd) {
progAngle += 6;
} else {
progAngle -= 2;
}
}
//刷新绘制,这里不用担心有那么多刷新绘制,会不会影响性能
//
postInvalidate()
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
;
}
6.3、loading状态,到打勾动画
那么这里首先要把loading动画取消,那么直接改变isLoading=false;不会只它同时启动打勾动画;打勾动画的动画,这里比较麻烦,也是我在别人自定义动画里学习的,通过PathMeasure,实现路径动画
/**
- 路径–用来获取对勾的路径
*/
private Path path = new Path();
/**
- 取路径的长度
*/
private PathMeasure pathMeasure;
//初始化打勾动画路径;
private void initOk() {
//对勾的路径
path.moveTo(default_all_distance + height / 8 * 3, height / 2);
path.lineTo(default_all_distance + height / 2, height / 5 * 3);
path.lineTo(default_all_distance + height / 3 * 2, height / 5 * 2);
pathMeasure = new PathMeasure(path, true);
}
//初始化打勾动画
private void set_draw_ok_animation() {
animator_draw_ok = ValueAnimator.ofFloat(1, 0);
animator_draw_ok.setDuration(duration);
animator_draw_ok.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
startDrawOk = true;
isLoading = false;
float value = (Float) animation.getAnimatedValue();
effect = new DashPathEffect(new float[]{pathMeasure.getLength(), pathMeasure.getLength()}, value * pathMeasure.getLength());
okPaint.setPathEffect(effect);
invalidate();
}
});
}
//启动打勾动画只需要调用
animator_draw_ok.start();
onDraw里绘制打勾动画
//绘制打勾,这是onDraw的,startDrawOk是判断是否开启打勾动画的标识
if (startDrawOk) {
canvas.drawPath(path, okPaint);
}
6.4、loading状态下回到失败样子(有点类似联网失败了)
之前6.1提到了矩形到圆角矩形和矩形到正方形的动画,
那么这里只是前面2个动画反过来,再加上联网失败的文案,和联网失败的背景图即刻
6.5、loading状态下启动扩散全屏动画(重点)
这里我通过loginSuccess里参数的类型启动不同效果:
1、启动扩散全屏动画
public void loginSuccess(Animator.AnimatorListener endListener) {}
2、启动打勾动画
public void loginSuccess(AnimationOKListener animationOKListener) {}
启动扩散全屏是本文的重点,里面还涉及到了一个自定义view
CirclBigView,这个控件是全屏的,而且是从一个小圆不断改变半径变成大圆的动画,那么有人会问,全屏肯定不好啊,会影响布局,
但是这里,我把它放在了activity的视图层:
ViewGroup activityDecorView = (ViewGroup) ((Activity) getContext()).getWindow().getDecorView();
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
activityDecorView.addView(circlBigView, layoutParams);
这个灵感也是前不久在学习微信,拖拽退出的思路里发现的。全部代码如下:
public void toBigCircle(Animator.AnimatorListener endListener) {
//把缩小到圆的半径,告诉circlBigView
circlBigView.setRadius(this.getMeasuredHeight() / 2);
//把当前背景颜色告诉circlBigView
circlBigView.setColorBg(normal_color);
int[] location = new int[2];
//测量当前控件所在的屏幕坐标x,y
this.getLocationOnScreen(location);
//把当前坐标告诉circlBigView,同时circlBigView会计算当前点,到屏幕4个点的最大距离,即是当前控件要扩散到的半径
//具体建议读者看完本博客后,去下载玩耍下。
circlBigView.setXY(location[0] + this.getMeasuredWidth() / 2, location[1]);
if (circlBigView.getParent() == null) {
ViewGroup activityDecorView = (ViewGroup) ((Activity) getContext()).getWindow().getDecorView();
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
activityDecorView.addView(circlBigView, layoutParams);
}
circlBigView.startShowAni(endListener);
isAnimRuning = false;
}
结束语
因为项目是把之前的功能写成了控件,所以有很多地方不完善。希望有建议的大牛和小伙伴,提示提示我,让我完善的更好。谢谢
希望读到这的您能转发分享和关注一下我,以后还会分享Android知识点及解析,您的支持就是我最大的动力!!
学习分享,共勉
题外话,我从事Android开发已经五年了,此前我指导过不少同行。但很少跟大家一起探讨,正好最近我花了一个多月的时间整理出来一份包括不限于高级UI、性能优化、移动架构师、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术,今天暂且开放给有需要的人,若有关于此方面可以关注+点赞后领取,或者评论与我一起交流探讨。
Android自定义控件:带你掌握一款多特效的智能loadingView相关推荐
- android 自定义 seekbar,Android自定义控件 带文字提示的SeekBar
封面 1.写在前面 SeekBar控件在开发中还是比较常见的,比如音视频进度.音量调节等,但是原生控件有时还不能满足我们的需求,今天就来学习一下如何自定义SeekBar控件,本文主要实现了一个带文字指 ...
- Android 自定义控件之圆形页面指示器CirclePageIndicator带划动效果
Android 自定义控件之圆形页面指示器CirclePageIndicator带划动效果 前言 感谢 效果图 目标 流程 自定义属性 自定义默认属性 自定义接口 创建控件类继承View 声明属性变量 ...
- 安卓网apk安装器_短视频无水印下载,apk提取,带壳截图 iOS Android极客必备的三款软件,你全都用上了吗?...
短视频无水印下载,apk提取,带壳截图 iOS Android 极客必备的三款软件,你全都用上了吗? 7SIZE丨插图内容来自于网络侵权删 给大家介绍 Android,iOS 上三款在生活中必备的三款 ...
- Android自定义控件开发系列(零)——基础原理篇
在后边的文章中发现在说Android自定义时,有时候要重复解释很多东西,所以想想返回来增加一篇"基础原理篇",直接进入正题吧-- 首先的问题是:在Android项目开发中,什么时候 ...
- android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...
转载:http://blog.csdn.net/xiabing082/article/details/48781489 1. 大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...
- Android 自定义控件打造史上最简单的侧滑菜单
侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑动时,通过Scroller或者不断的改变leftMargin ...
- Android.自定义控件的实现 (转载)
尊重他人劳动成果,转载请注明出处. 转自:http://kandy0619.blog.163.com/blog/static/64344345201012325939280/ 可能是一直都在做Web的 ...
- android 自定义进度条 水量,Android自定义带水滴的进度条样式(带渐变色效果)...
一.直接看效果 二.直接上代码 1.自定义控件部分 package com.susan.project.myapplication; import android.app.Activity; impo ...
- Android自定义控件学习(一)-----属性
Android中XML的命名空间.自定义属性 命名空间(namespace) XML 命名空间提供避免元素命名冲突的方法. 打个比方,A学校有名学生叫做林小明,B学校也有名学生叫林小明,那我们如何识别 ...
最新文章
- Java Process类的浅学习
- objective-c中Category类别(扩展类)专题总结
- Atitit. 高级软件工程师and 普通的区别 高级编程的门槛总结
- oracle中的greatest,ORACLE 内置函数之 GREATEST 和 LEAST(转)
- python登录网站 爬虫_Python爬虫如何使用Cookies登录网站
- asp.net core程序在k8s中基于rabbitmq队列消息数的HPA实践!
- 用python祝福父亲节_父亲节到来 最适合给年老的父亲祝福语
- react-native icon使用方式
- HDU3783 ZOJ【文本处理】
- spark++读写mysql_spark读写mysql
- uboot 之环境变量
- linux7.2 网卡设置,CentOS 7.2网络配置
- SitePoint播客#108:Kevin的最后一场演出
- 【python】画图保存为emf
- html5简单在线绘图,html5实现一个简单的在线画板
- java 字母大小写的转换_Java字母大小写转换的方法
- REST-assured简介
- 无惧黑暗强光,纯视觉导航也能全天候作业
- CESIUM例子学习(十一)——3DTiles Clipping Planes(1)
- 法国电信公司Orange削减英国电信集团股份至1.33%