Android车载HMI—Lottie讲解

1.前言 多年以前汽车还是以机械仪表主体的年代,各大汽车主机厂商并不十分关注操作系统UI的交互功能,但是随着车载SOC算力的不断提高以及主机厂商对汽车座舱竞争的白热化。座舱的HMI在设计上在强调功能...

1.前言

多年以前汽车还是以机械仪表主体的年代,各大汽车主机厂商并不十分关注操作系统UI的交互功能,但是随着车载SOC算力的不断提高以及主机厂商对汽车座舱竞争的白热化。座舱的HMI在设计上在强调功能性的同时也开始关注UI的艺术性,HMI的设计师们期望艺术与功能应该协同工作,让用户沉浸在“第三空间”的体验中。

有了需求程序员就需要关注如何实施和落地,然而Android应用本身虽然有着完整的动画框架支持,但是开发复杂、调试耗时,大型的gif或逐帧动画对于CPU&内存占用都不太理想,所以许多Android的手机应用基本上不怎么有动画。而且车载HMI上越来越多的开始引入各种光影、粒子效果,如果基于Android的原生控件来实现这些粒子效果,难度非常大,这就需要今天的主角Lottie来实现了。

Android车载HMI—Lottie讲解插图

2.Lottie概述

Lottie是一种基于JSON的动画文件格式,它使设计师能够在任何平台上发布动画,就像发布静态资产一样简单。它们是在任何设备上工作的小文件,可以在不进行像素化的情况下放大或缩小。

GitHub:https://github.com/airbnb/lottie-android 官方文档:http://airbnb.io/lottie/

Lottie在车载HMI中的优势

适量图形,不会出现失真

占用空间比序列帧动画小

可以修改属性,动态生成可交互的动画(使用视频动画难以实现交互功能)

节省HMI的开发、调试时间

可以更轻松的实现粒子、光影等特效

Lottie的使用方法

在build.gradle中添加依赖
dependencies { def lottieVersion = “5.2.0” implementation com.airbnb.android:lottie:$lottieVersion }
使用LottieAnimationView 首先将lottie动画的json文件放在assets文件夹下
Android车载HMI—Lottie讲解插图1
img

然后就可以在布局文件中使用LottieAnimationView了

<com.airbnb.lottie.LottieAnimationView android:id=”@+id/dynamic_text” android:layout_width=”wrap_content” android:layout_height=”wrap_content” app:lottie_fileName=”HamburgerArrow.json” app:lottie_autoPlay=”true” app:lottie_loop=”true”/>

然后运行APP就可以看到动画效果

Android车载HMI—Lottie讲解插图2

3.Lottie的常用属性&API

LottieAnimationView继承自AppCompatImageView,所以ImageView支持的属性,LottieAnimationView都是支持的,这部分就不再介绍了。

lottie_fileName 设定lottie动画所对应的json文件地址。json文件默认需要放置在assets下,设定时不需要再强调assets
app:lottie_fileName=“HamburgerArrow.json”

如果设定 app:lottie_fileName=”other/HamburgerArrow.json”,那么lottie就会读取assets/other/HamburgerArrow.json。 void setAnimationFromJson(String jsonString, @Nullable String cacheKey)

lottie_rawRes 设定lottie动画的json文件地址。json文件除了可以放置assets文件夹下,还可以放在raw文件夹下。使用时需要注意,利用lottie_rawRes引入资源时,json文件名前需要加上@raw,并且文件名不带.json后缀
app:lottie_rawRes=“@raw/name”
Android车载HMI—Lottie讲解插图3

imglottie_autoPlay 设定是否自动播放,取值为true | false

lottie_loop 设定是否循环播放,取值为true | false

lottie_url 当需要加载在线资源时,就可以使用lottie_url void setAnimationFromUrl(String url) void setAnimationFromUrl(String url, @Nullable String cacheKey)

lottie_fallbackRes 设置一个drawable,如果lotticomposition由于任何原因未能加载,则将呈现该drawable。 如果这是网络动画,可以使用它向用户显示错误,也可以添加一个失败的监听器重试下载。 void setFallbackResource(@DrawableRes int fallbackResource)

