安卓设置菊花动画_Android 加载菊花图 自定义
本来想找一个ios的菊花加载图旋转一下搞定这个的,但是发现使用图片后由于图片的不对称导致动画时有抖动现象,不能忍,于是搜了下自定义菊花图,额,然后就没有然后了,都不是想要的,于是开始了苦逼的自定义View...
先来张效果图:
菊花图.png
再来张动画效果图:
嗯是的我动起来了.gif
看图说话:
1、12条颜色线,带圆角
2、动画起来后12个颜色跟着变化
需要解决的问题:
1、12个颜色,也太多了吧!!!!我可不想吸这么多次,有毒的!(嗯,我为吸管代言)
于是愉快的决定了只是用两种颜色,即开始颜色和结束颜色,其余使用渐变获取。(额,好吧,我是懒了!)
2、动画
什么,动画是问题吗?一个旋转搞定了!嗯,你大佬,你来,反正我做不来!!
由于旋转动画是转动的,难以实现颜色跟着变化的效果,所以我使用了ValueAnimator,
嗯,是的,你猜对了,既然使用了ValueAnimator,就要调用 invalidate(); 或 postInvalidate(); 来实现重绘。
嗯,看起来还是挺简单的,来试试吧:
先定义一下属性:
/**
* 线圆角及宽度
*/
private int mLineBold;
/**
* 线条开始颜色 默认白色
*/
private int mStartColor = Color.parseColor("#FFFFFF");
/**
* 线条结束颜色 默认灰色
*/
private int mEndColor = Color.parseColor("#9B9B9B");
/**
* view的宽度 高度
*/
private int mWidth;
/**
* view的高度
*/
private int mHeight;
/**
* 线条长度
*/
private int mLineLength;
/**
* 线条个数 默认12条
*/
private int mLineCount = 12;
/**
* 背景画笔
*/
private Paint mBgPaint;
/**
* 渐变颜色
*/
private int[] mColors;
/**
* 动画是否已开启
*/
private boolean isAnimationStart;
/**
* 开始index
*/
private int mStartIndex;
/**
* 动画
*/
private ValueAnimator mValueAnimator;
>>>>>>>>>>>>>>> 然后继续 >>>>>>>>>>>>>>>>>
1.第一步:
自定义属性:
// 由于考虑到后续可能使用更多数量的线,所以索性加了个线个数的属性
2.第二步,初始化:
public ChrysanthemumView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
loadAttrs(context, attrs);
initPaint();
initColor();
}
/**
* 加载自定义的属性
*/
private void loadAttrs(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ChrysanthemumView);
mStartColor = array.getColor(R.styleable.ChrysanthemumView_startColor, mStartColor);
mEndColor = array.getColor(R.styleable.ChrysanthemumView_endColor, mEndColor);
mLineCount = array.getInt(R.styleable.ChrysanthemumView_lineCount, mLineCount);
//TypedArray回收
array.recycle();
}
/**
* 初始化画笔
*/
private void initPaint() {
mBgPaint = new Paint();
//使得画笔更加圆滑
mBgPaint.setAntiAlias(true);
mBgPaint.setStrokeJoin(Paint.Join.ROUND);
mBgPaint.setStrokeCap(Paint.Cap.ROUND);
}
3.第三步,解决颜色的问题:
/**
* 初始化颜色
*/
private void initColor() {
// 渐变色计算类
ArgbEvaluator argbEvaluator = new ArgbEvaluator();
// 初始化对应空间
mColors = new int[mLineCount];
// 获取对应的线颜色 此处由于是白色起头 黑色结尾所以需要反过来计算 即线的数量到0的数量递减 对应的ValueAnimator 是从0到线的数量-1递增
for (int i = mLineCount; i > 0; i--) {
float alpha = (float) i / mLineCount;
mColors[mLineCount - i] = (int) argbEvaluator.evaluate(alpha, mStartColor, mEndColor);
}
}
4.第四步,解决尺寸的问题:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获取view的宽度 默认40dp
mWidth = getViewSize(dp2px(getContext(), 40), widthMeasureSpec);
// 获取view的高度 默认40dp
mHeight = getViewSize(dp2px(getContext(), 40), heightMeasureSpec);
// 使宽高保持一致
mHeight = mWidth = Math.min(mWidth, mHeight);
// 获取线的长度
mLineLength = mWidth / 6;
// 获取线圆角及宽度
mLineBold = mWidth / mLineCount;
// 设置线的圆角及宽度
mBgPaint.setStrokeWidth(mLineBold);
setMeasuredDimension(mWidth,mHeight);
}
/**
* 测量模式 表示意思
* UNSPECIFIED 父容器没有对当前View有任何限制,当前View可以任意取尺寸
* EXACTLY 当前的尺寸就是当前View应该取的尺寸
* AT_MOST 当前尺寸是当前View能取的最大尺寸
*
* @param defaultSize 默认大小
* @param measureSpec 包含测量模式和宽高信息
* @return 返回View的宽高大小
*/
private int getViewSize(int defaultSize, int measureSpec) {
int viewSize = defaultSize;
//获取测量模式
int mode = MeasureSpec.getMode(measureSpec);
//获取大小
int size = MeasureSpec.getSize(measureSpec);
switch (mode) {
case MeasureSpec.UNSPECIFIED:
//如果没有指定大小,就设置为默认大小
viewSize = defaultSize;
break;
case MeasureSpec.AT_MOST:
//如果测量模式是最大取值为size
//我们将大小取最大值,你也可以取其他值
viewSize = size;
break;
case MeasureSpec.EXACTLY:
//如果是固定的大小,那就不要去改变它
viewSize = size;
break;
default:
}
return viewSize;
}
5.第五步,绘制:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 获取半径
int r = mWidth / 2;
// 绘制前先旋转一个角度,使最顶上开始位置颜色与开始颜色匹配
canvas.rotate(360f / mLineCount, r, r);
for (int i = 0; i < mLineCount; i++) {
// 获取颜色下标
int index = (mStartIndex + i) % mLineCount;
// 设置颜色
mBgPaint.setColor(mColors[index]);
// 绘制线条 mLineBold >> 1 == mLineBold / 2 使居中显示
canvas.drawLine(r, mLineBold >> 1, r, (mLineBold >> 1) + mLineLength, mBgPaint);
// 旋转角度
canvas.rotate(360f / mLineCount, r, r);
}
}
6.第六步,动起来!!!!:
/**
* 开始动画
*
* @param duration 动画时间
*/
public void startAnimation(int duration) {
Log.d(TAG, "startAnimation: " + mStartIndex);
if (mValueAnimator == null) {
mValueAnimator = ValueAnimator.ofInt(mLineCount, 0);
mValueAnimator.setDuration(duration);
mValueAnimator.setTarget(0);
mValueAnimator.setRepeatCount(-1);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 此处会回调3次 需要去除后面的两次回调
if (mStartIndex != (int) animation.getAnimatedValue()) {
mStartIndex = (int) animation.getAnimatedValue();
Log.d(TAG, "onAnimationUpdate: " + mStartIndex);
invalidate();
}
}
});
}
mValueAnimator.start();
isAnimationStart = true;
}
完成了!!!
额,少了东西了。
/**
* 结束动画
*/
public void stopAnimation() {
Log.d(TAG, "stopAnimation: " + mStartIndex);
if (mValueAnimator != null) {
mValueAnimator.cancel();
isAnimationStart = false;
}
}
不行,还不够!
/**
* 防止内存溢出 未结束动画并退出页面时,需使用此函数,或手动释放此view
*/
public void detachView() {
if (mValueAnimator != null) {
mValueAnimator.cancel();
mValueAnimator = null;
isAnimationStart = false;
}
}
所有源码:
/**
* @author : Kingsley
* info: 菊花图
* @date : 2019/4/30 10:12
*/
public class ChrysanthemumView extends View {
private static final String TAG = "ChrysanthemumView";
/**
* 线圆角及宽度
*/
private int mLineBold;
/**
* 线条开始颜色 默认白色
*/
private int mStartColor = Color.parseColor("#FFFFFF");
/**
* 线条结束颜色 默认灰色
*/
private int mEndColor = Color.parseColor("#9B9B9B");
/**
* view的宽度 高度
*/
private int mWidth;
/**
* view的高度
*/
private int mHeight;
/**
* 线条长度
*/
private int mLineLength;
/**
* 线条个数 默认12条
*/
private int mLineCount = 12;
/**
* 背景画笔
*/
private Paint mBgPaint;
/**
* 渐变颜色
*/
private int[] mColors;
/**
* 动画是否已开启
*/
private boolean isAnimationStart;
/**
* 开始index
*/
private int mStartIndex;
/**
* 动画
*/
private ValueAnimator mValueAnimator;
public ChrysanthemumView(Context context) {
this(context, null);
}
public ChrysanthemumView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ChrysanthemumView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
loadAttrs(context, attrs);
initPaint();
initColor();
}
/**
* 初始化颜色
*/
private void initColor() {
// 渐变色计算类
ArgbEvaluator argbEvaluator = new ArgbEvaluator();
// 初始化对应空间
mColors = new int[mLineCount];
// 获取对应的线颜色 此处由于是白色起头 黑色结尾所以需要反过来计算 即线的数量到0的数量递减 对应的ValueAnimator 是从0到线的数量-1递增
for (int i = mLineCount; i > 0; i--) {
float alpha = (float) i / mLineCount;
mColors[mLineCount - i] = (int) argbEvaluator.evaluate(alpha, mStartColor, mEndColor);
}
}
/**
* 加载自定义的属性
*/
private void loadAttrs(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ChrysanthemumView);
mStartColor = array.getColor(R.styleable.ChrysanthemumView_startColor, mStartColor);
mEndColor = array.getColor(R.styleable.ChrysanthemumView_endColor, mEndColor);
mLineCount = array.getInt(R.styleable.ChrysanthemumView_lineCount, mLineCount);
//TypedArray回收
array.recycle();
}
/**
* 初始化画笔
*/
private void initPaint() {
mBgPaint = new Paint();
//使得画笔更加圆滑
mBgPaint.setAntiAlias(true);
mBgPaint.setStrokeJoin(Paint.Join.ROUND);
mBgPaint.setStrokeCap(Paint.Cap.ROUND);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 获取view的宽度 默认40dp
mWidth = getViewSize(dp2px(getContext(), 40), widthMeasureSpec);
// 获取view的高度 默认40dp
mHeight = getViewSize(dp2px(getContext(), 40), heightMeasureSpec);
// 使宽高保持一致
mHeight = mWidth = Math.min(mWidth, mHeight);
// 获取线的长度
mLineLength = mWidth / 6;
// 获取线圆角及宽度
mLineBold = mWidth / mLineCount;
// 设置线的圆角及宽度
mBgPaint.setStrokeWidth(mLineBold);
setMeasuredDimension(mWidth,mHeight);
}
/**
* 测量模式 表示意思
* UNSPECIFIED 父容器没有对当前View有任何限制,当前View可以任意取尺寸
* EXACTLY 当前的尺寸就是当前View应该取的尺寸
* AT_MOST 当前尺寸是当前View能取的最大尺寸
*
* @param defaultSize 默认大小
* @param measureSpec 包含测量模式和宽高信息
* @return 返回View的宽高大小
*/
private int getViewSize(int defaultSize, int measureSpec) {
int viewSize = defaultSize;
//获取测量模式
int mode = MeasureSpec.getMode(measureSpec);
//获取大小
int size = MeasureSpec.getSize(measureSpec);
switch (mode) {
case MeasureSpec.UNSPECIFIED:
//如果没有指定大小,就设置为默认大小
viewSize = defaultSize;
break;
case MeasureSpec.AT_MOST:
//如果测量模式是最大取值为size
//我们将大小取最大值,你也可以取其他值
viewSize = size;
break;
case MeasureSpec.EXACTLY:
//如果是固定的大小,那就不要去改变它
viewSize = size;
break;
default:
}
return viewSize;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 获取半径
int r = mWidth / 2;
// 绘制前先旋转一个角度,使最顶上开始位置颜色与开始颜色匹配
canvas.rotate(360f / mLineCount, r, r);
for (int i = 0; i < mLineCount; i++) {
// 获取颜色下标
int index = (mStartIndex + i) % mLineCount;
// 设置颜色
mBgPaint.setColor(mColors[index]);
// 绘制线条 mLineBold >> 1 == mLineBold / 2 使居中显示
canvas.drawLine(r, mLineBold >> 1, r, (mLineBold >> 1) + mLineLength, mBgPaint);
// 旋转角度
canvas.rotate(360f / mLineCount, r, r);
}
}
/**
* 开始动画
*
* @param duration 动画时间
*/
public void startAnimation(int duration) {
Log.d(TAG, "startAnimation: " + mStartIndex);
if (mValueAnimator == null) {
mValueAnimator = ValueAnimator.ofInt(mLineCount, 0);
mValueAnimator.setDuration(duration);
mValueAnimator.setTarget(0);
mValueAnimator.setRepeatCount(-1);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 此处会回调3次 需要去除后面的两次回调
if (mStartIndex != (int) animation.getAnimatedValue()) {
mStartIndex = (int) animation.getAnimatedValue();
Log.d(TAG, "onAnimationUpdate: " + mStartIndex);
invalidate();
}
}
});
}
mValueAnimator.start();
isAnimationStart = true;
}
/**
* 开始动画 时间为1800毫秒一次
*/
public void startAnimation() {
startAnimation(1800);
}
/**
* 结束动画
*/
public void stopAnimation() {
Log.d(TAG, "stopAnimation: " + mStartIndex);
if (mValueAnimator != null) {
mValueAnimator.cancel();
isAnimationStart = false;
}
}
/**
* 是否在动画中
*
* @return 是为 true 否则 false
*/
public boolean isAnimationStart() {
return isAnimationStart;
}
/**
* dp转px
*
* @param context context
* @param dpVal dpVal
* @return px
*/
public static int dp2px(Context context, float dpVal) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, context.getResources().getDisplayMetrics());
}
/**
* 防止内存溢出 未结束动画并退出页面时,需使用此函数,或手动释放此view
*/
public void detachView() {
if (mValueAnimator != null) {
mValueAnimator.cancel();
mValueAnimator = null;
isAnimationStart = false;
}
}
}
安卓设置菊花动画_Android 加载菊花图 自定义相关推荐
- 安卓设置菊花动画_Android仿ios加载loading菊花图效果
项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loading,一般用的比较多的是仿照ios 的菊花加载loading 图,当然一些条件下还会涉及到加载成功/ 失败情况 ...
- 安卓设置菊花动画_这种 “小菊花”式加载动画,会让人觉得等了很久
IT之家讯 9月3日消息,最近一项研究表明,当人们在看电视或者刷网页时,遇到网络不佳而出现的加载动画,有可能影响到人们等待过程的感受.例如我们常见的环形菊花状的加载动动画,当我们在等待视频或页面加载的 ...
- android仿ios菊花,简易仿ios菊花加载loading图
项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loading,一般用的比较多的是仿照ios 的菊花加载loading 图,当然一些条件下还会涉及到加载成功/ 失败情况 ...
- Android RecyclerView设置分割线 / 下拉加载 / 选项菜单进行增加删除动画
首先看一下我的整个程序: 主类: package com.example.day02rk;import android.os.Bundle; import android.support.v4.con ...
- greensock下载_使用GreenSock的DrawSVG创建动画SVG加载器
greensock下载 今天的教程是一个使您印象深刻的东西,您编写了很少的代码即可实现如此令人愉快的动画. GreenSock的一个非常聪明的小组的DrawSVG允许您逐步显示(或隐藏)SVG的笔触. ...
- layui loading动画_loading加载和layer.js
layer.js中的loading加载 l本篇主要介绍layerjs中的loading加载在实际项目中的应用 1.使用的技术 前端:HTML5+CSS3+JS+layer.js 后端:.net 2.遇 ...
- echarts加载数据时自定义加载动画
echarts加载数据时自定义加载动画 1.实现 1.实现 有时用echarts加载数据时,尤其是首次加载,可能一时加载不出数据,可以给它加一个加载动画 let departChart = this. ...
- 安卓 加载服务器图片不显示图片,android 从服务器加载.9图
问题描述: APP启动时, 广告页的图片是从服务器上获取, 这个图片一般需要全屏显示, 这个怎么适配呢? 解决方法1: 如果使用android:scaleType="fitXY"属 ...
- Android 高清加载巨图方案 拒绝压缩图片
Android 高清加载巨图方案 拒绝压缩图片 转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/49300989: 本文出自: ...
最新文章
- Andriod使用webview控件往APP里内嵌网页
- MongoDB启动报错
- ASP.NET企业开发框架IsLine FrameWork系列之十--ExceptionProcessProvider异常框架(下)
- Android 数据库综述(二) 程序计算器与信号量来处理多线程并发问题
- Spring Boot系列教程五:使用properties配置文件实现多环境配置
- android学习笔记---57_采用方向传感器实现指南针,android设备传感器介绍,以及使用方法
- JS实现表单多文件上传样式美化支持选中文件后删除相关项
- “Windows Sandbox”——PC主系统从未如此安全
- mysql 注入攻击与防御_防御SQL注入和XSS攻击
- html图片怎么弄透明背景,如何使用CSS实现背景图像透明
- 三分钟破解奇迹热门外挂
- 中国工商注册企业全信息数据
- 笔记本电脑dns电脑服务器未响应如何处理,提示dns电脑服务器未响应如何处理?...
- (二)UPF之电压域、低功耗模式编码(Primary Supply Set、Power State)
- 第七章 将文件内容复制到另外文件
- 中职计算机技能,中职计算机专业技能竞赛规则
- 【MySQL】MySQL数据库结构与操作
- 新款任天堂Switch《游戏机专业投屏充电底座带网口方案》LDR6023B+AX88179
- 小红书关键词搜索商品列表API接口(商品详情页API接口)
- owncloud config.php,【ownCloud】添加信任域