前言

京东到家APP的引导页做的可圈可点,插画+动效,简明生动地说明了APP最吸引用户的几个亮点(商品多,价格低,配送快...)。本文主要分析拆解这些动画效果,并完成一个高仿Demo,完整的Demo代码可在文章结尾获取。

先看一下京东到家APP引导页动画效果,如下:

功能分析

分析结果基于对APP进行反编译和我个人的理解(不然还能怎么办呢?我太难了,哈哈哈)

  • 浅浅的背景图+四个引导页面,每一个页面动画播放完成后自动滑动到下一个页面。实现:用ViewPager+4个View实现,每一个页面对应一个View,一个View包含一个LottieAnimationView,在页面中监听Lottie动画的播放,播放完成后自动切换到下一个页面,ViewPager使用的是Alibaba开源的UltraViewPager
背景图片 引导图1 引导图2 引导图3 引导图4
  • 页面滑动切换时有一个旋转动画效果,可自定义ViewPager中的PageTransformer接口实现此效果,Title有透明度渐变效果,用属性动画ObjectAnimator实现

  • 当滑动到最后一个页面时,出现一个带有“立即体验”文本的按钮,出现时从底部向上弹出并且透明度从0到1变化,当从最后一个页面向前滑动时,按钮从底部消失并且透明度从1到0变化,即位移+透明度变化动画,使用属性动画ObjectAnimator可以实现此效果

布局分析

使用uiautomatorviewer查看layout布局文件,如下所示:

  • 根布局为ConstraintLayout,子节点为ViewPager,xml文件如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. android:background="@android:color/white">
  7. <com.airbnb.lottie.LottieAnimationView
  8. android:id="@+id/lottie_bg"
  9. android:layout_width="wrap_content"
  10. android:layout_height="wrap_content"
  11. app:layout_constraintBottom_toBottomOf="parent"
  12. app:layout_constraintLeft_toLeftOf="parent"
  13. app:layout_constraintRight_toRightOf="parent" />
  14. <com.tmall.ultraviewpager.UltraViewPager
  15. android:id="@+id/viewpager"
  16. android:layout_width="0.0dip"
  17. android:layout_height="0.0dip"
  18. app:layout_constraintBottom_toBottomOf="parent"
  19. app:layout_constraintLeft_toLeftOf="parent"
  20. app:layout_constraintRight_toRightOf="parent"
  21. app:layout_constraintTop_toTopOf="parent" />
  22. <com.airbnb.lottie.LottieAnimationView
  23. android:id="@+id/lottie_title"
  24. android:layout_width="wrap_content"
  25. android:layout_height="wrap_content"
  26. app:layout_constraintBottom_toBottomOf="parent"
  27. app:layout_constraintLeft_toLeftOf="parent"
  28. app:layout_constraintRight_toRightOf="parent"
  29. app:layout_constraintTop_toTopOf="parent" />
  30. <ImageView
  31. android:id="@+id/ivJump"
  32. android:layout_width="46.0dip"
  33. android:layout_height="0.0dip"
  34. android:layout_marginRight="20.0dip"
  35. android:background="@drawable/pdj_guide_jump"
  36. app:layout_constraintBottom_toBottomOf="parent"
  37. app:layout_constraintDimensionRatio="63:28.5"
  38. app:layout_constraintRight_toRightOf="parent"
  39. app:layout_constraintTop_toTopOf="parent"
  40. app:layout_constraintVertical_bias="0.07" />
  41. <ImageView
  42. android:id="@+id/iv_start"
  43. android:layout_width="104.29999dip"
  44. android:layout_height="30.669983dip"
  45. android:background="@drawable/pdj_guide_button"
  46. android:focusable="true"
  47. android:visibility="gone"
  48. app:layout_constraintBottom_toBottomOf="parent"
  49. app:layout_constraintLeft_toLeftOf="parent"
  50. app:layout_constraintRight_toRightOf="parent"
  51. />
  52. </androidx.constraintlayout.widget.ConstraintLayout>
  • 引导页总共有四个页面,每一个页面中LottieAnimationView对应一个Lottie文件,xml文件如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app="http://schemas.android.com/apk/res-auto"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent">
  6. <com.airbnb.lottie.LottieAnimationView
  7. android:id="@+id/pdj_guide_lottie_item"
  8. android:layout_width="wrap_content"
  9. android:layout_height="wrap_content"
  10. app:layout_constraintBottom_toBottomOf="parent"
  11. app:layout_constraintLeft_toLeftOf="parent"
  12. app:layout_constraintRight_toRightOf="parent" />
  13. </androidx.constraintlayout.widget.ConstraintLayout>

