本来想找一个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 加载菊花图 自定义相关推荐

  1. 安卓设置菊花动画_Android仿ios加载loading菊花图效果

    项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loading,一般用的比较多的是仿照ios 的菊花加载loading 图,当然一些条件下还会涉及到加载成功/ 失败情况 ...

  2. 安卓设置菊花动画_这种 “小菊花”式加载动画,会让人觉得等了很久

    IT之家讯 9月3日消息,最近一项研究表明,当人们在看电视或者刷网页时,遇到网络不佳而出现的加载动画,有可能影响到人们等待过程的感受.例如我们常见的环形菊花状的加载动动画,当我们在等待视频或页面加载的 ...

  3. android仿ios菊花,简易仿ios菊花加载loading图

    项目中经常会用到加载数据的loading显示图,除了设计根据app自身设计的动画loading,一般用的比较多的是仿照ios 的菊花加载loading 图,当然一些条件下还会涉及到加载成功/ 失败情况 ...

  4. Android RecyclerView设置分割线 / 下拉加载 / 选项菜单进行增加删除动画

    首先看一下我的整个程序: 主类: package com.example.day02rk;import android.os.Bundle; import android.support.v4.con ...

  5. greensock下载_使用GreenSock的DrawSVG创建动画SVG加载器

    greensock下载 今天的教程是一个使您印象深刻的东西,您编写了很少的代码即可实现如此令人愉快的动画. GreenSock的一个非常聪明的小组的DrawSVG允许您逐步显示(或隐藏)SVG的笔触. ...

  6. layui loading动画_loading加载和layer.js

    layer.js中的loading加载 l本篇主要介绍layerjs中的loading加载在实际项目中的应用 1.使用的技术 前端:HTML5+CSS3+JS+layer.js 后端:.net 2.遇 ...

  7. echarts加载数据时自定义加载动画

    echarts加载数据时自定义加载动画 1.实现 1.实现 有时用echarts加载数据时,尤其是首次加载,可能一时加载不出数据,可以给它加一个加载动画 let departChart = this. ...

  8. 安卓 加载服务器图片不显示图片,android 从服务器加载.9图

    问题描述: APP启动时, 广告页的图片是从服务器上获取, 这个图片一般需要全屏显示, 这个怎么适配呢? 解决方法1: 如果使用android:scaleType="fitXY"属 ...

  9. Android 高清加载巨图方案 拒绝压缩图片

    Android 高清加载巨图方案 拒绝压缩图片 转载请标明出处:  http://blog.csdn.net/lmj623565791/article/details/49300989:  本文出自: ...

最新文章

  1. Andriod使用webview控件往APP里内嵌网页
  2. MongoDB启动报错
  3. ASP.NET企业开发框架IsLine FrameWork系列之十--ExceptionProcessProvider异常框架(下)
  4. Android 数据库综述(二) 程序计算器与信号量来处理多线程并发问题
  5. Spring Boot系列教程五:使用properties配置文件实现多环境配置
  6. android学习笔记---57_采用方向传感器实现指南针,android设备传感器介绍,以及使用方法
  7. JS实现表单多文件上传样式美化支持选中文件后删除相关项
  8. “Windows Sandbox”——PC主系统从未如此安全
  9. mysql 注入攻击与防御_防御SQL注入和XSS攻击
  10. html图片怎么弄透明背景,如何使用CSS实现背景图像透明
  11. 三分钟破解奇迹热门外挂
  12. 中国工商注册企业全信息数据
  13. 笔记本电脑dns电脑服务器未响应如何处理,提示dns电脑服务器未响应如何处理?...
  14. (二)UPF之电压域、低功耗模式编码(Primary Supply Set、Power State)
  15. 第七章 将文件内容复制到另外文件
  16. 中职计算机技能,中职计算机专业技能竞赛规则
  17. 【MySQL】MySQL数据库结构与操作
  18. 新款任天堂Switch《游戏机专业投屏充电底座带网口方案》LDR6023B+AX88179
  19. 小红书关键词搜索商品列表API接口(商品详情页API接口)
  20. owncloud config.php,【ownCloud】添加信任域

热门文章

  1. cad尺寸标注快捷键_Auto CAD 尺寸标注快捷键
  2. 创意设计:40幅唯美的卡通插画作品欣赏
  3. 3D建模学习资料大全,网站最齐!其他地方你怎么也找不到的
  4. MySql递归RECURSIVE的详解
  5. “芯”机遇!百能云芯诚邀您共聚慕尼黑上海电子展!| 百能云芯
  6. 抖音滑块验证解决方案
  7. 台式计算机管理,拴住宝宝的胃从焖饭开始
  8. 接入丰桥,下单到打印面单到配送开发流程
  9. Qt实现图片水平垂直翻转,旋转
  10. NAT-T技术原理简单分析及应用实验解析