AndroidTv Home界面实现原理(二)——Leanback 库的主页卡位缩放动画源码解析
先看个效果图:

上一篇中,我们留了问题,在 Tv Home 界面这种很常见聚焦卡位放大动画效果,我们这一篇就来看看 Leanback 库是怎么实现的。
如果要我们自己实现的话,思路应该不难,就是写个放大、缩小动画,然后在卡位获得焦点时应用放大动画,失去焦点时应用缩小动画,所以关键点只是在于如何进行封装。那下面就来学学 Google Leanback 库的 ItemView 缩放动画的实现思路。
源码分析
看源码时,我习惯带着目的性地去阅读,这样只要专注于理解跟目的相关的代码即可,不用每行代码地去分析,毕竟好多代码目前能力有限,还啃不透。
那么,我们这次阅读源码的目的就是要搞清楚:卡位获得焦点时放大、缩小动画是如何实现的?
阅读源码时经常会碰到一个问题,那就是该从哪入手,从哪开始看?
这就是为什么我习惯带着目的去阅读,因为我们可以从目的分析,猜测我们需要的代码应该在哪里,然后找到我们该从哪里阅读,再一步步的去分析。
比如我们这次的任务,我们该从哪里入手阅读源码呢?首先,你得先了解一下 Leanback 库的基本使用,这就是为什么我第一篇博客先简单介绍了 Leanback 库的使用。在上一篇博客里,可以看到,我们跟 Leanback 库打交道的也就是下面这几个类:
- ArrayObjectAdapter:作用类似于 List,装填着整个页面的数据,页面数据其实是分两级,以行为单位和以每一行中的 Item 为单位,所以整个页面有一个 ArrayObjectAdapter(mRowsAdapter) 对象,它由许多行数据 ArrayObjectAdapter(rowAdapter) 对象组成,每行数据 rowAdapter 由许多 Item 组成。初始化一个 ArrayObjectAdapter 对象时,必须提供一个 Presenter 对象与它关联。
- ListRowPresenter:Leanback 库中的 Presenter 作用都有些类似于 RecyclerView.Adapter,用于创建 ItemView 以及将数据绑定到 ItemView 上。
- ListRow:可以理解成一个 Mode,也就是把每一行抽象封装成一个 ListRow
- BrowerFragment:用来展示可左右上下滑动的视频列表界面,Leanback 已高度封装,我们只需提供一个页面的 ArrayObjectAdapter(mRowsAdapter) 对象,通过
setAdapter()将数据设置进去,Leanback 会自动根据 ArrayObjectAdapter 里的数据以及和它关联的 Presenter 将界面显示出来。
既然我们跟 Leanback 打交道只有这么几点,那么切入点应该就在这些,毕竟我们对 Leanback 并不熟,那么只能从我们接触到的地方来着手。
那么,再来想想,既然是要实现卡位获得焦点和失去焦点时放大和缩小动画,那么肯定是需要监听 ItemView 的焦点变化,对吧?那我们通常是怎么做的,无外乎就是在 RecyclerView.Adapter 里的 onCreateViewHolder() 或 onBindViewHolder() 里监听 ItemView 的焦点变化吧。
既然方向有了,那么就是要寻找 Leanback Home 界面对应的 RecyclerView.Adapter 是由哪个类实现的吧。我们也知道了在 Leanback 中 Presenter 的作用就是类似于 RecyclerView.Adapter,那么我们就先到 Presenter 里看一下。

ListRowPresenter 继承自 RowPresenter 继承自 Presenter,那么我们通过 Android Studio 跳到 Presenter 里看看。

Presenter 是个抽象类,有三个抽象方法, onCreateViewHolder()、onBindViewHolder()、onUnbindViewHolder(),跟 RecyclerView.Adapter 很像吧。根据我们之前的分析, ItemView 焦点的监听通常是在 onCreateViewHolder() 或 onBindViewHolder() 里实现的,那么我们就去它的实现类 ListRowPresenter 里看一看。
ListRowPresenter 里找不到这三个方法的实现,那么就是由它的父类 RowPresenter 实现了,我们继续通过 AS 跳到 RowPresenter 里看看。