资源分析

  • 资源文件放在了src/main/assets目录下,配置文件为LottieConfig.json,如下:
  1. {
  2. "radius": 2020,
  3. "totalPageNum": 4,
  4. "lottieBgName": "guide_lottie_bg",
  5. "pageAnimTime": 350,
  6. "buttonAnimTime": 350,
  7. "lottieTitle": [
  8. {
  9. "lottieName": "title_lottie_0"
  10. },
  11. {
  12. "lottieName": "title_lottie_1"
  13. },
  14. {
  15. "lottieName": "title_lottie_2"
  16. },
  17. {
  18. "lottieName": "title_lottie_3"
  19. }
  20. ],
  21. "lottieMain": [
  22. {
  23. "lottieName": "main_lottie_0"
  24. },
  25. {
  26. "lottieName": "main_lottie_1"
  27. },
  28. {
  29. "lottieName": "main_lottie_2"
  30. },
  31. {
  32. "lottieName": "main_lottie_3",
  33. "repeatInterval": {
  34. "start": 0.288,
  35. "end": 1
  36. }
  37. }
  38. ]
  39. }
  • radius --定义PageTransformer时会用到,用于确定最大旋转角度
  • totalPageNum --引导页的数量
  • lottieBgName --背景图片对应的Lottie文件名
  • pageAnimTime --ViewPager各个页面之间切换的滑动时间
  • buttonAnimTime --最后一个页面"立即体验"按钮做位移和透明度动画的时长
  • lottieTitle --每个引导页标题对应的Lottie文件名
  • lottieMain -- 每个引导页内容对应的Lottie文件名

代码实现

基于以上分析,用代码实现就比较简单了,此处贴出部分实现代码

  • 加载本地Lottie zip文件,Lottie支持加载本地和远程json,zip文件,通常我们将Lottie文件放在src/main/assets目录下
  1. fun loadAssetsLottieZipFile(context: Context, lottieImageView:LottieAnimationView, fileName:String,repeatCount:Int= LottieDrawable.INFINITE,autoPlay:Boolean=true){
  2. val lottieCompose= if(fileName.endsWith(".zip")){
  3. LottieCompositionFactory.fromAssetSync(context, fileName).value
  4. }
  5. else{
  6. LottieCompositionFactory.fromAssetSync(context, fileName.plus(".zip")).value
  7. }
  8. lottieImageView.progress=0.0f
  9. lottieImageView.repeatCount=repeatCount
  10. lottieImageView.setComposition(lottieCompose!!)
  11. if(autoPlay){
  12. lottieImageView.playAnimation()
  13. }
  14. }
  • 添加圆点指示符
  1. viewpager.initIndicator()
  2. viewpager.indicator.setOrientation(UltraViewPager.Orientation.HORIZONTAL)
  3. .setNormalIcon(drawableToBitmap(ContextCompat.getDrawable(applicationContext, R.drawable.guide_white_dot)!!))
  4. .setFocusIcon(drawableToBitmap(ContextCompat.getDrawable(applicationContext, R.drawable.guide_dark_dot)!!))
  5. .setIndicatorPadding(ScreenHelper.dp2px(applicationContext, 5.0F))
  6. .setMargin(0, 0, 0, ScreenHelper.dp2px(applicationContext, 20.0F))
  7. viewpager.indicator.setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)
  8. viewpager.indicator.build()

其中drawableToBitmap对应的代码为:

  1. /**
  2. * Drawable转换成Bitmap
  3. */
  4. private fun drawableToBitmap(drawable: Drawable): Bitmap {
  5. val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
  6. val canvas = Canvas(bitmap)
  7. drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
  8. drawable.draw(canvas)
  9. return bitmap
  10. }
  • 自定义PageTransformer实现页面切换旋转效果

    • position < -1 时,旋转到最大角度,旋转中心为右下角;
    • -1 < position < 0 时,position 越靠近 0 ,旋转角度越小,旋转中心向下边缘中心靠拢;
    • 0 <= position <= 1 时,position 越靠近 0 ,旋转角度越小,旋转中心向下边缘中心靠拢;
    • position > 1 时,旋转到最大角度,旋转中心为左下角。

代码如下所示:

  1. import android.content.Context
  2. import android.view.View
  3. import androidx.viewpager.widget.ViewPager
  4. import com.kongpf.commonhelper.ScreenHelper
  5. import kotlin.math.atan2
  6. class GuideTransformer(context: Context, private val mRadius: Int) : ViewPager.PageTransformer {
  7. private var mMaxRotate = 0f
  8. init {
  9. if (mRadius > 0) {
  10. mMaxRotate = (2.0 * Math.toDegrees(atan2((ScreenHelper.getScreenWidth(context) / 2).toDouble(), mRadius.toDouble()))).toFloat()
  11. }
  12. }
  13. override fun transformPage(page: View, position: Float) {
  14. if (mRadius == 0) {
  15. return
  16. }
  17. when {
  18. position < -1.0f -> {
  19. page.rotation = -1.0f * mMaxRotate
  20. page.pivotX = page.width.toFloat()
  21. page.pivotY = page.height.toFloat()
  22. }
  23. position <= 1.0f -> {
  24. if (position < 0.0f) {
  25. page.pivotX = page.width * (0.5f + 0.5f * -position)
  26. page.pivotY = page.height.toFloat()
  27. page.rotation = position * mMaxRotate
  28. } else {
  29. page.pivotX = 0.5f * page.width * (1.0f - position)
  30. page.pivotY = page.height.toFloat()
  31. page.rotation = position * mMaxRotate
  32. }
  33. }
  34. else -> {
  35. page.rotation = mMaxRotate
  36. page.pivotX = 0f
  37. page.pivotY = page.height.toFloat()
  38. }
  39. }
  40. }
  41. }