lottie_repeatMode 设定循环播放的顺序。取值为restart | reverse 。restart表示正常循环播放,reverse表示倒序播放 void setRepeatMode(@LottieDrawable.RepeatMode int mode) int getRepeatMode()

lottie_repeatCount 设定循环播放次数,取值为整数类型。 void setRepeatCount(int count) int getRepeatCount()

lottie_imageAssetsFolder 设定图片文件在assets文件夹下的访问路径。有的时候使用AE导出lottie的json时也会导出一些图片,这时候就需要该属性设定图片的地址。 void setImageAssetsFolder(String imageAssetsFolder) String getImageAssetsFolder()

void setFrame(int frame) 将进度设置为指定的帧。将进度设置为指定的帧。如果尚未设置合成,则进度将在设置时设置为帧。 通过int getFrame()可以获取当前渲染的帧。

void setMaxFrame(int endFrame) 设置播放或循环时动画将结束的最大帧。 该值将被钳制到合成边界。例如,设置整数最大值将产生与合成相同的结果。 通过float getMaxFrame()可以获取当前设定的最大帧

void setMinFrame(int startFrame) 设置播放或循环时动画开始的最小帧。 设定最大、最小帧可以只播放lottie动画中的一部分,例如下面的两张图,第一张是完整的从0播放到183帧,第二张则是从60播放到100帧。

Android车载HMI—Lottie讲解插图4

lottie_progress 设定动画初次显示时的进度,类型为float。取值范围0.0 ~ 1.0 void setProgress(@FloatRange(from = 0f, to = 1f) float progress) float getProgress()

lottie_speed 设定播放速度,取值类型为float。当速度<1时,动画会慢放,当速度<0时,可以实现倒序播放。 void setSpeed(float speed) float getSpeed() void reverseAnimationSpeed():反转当前动画速度。这不会播放动画。

Android车载HMI—Lottie讲解插图5

速度是一个比较重要的属性,与progress、frame等属性一起灵活运用,我们就可以轻松地在HMI上实现炫酷而复杂的仪表盘效果,这对车载HMI尤为重要。

lottie_enableMergePathsForKitKatAndAbove 设定是否开启MergePath属性,取值为true | false。默认为false void enableMergePathsForKitKatAndAbove(boolean enable) boolean isMergePathsEnabledForKitKatAndAbove()

void playAnimation() 从头开始播放动画。如果速度<0,它将从终点开始,并向起点播放。必须在主线程中调用。

void cancelAnimation() 取消动画,必须在主线程中调用。

void pauseAnimation() 暂停动画,必须在主线程中调用。

void resumeAnimation() 从当前位置继续播放动画。如果速度<0,它将从当前位置向后播放。必须在主线程中调用。

long getDuration() 获取动画的播放时长。

void setTextDelegate(TextDelegate textDelegate) 设置此选项可在运行时用自定义文本替换动画文本

lottie_cacheComposition 设定是否开启缓存,取值 true | false,默认开启。开启缓存可以提升动画的加载效率。

void setCacheComposition(boolean cacheComposition)
lottie_ignoreDisabledSystemAnimations 允许忽略系统动画设置,因此即使禁用动画,也允许运行动画。取值 true | false,默认为false。 void setIgnoreDisabledSystemAnimations(boolean ignore)lottie_clipToCompositionBounds 设置lottie是否应剪辑到原始动画合成边界。设置为true时,父视图可能需要禁用clipChildren,以便Lottie可以在LottieAnimationView边界之外进行渲染。默认为true。 void setClipToCompositionBounds(boolean clipToCompositionBounds)lottie_renderMode 设定渲染模式,取值为 automatic | hardware | software。设定渲染模式为hardware时,可以显著提升动画的渲染效率,但是有些系统函数可能并不支持硬件加速,实际使用时需要结合调试时的效果选择是否开启。 void setRenderMode(RenderMode renderMode) RenderMode getRenderMode()void addAnimatorListener(Animator.AnimatorListener listener) 添加动画的属性监听。 对应也提供了removeUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener)用来移除指定的监听。或者也可以使用removeAllAnimatorListeners()移除所有监听。
binding.animationView.addAnimatorUpdateListener(object : ValueAnimator.AnimatorUpdateListener { override fun onAnimationUpdate(animation: ValueAnimator?) { } })
void addAnimatorPauseListener(Animator.AnimatorPauseListener listener) 添加动画暂停/恢复监听。 对应也提供了removeAnimatorPauseListener(Animator.AnimatorPauseListener listener)用来移除指定的监听。
binding.animationView.addAnimatorPauseListener(object : Animator.AnimatorPauseListener{ override fun onAnimationPause(animation: Animator?) { } override fun onAnimationResume(animation: Animator?) { } })
void addAnimatorUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener) 添加动画发生更新时的监听 对应也提供了removeUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener)用来移除指定的监听。或者也可以使用removeAllUpdateListeners()移除所有监听。
binding.animationView.addAnimatorUpdateListener(object : ValueAnimator.AnimatorUpdateListener{ override fun onAnimationUpdate(animation: ValueAnimator?) { } })
void addValueCallback(KeyPath keyPath, T property, LottieValueCallback callback)