onCreateViewHolder() 里的代码我们不用去理解,当然你有时间有能力也可以,但现在主要是想搞懂它的卡位缩放动画实现,所以我们只要看有没有跟焦点监听相关的代码即可。很显然,这里面并没有找到,里面调用了几个方法,有些方法一看就知道作用是创建某个对象的,你们也可以点进去看看,这里我们着重看一下 initializeRowViewHolder() 这个方法。

好像也没找到跟焦点监听的相关代码,但是左边有个标志,说明子类 ListRowPresenter 有复写这个方法,那么代码运行时实际上是调用的之类的方法,那么我们就点进去看看。

代码很多,截图也没截完,但是我们发现了一个关键,找到了一个看名字就觉得跟焦点有关的方法 FocusHighlightHelper.setupBrowseItemFocusHighlight(),那么到底是不是呢?我们继续点击去看一下。

这个方法的介绍大意就是说设置每行的 ItemView 即卡位获得焦点时的行为,这不就是指卡位的缩放动画嘛,看来我们找到了。看代码,是调用了 ItemBridgeAdapter 的 setFocusHighlight() 方法,继续跟进看一下。


方法就只是设置了 mFocusHighlight 变量的值,而 ItemBridgeAdapter 是继承 RecyclerView.Adapter 的,看来卡位的焦点监听就是在这里实现了。看一下 onCreateViewHolder() 方法就知道是不是了。

presenterView 其实就是在 Presenter 里创建出来的 ItemView,所以这里其实就是对卡位设置焦点的变化监听,viewHolder.mFocusChangeListener 应该是 View.OnFocusChangeListener 的对象,我们看一下。

mFocusChangeListener 初始化通过实例化一个类对象赋值,那么这个类应该就是实现 View.OnFocusChangeListener 接口的,我们继续看一下。

这下清楚了吧,焦点发生变化时,会去调用 mFcousHightlight 的 onItemFocused() 方法。而 mFcousHightlight 是之前 FocusHighlightHelper.setupBrowseItemFocusHighlight() 里调用了 ItemBridgeAdapter 的 setFocusHighlight() 方法传进来的,我们再看看它传进来的是什么。

看一下 BrowseItemFocusHighlight 这个类做了什么。

所以,在 ItemBridgeAdapter 里注册了焦点变化监听,当焦点变化时,通知 mFcousHightlight 执行 onItemFocused() 方法,而 mFcousHightlight 是 BrowseItemFocusHighlight 类的实例,所以实际上是来执行上图里的逻辑。看代码也很容易明白,设置 ItemView 的选中状态,并且去运行一个焦点动画,那么卡位的缩放动画应该就是在这里实现了。继续看一下是不是。

该方法其实就是创建一个动画对象,如果该对象有缓存的话,那么就从缓存中取出,没有的话,就 new 一个,这种缓存的思想很值得学习。

