Android-Notes|BottomNavigationView-爱上-Lottie,kotlin声明数组
- selected: 选中才显示标题;
- labeled: 标题一直显示;
- unlabeled: 只显示 icon,不显示标题。
2. Lottie
对于这个东东,不知道说啥。忽略吧。
想起来都是累,韩总让我自己折腾 Lottie json 文件。哭唧唧
安利一个在线编辑 Lottie json 文件的地址:
- lottiefiles.com/editor
实战部分
Step 1:导入提供的 Lottie Json 文件
新建 assets 目录,这里我做了 Android 10 深色兼容,所以需要提供深色(暗黑)模式下 Lottie 文件。
老渣男,给我的素材用不了,害我借用别人家的 App Lottie 素材。
Step 2:定义 Lottie 枚举类并封装基础数据:
enum class LottieAnimation(val value: String) {
// 截取「喜马拉雅」App Lottie 素材
HOME(“lottie/bottom_tab_home_page_btn.json”),
SUBSCRIBE(“lottie/bottom_tab_my_listen_btn.json”),
DISCOVERY(“lottie/bottom_tab_finding_btn.json”),
ACCOUNT(“lottie/bottom_tab_mine_btn.json”),
HOME_NIGHT(“lottie-night/bottom_tab_home_page_btn.json”),
SUBSCRIBE_NIGHT(“lottie-night/bottom_tab_my_listen_btn.json”),
DISCOVERY_NIGHT(“lottie-night/bottom_tab_finding_btn.json”),
ACCOUNT_NIGHT(“lottie-night/bottom_tab_mine_btn.json”)
}
复制代码
封装个 BasicData,存放 App 内置的一些基本数据,这里主要针对 Lottie 文件:
val mNavigationAnimationList = arrayListOf(
LottieAnimation.HOME,
LottieAnimation.SUBSCRIBE,
LottieAnimation.DISCOVERY,
LottieAnimation.ACCOUNT
)
val mNavigationAnimationNightList = arrayListOf(
LottieAnimation.HOME_NIGHT,
LottieAnimation.SUBSCRIBE_NIGHT,
LottieAnimation.DISCOVERY_NIGHT,
LottieAnimation.ACCOUNT_NIGHT
)
复制代码
Step 3:导入对应依赖,新增 Lottie Utils
api ‘com.google.android.material:material:1.2.0’
api ‘com.airbnb.android:lottie:3.4.1’
复制代码
工具类方法:
/**
- 获取 Lottie Drawable
*/
fun getLottieDrawable(
animation: LottieAnimation,
bottomNavigationView: BottomNavigationView
): LottieDrawable {
return LottieDrawable().apply {
val res
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
ult = LottieCompositionFactory.fromAssetSync(
bottomNavigationView.context.applicationContext, animation.value
)
callback = bottomNavigationView
composition = result.value
}
}
/**
- 获取不同模式下 Lottie json 文件
*/
fun getLottieAnimationList(context: Context): ArrayList {
return if (isDarkTheme(context)) {
mNavigationAnimationNightList
} else {
mNavigationAnimationList
}
}
复制代码
判断是否深色模式我单独提取了一个工具类,Lottie-android 中也有对深色模式的兼容方法:
/**
- 验证当前是否为深色模式
*/
fun isDarkTheme(context: Context): Boolean {
val flag = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return flag == Configuration.UI_MODE_NIGHT_YES
}
复制代码
Step 4:设置布局
先添加个 tab 字体选中和非选中的字体颜色 selecor:
<?xml version="1.0" encoding="utf-8"?> 复制代码
整一波布局文件:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:background="@color/colorBackground"
tools:context=".module.home.activity.HomeActivity">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_bottom_bar"
android:layout_width="@dimen/dp_0"
android:layout_height=“wrap_content”
android:background="@color/colorBackground"
app:itemIconSize="@dimen/dp_30"
app:itemTextColor="@color/selector_menu_state_navigation"
app:labelVisibilityMode=“labeled”
app:layout_constraintBottom_toBottomOf=“parent”
app:layout_constraintEnd_toEndOf=“parent”
app:layout_constraintStart_toStartOf=“parent” />
</androidx.constraintlayout.widget.ConstraintLayout>
复制代码
Step 5:初始化 BottomNavigationView 以及 Menu
private fun initBottomNavigationView() {
nav_bottom_bar.menu.apply {
for (i in 0 until mNavigationTitleList.size) {
add(Menu.NONE, i, Menu.NONE, mNavigationTitleList[i])
}
setLottieDrawable(getLottieAnimationList(mSelfActivity))
}
initEvent()
}
private fun initEvent() {
nav_bottom_bar.setOnNavigationItemSelectedListener(this)
nav_bottom_bar.setOnNavigationItemReselectedListener(this)
// 默认选中第一个
nav_bottom_bar.selectedItemId = 0
// 处理长按 MenuItem 提示 TooltipText
nav_bottom_bar.menu.forEach {
val menuItemView = mSelfActivity.findViewById(it.itemId) as BottomNavigationItemView
menuItemView.setOnLongClickListener {
true
}
}
}
private fun Menu.setLottieDrawable(lottieAnimationList: ArrayList) {
for (i in 0 until mNavigationTitleList.size) {
findItem(i)?.icon =
getLottieDrawable(lottieAnimationList[i], nav_bottom_bar)
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
handleNavigationItem(item)
return true
}
override fun onNavigationItemReselected(item: MenuItem) {
handleNavigationItem(item)
}
private fun handleNavigationItem(item: MenuItem) {
handlePlayLottieAnimation(item)
mPreClickPosition = item.itemId
}
private fun handlePlayLottieAnimation(item: MenuItem) {
val currentIcon = item.icon as? LottieDrawable
currentIcon?.apply {
playAnimation()
}
// 处理 tab 切换,icon 对应调整
if (item.itemId != mPreClickPosition) {
nav_bottom_bar.menu.findItem(mPreClickPosition).icon =
getLottieDrawable(
getLottieAnimationList(mSelfActivity)[mPreClickPosition],
nav_bottom_bar
)
}
}
复制代码
问题汇总
鸡老大说:
- 遇到问题是好事儿,多总结,多积累,掌握一个循循渐进的过程。
1、BottomNavigationView 切换对应的 Lottie 不改变,怎么玩?
这个问题是我从一开始就陷入了固有思维循环中。
下面是我陷入误区的思路:
- 我想着因为是通过 playAnimation 开始执行动画从而过渡到最后的颜色,那么对应的 endAnimation 应该是直接能回到初始状态。那么我直接缓存上一此点击 MenuItem 然后修改状态不就好了嘛。
- tint 着色器修改?
整整折腾了好久,折腾到韩总说,不行咱就放弃吧。
想想鸡老大,怎能轻易放弃?
昨天突然想到,为什么我不重新给设置一次 Drawable 呢?反正初始的 Drawable 就是灰色,当然也是未选中的状态,随后赶紧实战测试了一波,附上关键代码:
override fun onNavigationItemReselected(item: MenuItem) {
handleNavigationItem(item)
}
private fun handleNavigationItem(item: MenuItem) {
handlePlayLottieAnimation(item)
mPreClickPosition = item.itemId
}
private fun handlePlayLottieAnimation(item: MenuItem) {
val currentIcon = item.icon as? LottieDrawable
currentIcon?.apply {
playAnimation()
}
// 这里判断如果当前点击的和上一次点击索引不同,则将上一次点击索引位置的 MenuItem Icon 替换
if (item.itemId != mPreClickPosition) {
// 获取到上一个 MenuItem 并修改对应的 icon drawable
nav_bottom_bar.menu.findItem(mPreClickPosition).icon =
getLottieDrawable(
getLottieAnimationList(mSelfActivity)[mPreClickPosition],
nav_bottom_bar
)
}
}
复制代码
具体代码参考文章实战部分。
小教训(心得):
真的是有时候不得不换种思维方式,首要的便是实现,随后才是优化。基本雏形都没有,何谈优化?
身为猿猿,面对实际开发中遇到的问题,一定要采取多方案,首要保证内容、结果的输出,其次才是合理的循循渐进的优化。
2、BottomNavigationView Item 长按提示怎么搞掉?
先来看个效果图:
比较尴尬的是,用了很久了,头一次某天闲来无事长按发现的这个东西,当时还好奇,没事弹个 Toast 干啥玩意?怀揣着满满的自信,去找源码关于我理想中认为的这个 Toast,没找到。
来,正好一起翻翻源码,看看能否从源码的角度去思考并解决这个问题。
进行一波简要分析:
从上图以及实际编码过程中,我们可以得知,所谓的底部 item,其实只是一个数量不能超过 5 个的 Menu,而实际触发这个提示肯定是 ItemView,那么一起来看下针对 itemView 它做了什么操作?
在初始化 ItemView 时,最后有这么一段设置,一起来看下:
@Override
public void initialize(@NonNull MenuItemImpl itemData, int menuType) {
// …
CharSequence tooltipText = !TextUtils.isEmpty(itemData.getTooltipText())
? itemData.getTooltipText()
: itemData.getTitle();
TooltipCompat.setTooltipText(this, tooltipText);
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
}
复制代码
tooltipText,工具提示文本?有点茫然,似乎有点意思。这里有个验证,如果 tooltipText 为空则使用 title 值,反正使用自身值,最后将最终的判断结果进行 setTooltipText,我们一起来看看这个 set 里面干了什么?
/**
- Helper class used to emulate the behavior of {@link View#setTooltipText(CharSequence)} prior
- to API level 26.
/
public class TooltipCompat {
/*
- Sets the tooltip text for the view.
Prior to API 26 this method sets or clears (when tooltip is null) the view's
- OnLongClickListener and OnHoverListener. A toast-like subpanel will be created on long click
- or mouse hover.
- @param view the view to set the tooltip text on
- @param tooltipText the tooltip text
*/
public static void setTooltipText(@NonNull View view, @Nullable CharSequence tooltipText) {
if (Build.VERSION.SDK_INT >= 26) {
view.setTooltipText(tooltipText);
} else {
TooltipCompatHandler.setTooltipText(view, tooltipText);
}
}
private TooltipCompat() {}
}
复制代码
注释写的倒是蛮清楚的,在 Api 26 添加的一个工具提示文本,主要用于长按或者鼠标悬停的一个提示,类似 Toast。
继续往下看,set 到底干了啥?
/**
- Sets the tooltip text which will be displayed in a small popup next to the view.
- // …
- // 重点是下面参数描述,如果不需要设置 null
- @param tooltipText the tooltip text, or null if no tooltip is required
- @see #getTooltipText()
- @attr ref android.R.styleable#View_tooltipText
*/
public void setTooltipText(@Nullable CharSequence tooltipText) {
if (TextUtils.isEmpty(tooltipText)) {
setFlags(0, TOOLTIP);
// hide 隐藏?鸡老大万岁
hideTooltip();
mTooltipInfo = null;
} else {
// …
}
}
复制代码
可以很明确的看到,如果不想显示这个所谓的 tooltipText 只需要将其设置为空即可。看到这里大概有个思路了,但是还是好奇弹出的这个东西是啥?继续往下找。
@UnsupportedAppUsage
void hideTooltip() {
// …
if (mTooltipInfo.mTooltipPopup == null) {
return;
}
mTooltipInfo.mTooltipPopup.hide();
mTooltipInfo.mTooltipPopup = null;
// …
}
复制代码
ummm,原来是个 PopupWindow。
好吧,回归正题,取消 BottomNavigationView 长按时的 tooltipText 提示。
循环遍历 Menu 并将 tooltipText 设置为 null。
nav_bottom_bar.menu.forEach {
TooltipCompat.setTooltipText(mSelfActivity.findViewById(it.itemId),null)
}
复制代码
来看下效果图:
ummm。不对呀。首次进来两个 Tab 长按符合预期,后续呢?
ummm,或者,我直接断了丫的念想?直接拦截长按事件一波?
nav_bottom_bar.menu.forEach {
val menuItemView = mSelfActivity.findViewById(it.itemId) as BottomNavigationItemView
menuItemView.setOnLongClickListener {
true
}
}
运行一波看看?
ummm,好扎心。
3、选中时,想来个动画,怎么搞?(2020/09/03)
昨天大半夜,韩总发消息,部分内容如下:
ummm,我这个扎心的啊。都要准备入眠来着。
好吧,简单分析波。
先附上关于 BottomNavigationView 的 item 部分代码,其实是 Menu Item:
只需要获取到当前点击位置的 MenuItemView,随后通过此 View 获取到 id 为 icon 的 ImageView 顺便给其设置动画即可。
关键代码如下:
m_navigation_text_size"/>
</com.google.android.material.internal.BaselineLayout>
复制代码
只需要获取到当前点击位置的 MenuItemView,随后通过此 View 获取到 id 为 icon 的 ImageView 顺便给其设置动画即可。
关键代码如下:
Android-Notes|BottomNavigationView-爱上-Lottie,kotlin声明数组相关推荐
- Android Notes|BottomNavigationView 爱上 Lottie
鸡老大说:大丈夫岂能久居人下. 前言 好容易解决个问题,感觉记录一波. 当日事当日毕,践行鸡老大,点滴积累,万一某天优秀了呢? 以前大部分项目底部导航栏关于图片部分的实现,要么两套图 selector ...
- android js变量定义数组长度,js 声明数组和向数组中添加对象变量的简单实例
数组有四种定义的方式 使用构造函数: var a = new Array(); var b = new Array(10); var c = new Array("first", ...
- android js变量定义数组,js 声明数组和向数组中添加对象变量的简单实例
数组有四种定义的方式 使用构造函数: var a = new Array(); var b = new Array(10); var c = new Array("first", ...
- Kotlin的数组array和集合list的简单使用以及注意事项
学习到Kotlin的数组和集合,这里做个记录. 数组Array Kotlin中数组也是个容器,提供了不同的类型有:ByteArray, CharArray, ShortArray, IntArray, ...
- java指定长度数组长度_Java声明数组时不能指定其长度
声明数组平均折旧法包括 长度The First Folio was published after the death of W. Shakespeare. 声明数组W. Shakespeare mi ...
- java一维数组的特点,数据结构:java数组特点以及声明数组类
JAVA数组的特点:动态数组,具有长度属性length,引用数据类型 动态数组是指,在声明数组变量之后,使用new运算符动态申请指定容量(存储单元个数)的数据存储空间:当数组不再被使用时,java将自 ...
- kotlin字符串数组_Kotlin程序读取,遍历,反向和排序字符串数组
kotlin字符串数组 Given a string array, we have to read, traverse, reverse and sort its elements. 给定一个字符串数 ...
- java 声明数组_Java中的数组简介
对于Java来说,如果需要用一种数据结构来保存若干个同类型的元素,那么可以选择使用数组. 所谓数组就是用来保存若干个同类型的数据元素,在Java中也可以认为数据元素就是对象.也就是说,只要给定了一个数 ...
- Android Studio 配置 Copyright 插入版权声明
Android Studio 配置 Copyright 插入版权声明 GitHub地址:Android Studio 配置 Copyright 插入版权声明 一个规范的项目无论是开源的还是公司内部,版 ...
最新文章
- typedef的四个用途和两大陷阱
- [c]如何通过结构体元素找到结构体?
- 模块就是一个普通的python程序文件_Python-模块和包
- Java回调机制解析
- 运行wordcount.java_运行hadoop wordcount程序
- 1G、3G都失败了,5G也会失败吗?
- 算法导论8-5习题解答(平均排序)
- 维纶触摸屏程序实际项目,威纶通界面UI
- 支付宝第三方登录集成文档
- 我的TLS1.3之旅
- codeforces 596E Wilbur and Strings(DFS)
- 我的大学六年 郭天祥
- ubuntu: du指令
- Android 插件化开发——宿主APP加载APK插件
- 高数 | 单调函数必有反函数 有反函数的不一定是单调函数
- 长庆油田嬗变记:“骑着毛驴”踏上“信息高速路”
- HTML5页面调用百度地图API,获取当前位置并导航到目的地
- Ubuntu 12.04 安装 IQQ
- 抽奖活动设计 php,如何设计高并发下的抽奖?
- 彩虹QQ显IP v2.5 正式版支持QQ2008正式版
热门文章
- c++游戏源代码(自编)
- 出现socket:(10107)系统调用失败
- axis2异常:File quot;/axis2-web/listSingleService.jspquot; not found 的处理
- Material Theme 主题插件 - Sublime 插件
- MATLAB算法实战应用案例精讲-【自动驾驶】FMCW 雷达(补充篇)
- 机器学习模型性能评估(二):P-R曲线和ROC曲线
- 智能指针初始化(pcl库)
- spring boot中jackson时间格式和东八区的设置
- datagridview 获取选中行的索引
- dataGridView选中行的语法