完整代码地址

https://github.com/kongpf8848/Animation

高仿京东到家APP引导页炫酷动画效果的更多相关文章

  1. Android高级控件(四)——VideoView 实现引导页播放视频欢迎效果,超级简单却十分的炫酷

    Android高级控件(四)--VideoView 实现引导页播放视频欢迎效果,超级简单却十分的炫酷 是不是感觉QQ空间什么的每次新版本更新那炫炫的引导页就特别的激动,哈哈,其实他实现起来真的很简单很 ...

  2. android仿网易云音乐引导页、仿书旗小说Flutter版、ViewPager切换、爆炸菜单、风扇叶片效果等源码

    Android精选源码 复现网易云音乐引导页效果 高仿书旗小说 Flutter版,支持iOS.Android Android Srt和Ass字幕解析器 Material Design ViewPage ...

  3. 浅谈android中只使用一个TextView实现高仿京东,淘宝各种倒计时

    今天给大家带来的是只使用一个TextView实现一个高仿京东.淘宝.唯品会等各种电商APP的活动倒计时.近期公司一直加班也没来得及时间去整理,今天难得歇息想把这个分享给大家.只求共同学习,以及自己兴许 ...

  4. iOS - GitHub干货分享(APP引导页的高度集成 - DHGuidePageHUD - ②)

    距上一篇博客"APP引导页的高度集成 - DHGuidePageHUD - ①"的发布有一段时间了, 后来又在SDK中补充了一些新的内容进去但是一直没来得及跟大家分享, 今天来跟大 ...

  5. iOS - GitHub干货分享(APP引导页的高度集成 - DHGuidePageHUD - ①)

    好长时间没更新博客, 是时候来一波干货分享了;APP引导页话不多说每一个APP都会用到,分量不重但是不可缺少,不论是APP的首次安装还是版本的更新,首先展现给用户眼前的也就只有它了吧,当然这里讲的不是 ...

  6. GitHub干货分享(APP引导页的高度集成 - DHGuidePageHUD)

    每一个APP都会用到APP引导页,分量不重但是不可缺少,不论是APP的首次安装还是版本的更新,首先展现给用户眼前的也就只有它了,当然这里讲的不是APP引导页的美化而是APP引导页的高度集成,一行代码搞 ...

  7. [Android实例] app引导页(背景图片切换加各个页面动画效果)(申明:来源于网络)

    [Android实例] app引导页(背景图片切换加各个页面动画效果)(申明:来源于网络) 地址: http://www.eoeandroid.com/thread-918356-1-1.html h ...

  8. 使用CoordinatorLayout打造各种炫酷的效果

    使用CoordinatorLayout打造各种炫酷的效果 自定义Behavior -- 仿知乎,FloatActionButton隐藏与展示 NestedScrolling 机制深入解析 一步步带你读 ...

  9. 15个来自 CodePen 的酷炫 CSS 动画效果【下篇】

    CodePen 是一个在线的前端代码编辑和展示网站,能够编写代码并即时预览效果.你在上面可以在线分享自己的 Web 作品,也可以欣赏到世界各地的优秀开发者在网页中实现的各种令人惊奇的效果. 今天这篇文 ...

随机推荐

  1. git commit guidelines

    git-commit-guidelines AngularJS Development Setup Running Tests Coding Rules Commit Message Guidelin ...

  2. CURL & Weather

    CURL & Weather https://wttr.in/ $ curl wttr.in https://github.com/chubin/wttr.in refs http://www ...

  3. vue & less bug

    vue & less bug bezierEasingMixin(); ^ Inline JavaScript is not enabled. Is it set in your option ...

  4. Headless Chrome Node API

    puppeteer Headless Chrome Node API https://github.com/GoogleChrome/puppeteer https://pptr.dev/ PWA h ...

  5. 高级数据结构之 BloomFilter

    高级数据结构之 BloomFilter 布隆过滤器 https://en.wikipedia.org/wiki/Bloom_filter A Bloom filter is a space-effic ...

  6. js & bitwise-operators

    js & bitwise-operators 不用加减乘除运算符, 求整数的7倍 "use strict"; /** * * @author xgqfrms * @lice ...

  7. macOS open url from terminal

    macOS open url from terminal open URL && start terminal bash open url in chrome open chrome ...

  8. js 检测浏览器开发者控制台是否被打开

    var element = new Image(); Object.defineProperty(element, "id", { get: function () { debug ...

  9. 12月15日BGV币行情分析

    今日,DeFi市场格外精彩.各主流概念币种走势出现了涨跌各半的两极态势.笔者认为,由于并没有总体可以利好DeFi市场的基本面因素,所以各DeFi概念币种的涨跌态势,还是与各自的基本面和技术面走势相关. ...

  10. 学习String源码的部分方法

    先看构造器: private final char value[]; //char类型的数组 以下均会用到 private int hash; //缓存字符串的哈希值 //以下均会用到 public ...