本文目的

平时Android项目中看到的加载中的动效基本上就是转圈的形式,有点审美疲劳。前一篇文章通过ViewGroup做了一个简单的加载中的动效,上一篇文章的主要知识点基于ViewGroup实现自定义组合视图。本文仍然是基于ViewGroup实现自定义组合视图,将重点展示如何充分利用Android提供的属性动效实现一个有趣的加载中动效。文中较多的使用了位移动效,透明度动效。

效果介绍

实现了一个太阳升级/落下,月亮升起落下/星星眨眼的动效,组合起来之后可以作为加载中动效

自定义View代码

本次基于自定义视图绘制的形状包括太阳/月亮/星星三类自然景物。首先太阳绘制的基本思路是画完圆形之后,绘制横线并且进行画布的旋转,即可实现太阳光线。关键代码如下:

/*** 绘制太阳* @param canvas*/
private void drawSun(Canvas canvas){//将画布平移至中心点canvas.translate(width/2+getPaddingLeft(),height/2+getPaddingTop());//画圆canvas.drawCircle(0,0,sunRadius,sunPaint);//画光线drawSunshine(canvas);
}
……
private void drawSunshine(Canvas canvas){/*** 逐个绘制光线,绘制10条线*/if(index_draw_sunshine < 10 && index_draw_sunshine >= 0){for(int i=0 ;i<=index_draw_sunshine;i++){canvas.drawLine(sunRadius+10, 0, sunRadius+30,0,sunshinePaint);canvas.rotate(-360/MAX_COUNT);}index_draw_sunshine++;postInvalidateDelayed(20);}else{for(int i=0 ;i<index_draw_sunshine;i++){canvas.drawLine(sunRadius+10, 0, sunRadius+30,0,sunshinePaint);canvas.rotate(-360/MAX_COUNT);}}
}

绘制月亮的基本方法是通过绘制两端圆弧连接实现,过程中需要平移画布的位置实现,过程代码如下:

//绘制月亮
private void drawMoon(Canvas canvas){canvas.translate(width/2+getPaddingLeft(),height/2+getPaddingTop());//绘制大圆弧RectF oval = new RectF(-sunRadius , -sunRadius , sunRadius ,sunRadius);canvas.drawArc(oval , -120 , 240 , false , sunPaint);//绘制小圆弧RectF oval1 = new RectF(-2*sunRadius , -sunRadius , 0 ,sunRadius);canvas.drawArc(oval1 , -60 , 120 ,false ,sunPaint);
}

绘制星星的基本思路是先计算得到星星绘制所需要的10个点的横纵坐标,然后基于Path连接这些坐标点即可,代码如下