监听lottie动画json中某个片段的属性。

keypath

可以解析为多个内容,在这种情况下,回调的值将应用于所有回调。在内部会首先检查是否已使用resolveKeyPath(KeyPath)解析keypath,如果尚未解析,则将对其进行解析。

Android车载HMI—Lottie讲解插图6
img

Lottie动画的Json中属性都是英文简写,我们很难把json中key与实际的属性对应起来,所以有了第二个参数

LottieProperty

,它的内部定义了大量的属性,当我们需要修改json时,只需要传入

LottieProperty

中属性即可。

例如,需要监听json中

LeftArmWave

的持续时间,就可以这么写

animationView.addValueCallback(KeyPath(“LeftArmWave”),LottieProperty.TIME_REMAP){frameInfo-> }

4.Lottie的常见用法

Lottie的Demo中内置了很多官方自己开发的动画效果,目的是为我们展示Lottie的常见用法,作为开发者我们必须掌握,并在适当的时候运用到我们的应用中。

动态属性效果

该效果展示了lottie支持动态修改json,让动画中的一小部分属性发生改变。

修改局部动画的速度
binding.animationView.addValueCallback(KeyPath(“LeftArmWave”),LottieProperty.TIME_REMAP){frameInfo-> 2*speed.toFloat()*frameInfo.overallProgress}

KeyPath中的LeftArmWave是Json中的一个属性

Android车载HMI—Lottie讲解插图7
img

修改的效果如下。注意看右手的摆动频率X3后比X1高,以至于录制的GIF直接丢帧了。

Android车载HMI—Lottie讲解插图8
修改局部动画的颜色
val shirt = KeyPath(“Shirt”, “Group 5”, “Fill 1”) val leftArm = KeyPath(“LeftArmWave”, “LeftArm”, “Group 6”, “Fill 1”) val rightArm = KeyPath(“RightArm”, “Group 6”, “Fill 1”) binding.animationView.addValueCallback(shirt, LottieProperty.COLOR) { COLORS[colorIndex] } binding.animationView.addValueCallback(leftArm, LottieProperty.COLOR) { COLORS[colorIndex] } binding.animationView.addValueCallback(rightArm, LottieProperty.COLOR) { COLORS[colorIndex] }

修改后的效果如下

Android车载HMI—Lottie讲解插图9
修改局部动画的运动范围
val point = PointF() binding.animationView.addValueCallback( KeyPath(“Body”), LottieProperty.TRANSFORM_POSITION ) { frameInfo -> val startX = frameInfo.startValue.x var startY = frameInfo.startValue.y var endY = frameInfo.endValue.y if (startY > endY) { startY += EXTRA_JUMP[extraJumpIndex] } else if (endY > startY) { endY += EXTRA_JUMP[extraJumpIndex] } point.set(startX, lerp(startY, endY, frameInfo.interpolatedKeyframeProgress)) point }

修改后的效果如下

Android车载HMI—Lottie讲解插图10

动画文字效果

Android车载HMI—Lottie讲解插图11
img

该效果展示了动画文字效果。这个效果实现起来其实不难,从程序中捕获输入的字母,再替换成lottie的资源文件即可。

val letter = “” + Character.toUpperCase(event.unicodeChar.toChar()) val fileName = “Mobilo/$letter.json” LottieCompositionFactory.fromAsset(context, fileName) .addListener { addComposition(it) }