该类就是实现了缩放的动画效果了,通过实现 TimeAnimator.TimeListener 接口来实现的属性动画,当然缩放动画也可以用其他方式实现,无非就是对 View 进行放大、缩小而已,这里就不具体去分析了,感兴趣的可以自己来这里看看 Google 是如何实现缩放动画的,后期有时间的话我可以再来分析一下这个类。
好了,到这里基本就分析完了,Leanback 库关于卡位的缩放动画的实现,从我们要从哪里着手开始阅读源码到找到焦点监听实现的相关代码到动画实现的代码整个过程基本就是这样。以后大家在想看源码的某个功能是如何实现时,可以参考这种思路来进行分析,一步步的去跟进,只找我们目标相关的代码,这样可以不至于被整个源码的复杂性混乱掉。
最后,我想再总结一下上面的过程。
总结
- 卡位缩放动画的实现在类 FocusHighlightHelper 的内部类 FocusAnimator 里实现。
- 缩放动画跟 ItemView 的绑定过程:
RowPresenter#onCreateViewHoler()
-> RowPresenter#initializeRowViewHolder()
-> ListRowPresenter#initializeRowViewHolder()
-> FocusHighlightHelper#setupBrowseItemFocusHighlight()
-> ItemBridgeAdapter#setFocusHighlight() - 简单点说就是,当每一行的 View 要创建时,会注册一个焦点监听器,该行里的 ItemView 焦点发生变化时会从 ItemViwe 的 Tag 里取出缩放动画对象,如果没有则 new 一个,然后应用缩放动画。
AndroidTv Home界面实现原理(二)——Leanback 库的主页卡位缩放动画源码解析的更多相关文章
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- Android 热修复Nuwa的原理及Gradle插件源码解析
现在,热修复的具体实现方案开源的也有很多,原理也大同小异,本篇文章以Nuwa为例,深入剖析. Nuwa的github地址 https://github.com/jasonross/Nuwa 以及用于 ...
- 顺序线性表 ---- ArrayList 源码解析及实现原理分析
原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7738888.html ------------------------------------ ...
- dubbo源码解析五 --- 集群容错架构设计与原理分析
欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...
- 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试
机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...
- Generator函数执行器-co函数库源码解析
一.co函数是什么 co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行.短小精悍只有短短200余行,就可以免去手动编写G ...
- 【Java实战】源码解析Java SPI(Service Provider Interface )机制原理
一.背景知识 在阅读开源框架源码时,发现许多框架都支持SPI(Service Provider Interface ),前面有篇文章JDBC对Driver的加载时应用了SPI,参考[Hibernate ...
- Laya Timer原理 & 源码解析
Laya Timer原理 & 源码解析 @author ixenos 2019-03-18 16:26:38 一.原理 1.将所有Handler注册到池中 1.普通Handler在handle ...
- Spring事务源码解析(二)获取增强
在上一篇文章@EnableTransactionManagement注解解析中,我们搭建了源码阅读的环境,以及解析了开启Spring事务功能的注解@EnableTransactionManagemen ...
随机推荐
- Java设计模式学习笔记,一:单例模式
开始学习Java的设计模式,因为做了很多年C语言,所以语言基础的学习很快,但是面向过程向面向对象的编程思想的转变还是需要耗费很多的代码量的.所有希望通过设计模式的学习,能更深入的学习. 把学习过程中的 ...
- 《深入理解Java虚拟机》虚拟机类加载机制
上节学习回顾 上一节,我们深入到类文件去了解其结构细节,也大概对类文件的编写规则略知一二了,解析来我们就得学习这个类文件是如何被加载到Java虚拟机的,看看有什么引人入胜的奥秘. 本节学习重点 大部分 ...
- 使用ide编程时候 不知为何突然光标变宽,如何恢复成原有的细竖光标
各位朋友们, 你们在编程时候有没有这样的情况: 码着码着,突然不知什么原因,光标变成这样了: 这种宽的光标,不知道怎么调都调不回去,而且网上也没有类似的问题描述 就对我们编程极其不便(因为这种光标是操 ...
- C++基本内置类型
C++基本内置类型 基本内置类型包括算术类型和空类型. 算术类型 算术类型包括整型和浮点型. 类型 含义 最小尺寸 bool 布尔型 - char 字符型 8 bit wchar_t 宽字符型 16 ...
- Wamp之mysql密码故事
注:有时候修改mysql密码会出现如下状况:密码改了,但新密码就是进不进去. 原因大概是语法错误.例如: >update user set password='hooray' where use ...
- C#之异步
C#之异步 异步是相对于同步而言.跟多线程不能同一而论.异步简单而言好比一个人两双手可以同时做两件以上不同的事情.多线程好比多个人做不同或相同的事情. 异步跟多线程有什么关系? 异步可以分为CPU异步 ...
- 1059. C语言竞赛
C 语言竞赛是浙江大学计算机学院主持的一个欢乐的竞赛.既然竞赛主旨是为了好玩,颁奖规则也就制定得很滑稽: 冠军将赢得一份"神秘大奖"(比如很巨大的一本学生研究论文集--). 排名为 ...
- Dubbo实战快速入门 (转)
Dubbo是什么? Dubbo[]是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案. 其核心部分包含: 远程通讯: 提供对多种基于长连接的NIO框架抽象封 ...
- Abp(.NetCore)开发与发布过程3-部署Ubuntu站点
以下是笔者在 Ubuntu 16.0-64bit 环境下 发布 ABP(.NetCore)的全过程.特此记录,希望对大家有所帮助. 准备的工具 1.PuTTY(ssh,如果不想每次都用阿里云的远程登录 ...
- swift3.0 屏幕截图并且保存到本地相册
所要截取的对象 var bg_view: UIView! 截取并且保存的代码如下 UIGraphicsBeginImageContextWithOptions(bg_view.frame.size, ...