/*** 自定义视图实现星星*/
public class StarView extends View {/*** 绘制星星所需的内径和外径*/private int outRadius , innerRadius;private Paint mPaint;private int[][] points = new int[10][2];private static final int NUM_POINTS = 10;/*** 视图的宽度和高度*/private int width,height;/*** 星星在父类视图的左上坐标,半径*/private int x , y;private int size;public StarView(Context context) {super(context);initPaint();}public StarView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);initPaint();}public StarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initPaint();}private void initPaint(){if(null == mPaint){mPaint = new Paint();mPaint.setAntiAlias(true);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(3);mPaint.setColor(getResources().getColor(R.color.purple_200));}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();outRadius = Math.min(width,height)/4;innerRadius = outRadius/2;getPositions();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawStar(canvas);}/*** 使用Path绘制星星* @param canvas*/private void drawStar(Canvas canvas){Path path = new Path();for(int i = 0 ; i < NUM_POINTS ; i++){if(i == 0){path.moveTo(points[i][0] , points[i][1]);}else{path.lineTo(points[i][0] , points[i][1]);}}path.close();canvas.drawPath(path,mPaint);}/*** 计算星星的顶点坐标* @return*/private int[][] getPositions(){int center_x = width/2 + getPaddingLeft();int center_y = height/2 + getPaddingTop();for(int i = 0 ; i < NUM_POINTS ; i++){int r = (i%2 == 0 ? outRadius : innerRadius);double angle = -Math.PI / 10 + i*2*Math.PI / 10 ;//横坐标points[i][0] = (int)(r*Math.cos(angle)) + center_x;//纵坐标points[i][1] = (int)(r*Math.sin(angle)) + center_y;}return points;}public int getPositionX() {return x;}public void setPositionX(int x) {this.x = x;}public int getPositionY() {return y;}public void setPositionY(int y) {this.y = y;}public int getSize() {return size;}public void setSize(int size) {this.size = size;}
}

实现动效的代码

本次加载中动效分为4个步骤实现:1.太阳落下{@link #sunSet()};2.月亮升起并且星星显示{@link #moonRise()};月亮和星星收起{@link #moonStarVanish()};4.太阳升起{@link #sunRise()}。过程代码如下:

/*** 本次加载中动效分为4个步骤实现:1.太阳落下{@link #sunSet()};2.月亮升起并且星星显示{@link #moonRise()};* 3.月亮和星星收起{@link #moonStarVanish()};4.太阳升起{@link #sunRise()}*/
public class LoadingView extends FrameLayout {private SunMoonView sunMoonView;/*** 全部的星星将通过addView添加,此处缓存添加的星星对象,用于对星星设置动效*/private List<StarView> starViews = new ArrayList<>();/*** 整体布局宽度,高度*/private int width , height;/*** 太阳的视图半径,星星最大半径*/private int sunRadius , starMaxRadius;/*** 太阳和月亮落下,升起时的位移距离*/private int shiftDistance;/*** 上升和下降的组合动效*/private AnimatorSet upAnimatorSet,downAnimatorSet;private static final String TAG = "LoadingView";public LoadingView(Context context) {super(context);}public LoadingView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init(context, attrs);}public LoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context, attrs);}private void init(Context context , AttributeSet attrs){LayoutInflater.from(context).inflate(R.layout.layout_loading_view,this , true);sunMoonView = findViewById(R.id.sun_moon_view);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();sunRadius = Math.min(width , height) / 5;starMaxRadius = sunRadius / 2;shiftDistance = height/2 + sunRadius;}@Overrideprotected void onFinishInflate() {super.onFinishInflate();removeAllViews();post(new Runnable() {@Overridepublic void run() {layoutSunMoon();sunSet();}});}/*** 设置太阳月亮初始位置*/private void layoutSunMoon(){ViewGroup.MarginLayoutParams layoutParams  = (ViewGroup.MarginLayoutParams)sunMoonView.getLayoutParams();layoutParams.width = 2*sunRadius;layoutParams.height = layoutParams.width;layoutParams.leftMargin = width/2 - sunRadius;layoutParams.topMargin = height/2 - sunRadius;sunMoonView.setLayoutParams(layoutParams);addView(sunMoonView);}/*** 随机设置星星的位置*/private void layoutStar(){Random random = new Random();int maxNum = height*2/5/2/starMaxRadius * width/2/starMaxRadius;int setNum = maxNum /2;//可以布置的行数int rows = height*2/5/2/starMaxRadius;//可以布置的列数int lines = width/2/starMaxRadius;starViews.clear();Log.d(TAG,"starMaxRadius = "+starMaxRadius+",sunRadius = "+sunRadius+",rows = "+rows + ",setNum = "+setNum);if(setNum > 0 ){for(int i = 0 ; i < setNum ; i++){StarView starView = new StarView(getContext());int x = i%lines*2*starMaxRadius + random.nextInt(starMaxRadius);int y = i/lines*2*starMaxRadius + random.nextInt(starMaxRadius);int size = starMaxRadius/2 + random.nextInt(starMaxRadius/2);starView.setPositionX(x);starView.setPositionY(y);starView.setSize(size);ViewGroup.MarginLayoutParams layoutParams = new ViewGroup.MarginLayoutParams(size*2,size*2);layoutParams.leftMargin = x;layoutParams.topMargin = y;starView.setLayoutParams(layoutParams);addView(starView);starViews.add(starView);}}}/*** 将dp值转为像素* @param dp 输入dp类型尺寸* @return*/private int dp2px(int dp){float dpi = getContext().getResources().getDisplayMetrics().density;return (int)(dp*dpi + 0.5f);}/*** 1.日落*/private void sunSet(){ObjectAnimator bottomShiftAnimator = ObjectAnimator.ofFloat(sunMoonView,"translationY",0,shiftDistance);ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(sunMoonView , "rotation",0,360);downAnimatorSet = new AnimatorSet();downAnimatorSet.playTogether(bottomShiftAnimator , rotateAnimator);downAnimatorSet.setDuration(3000);downAnimatorSet.setInterpolator(new DecelerateInterpolator());downAnimatorSet.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {postDelayed(new Runnable() {@Overridepublic void run() {moonRise();}} , 1000);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});downAnimatorSet.start();}/*** 2.月出:月亮升起,星星出现和闪烁*/private void moonRise(){layoutStar();sunMoonView.changeShape();ObjectAnimator upShiftAnimator = ObjectAnimator.ofFloat(sunMoonView,"translationY",shiftDistance,0);ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(sunMoonView , "rotation",-20,20);rotateAnimator.setRepeatCount(2);rotateAnimator.setRepeatMode(ObjectAnimator.REVERSE);upAnimatorSet = new AnimatorSet();upAnimatorSet.playTogether(upShiftAnimator , rotateAnimator);upAnimatorSet.setDuration(3000);upAnimatorSet.setInterpolator(new AccelerateInterpolator());upAnimatorSet.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {postDelayed(new Runnable() {@Overridepublic void run() {starShink();}} , 1000);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});upAnimatorSet.start();starShow();}/*** 2.2设置星星闪烁*/private void starShink(){for(int i = 0 ; i < starViews.size() ; i++){if(i % 2 == 0){ObjectAnimator alpha = ObjectAnimator.ofFloat(starViews.get(i),"alpha",1.0f,0f);alpha.setDuration(1000);alpha.setRepeatCount(2);alpha.setRepeatMode(ValueAnimator.REVERSE);alpha.start();if((i == (starViews.size()-1)) | (i == (starViews.size()-1))){alpha.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {postDelayed(new Runnable() {@Overridepublic void run() {moonStarVanish();}} , 1000);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});}}}}/*** 2.1设置星星显示*/private void starShow(){for(int i = 0 ; i < starViews.size() ; i++){ObjectAnimator bottomShiftAnimator = ObjectAnimator.ofFloat(starViews.get(i),"translationY",-3*starViews.get(i).getSize(),starViews.get(i).getPositionY());bottomShiftAnimator.setDuration(3000);bottomShiftAnimator.start();}}/*** 3.1设置星星隐藏*/private void starHide(){for(int i = 0 ; i < starViews.size() ; i++){ObjectAnimator upShiftAnimator = ObjectAnimator.ofFloat(starViews.get(i),"translationY",starViews.get(i).getPositionY(),-3*starViews.get(i).getSize());upShiftAnimator.setDuration(3000);upShiftAnimator.start();}}/*** 3.月亮落下,星星收起*/private void moonStarVanish(){ObjectAnimator bottomShiftAnimator = ObjectAnimator.ofFloat(sunMoonView,"translationY",0,shiftDistance);ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(sunMoonView , "rotation",-20,20);rotateAnimator.setRepeatCount(1);rotateAnimator.setRepeatMode(ObjectAnimator.REVERSE);downAnimatorSet = new AnimatorSet();downAnimatorSet.playTogether(bottomShiftAnimator , rotateAnimator);downAnimatorSet.setDuration(3000);downAnimatorSet.setInterpolator(new DecelerateInterpolator());downAnimatorSet.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {postDelayed(new Runnable() {@Overridepublic void run() {sunRise();}} , 1000);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});downAnimatorSet.start();starHide();}/*** 4.太阳升起* 结束后重新执行onFinishInflated()方法*/private void sunRise(){sunMoonView.changeShape();ObjectAnimator upShiftAnimator = ObjectAnimator.ofFloat(sunMoonView,"translationY",shiftDistance,0);upShiftAnimator.setDuration(3000);upShiftAnimator.setInterpolator(new AccelerateInterpolator());upShiftAnimator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {postDelayed(new Runnable() {@Overridepublic void run() {//重写执行整个动效过程onFinishInflate();}} , 1000);}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}});upShiftAnimator.start();}
}