动态文字效果

Android车载HMI—Lottie讲解插图12

该效果展示动态替换动画中的文字。使用就可以在动画运行中修改lottie动画中的文字

val textDelegate = TextDelegate(binding.dynamicTextView) binding.nameEditText.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(s: Editable?) { textDelegate.setText(“NAME”, s.toString()) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {} }) binding.dynamicTextView.setTextDelegate(textDelegate)

注意,这里其实用了两个lottieView,分别设定了不同的文字。

<LinearLayout android:layout_width=“match_parent” android:layout_height=“wrap_content” android:layout_gravity=“center_horizontal” android:orientation=“horizontal”> <com.airbnb.lottie.LottieAnimationView android:id=“@+id/originalTextView” android:layout_width=“wrap_content” android:layout_height=“wrap_content” android:layout_marginRight=“16dp” app:lottie_rawRes=“@raw/name” app:lottie_autoPlay=“true” app:lottie_loop=“true”/> <com.airbnb.lottie.LottieAnimationView android:id=“@+id/dynamicTextView” android:layout_width=“wrap_content” android:layout_height=“wrap_content” app:lottie_rawRes=“@raw/name” app:lottie_autoPlay=“true” app:lottie_loop=“true”/> </LinearLayout>

手势交互效果

Android车载HMI—Lottie讲解插图13

该效果展示了Lottie的手势交互。其实和第一个效果实现思路相同,都是通过修改json中的属性来实现的。

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val largeValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f)) binding.animationView.addValueCallback(KeyPath(“First”), LottieProperty.TRANSFORM_POSITION, largeValueCallback) val mediumValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f)) binding.animationView.addValueCallback(KeyPath(“Fourth”), LottieProperty.TRANSFORM_POSITION, mediumValueCallback) val smallValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f)) binding.animationView.addValueCallback(KeyPath(“Seventh”), LottieProperty.TRANSFORM_POSITION, smallValueCallback) var totalDx = 0f var totalDy = 0f val viewDragHelper = ViewDragHelper.create(binding.containerView, object : ViewDragHelper.Callback() { override fun tryCaptureView(child: View, pointerId: Int) = child == binding.targetView override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int { return top } override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int { return left } override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) { totalDx += dx totalDy += dy smallValueCallback.setValue(getPoint(totalDx, totalDy, 1.2f)) mediumValueCallback.setValue(getPoint(totalDx, totalDy, 1f)) largeValueCallback.setValue(getPoint(totalDx, totalDy, 0.75f)) } }) binding.containerView.viewDragHelper = viewDragHelper }

在RecyclerView中使用

Android车载HMI—Lottie讲解插图14

该效果展示通过监听点击事件来播放不同的lottie动画。这个效果最常见,APP中的点赞效果大多都是这样的实现思路。

以上就是车载HMI—Lottie的一些讲解。更多有关车载可以参考BYD高工整理出的《车载技术开发手册》,市面上车载技术资料少之又少;希望这本电子手册能够帮助到你。

5.总结

在车载HMI开发中往往我们会在实现、调试UI上花费大量的时间,如果能够灵活的运用Lottie,就可以显著节省程序的开发时间。例如,光影、粒子等特效虽然可以也考虑用Kanzi等3D引擎实现,但是3D引擎会消耗成倍的SOC性能,实际开发过程中,简单的特效使用Lottie实现,可以极大的优化应用的性能,给用户一个更优秀的体验。

该效果展示通过监听点击事件来播放不同的lottie动画。这个效果最常见,APP中的点赞效果大多都是这样的实现思路。

转载:https://www.jianshu.com/p/2388c72d5aff

以上就是车载HMI—Lottie的一些讲解。更多有关车载可以参考BYD高工整理出的《车载技术开发手册》,市面上车载技术资料少之又少;希望这本电子手册能够帮助到你。

Android车载HMI—Lottie讲解插图15

联系我们

联系我们

0769-81627526

在线咨询: QQ交谈

邮箱: info@kingpo.hk

工作时间:周一至周五,9:00-17:30,节假日休息
关注微信
微信扫一扫关注我们

微信扫一扫关注我们

手机访问
手机扫一扫打开网站

手机扫一扫打开网站

返回顶部