动效使用

在需要引用的布局文件中引用相应的类,如下示例

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"tools:context=".MainActivity"><!--引用的加载中动效--><com.guo.loadinganim.LoadingViewandroid:layout_width="300dp"android:layout_height="300dp"android:background="@color/gray"/></LinearLayout>

源码位置

https://gitee.com/com_mailanglidegezhe/loading-view2.git

利用Android属性动画实现有趣的加载中动效相关推荐

  1. 利用Android属性动画实现Banner的原理与实践

    事实上在Android刚推出属性动画的时候.就想利用它来设计一个Banner控件,一直没什么时间尝试. 在当时看我们应用中的Banner,使用计时器来控制自己主动播放,设置一个非常大的数,利用余数原理 ...

  2. android 属性动画伸缩,Android的属性动画(二)加载框圆点旋转收缩放大缩小效果的实现...

    案例效果图如下, 案例实现步骤 1.首先用drawCircle()画好6个圆点 private void drawCircles(Canvas canvas) { //每个小圆之间的间隔角度 = 2π ...

  3. android 魅族动画效果,魅族 Flyme 9 知意动效:动画自然,提供 Flyme 1-8 经典系统主题...

    IT之家 3 月 2 日消息今天下午,魅族带来了 Flyme 9 发布会.魅族称,系统作为用户与手机交互的媒介,是用户体验的灵魂.回看过去,每一代的 Flyme 都堪称经典.Flyme 用简洁优雅的设 ...

  4. android菊花动画,仿ios系统加载动画(菊花旋转)

    无需添加图片,通过自定义view方式绘制菊花图,代码极简 效果图: image 一.自定义loadingview: public class LoadingView extends View { pr ...

  5. css3加载中动画效果,CSS3实现加载中的动画效果

    Loading 的菊花图形组合的不太好,基本上实现这个功能了 动画解析 这个动画用到的 CSS3 特性: transform 主要使用 transform 属性的 rotate,将线条组合成 Load ...

  6. css实现loading,CSS3 19种LOADING(加载)动效

    CSS 语言: CSSSCSS 确定 body { background: #323943; text-align: center; width: 960px; max-width: 100%; ma ...

  7. JQuery datatables 给表格添加加载中效果

    JQuery datatables 中如果有很多数据的话,加载就会很慢,这时候就需要使用加载中的效果,而datatables是提供了加载中的属性的:processing,加载中的style样式也是 可 ...

  8. android 仿脉搏柱动画,Android属性动画学习笔记

    这段时间正好要做些动画,于是把属性动画重新学习了一遍,做些总结 1. 前言 Android动画分为Frame Animation,Tweened Animation ,Property Animati ...

  9. Android 属性动画 详解

    Android 属性动画 详解 Android动画类型: View Animation(即所谓的Tween Animation补间动画):View Animation相当简单,不过只能支持简单的缩放. ...

最新文章

  1. 获取长度length_lab、labE、la、laE、ll、llE 钢筋锚固搭接长度6项参数的相互关系...
  2. 交叉熵损失函数公式_交叉熵损失函数对其参数求导
  3. ioS html的转义
  4. jpg图片使用pil的resize后_如何使用PIL调整图像大小并保持其纵横比?
  5. LeetCode 241. 为运算表达式设计优先级(动态规划)
  6. chmod是linux命令吗,Linux chmod命令怎么用
  7. C#中获取本机IP地址,子网掩码,网关地址
  8. 已锁定 java.lang.Object@25ff46f5
  9. extjs 学习自我理解
  10. linux内存源码分析 - 内存回收(匿名页反向映射)
  11. C#中Panel控件的使用
  12. WPS Office 2021 for Mac(办公软件)
  13. Javamail发信和收信机制(smtp、pop3、imap)
  14. 没有Console线,又不知道自己通过哪个IP连接到路由器,怎么办?
  15. 回顾过去,展望未来!
  16. Vector的自动排序Sort
  17. 2013年节假日安排公布 春节假期2月9日至15日
  18. 一开机checkingmedia_电脑开机出现checking media怎么办?
  19. 云栖大会:友盟+互联网企业数据中台,助力企业数智化转型
  20. IE6及IE8默认SSL设置

热门文章

  1. 电脑计算机窗口不见了怎么调出来,电脑桌面上的任务栏不见了怎么办
  2. Android百日程序:绘画程序-画手指路径
  3. 如何去除word中的空行
  4. 计算正五边形的面积和周长
  5. U3d之物理引擎(下)
  6. linkerd 本地环境安装
  7. msvcr120.dll WIN7 64位/32位丢失怎么办
  8. 固件-驱动-软件 区别
  9. 性能测试之nmon对linux服务器的监控 侵删
  10. 固态硬盘、机械硬盘、手机的“内存”有三种