惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
N
News and Events Feed by Topic
S
Schneier on Security
G
GRAHAM CLULEY
Scott Helme
Scott Helme
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
C
CERT Recently Published Vulnerability Notes
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
Cisco Talos Blog
Cisco Talos Blog
P
Privacy & Cybersecurity Law Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 司徒正美
Blog — PlanetScale
Blog — PlanetScale
Project Zero
Project Zero
MyScale Blog
MyScale Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Apple Machine Learning Research
Apple Machine Learning Research
小众软件
小众软件
The Last Watchdog
The Last Watchdog
Vercel News
Vercel News
The Cloudflare Blog
C
Check Point Blog
Help Net Security
Help Net Security
Microsoft Security Blog
Microsoft Security Blog
AI
AI
Simon Willison's Weblog
Simon Willison's Weblog
云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
NISL@THU
NISL@THU
S
Security @ Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
S
SegmentFault 最新的问题
MongoDB | Blog
MongoDB | Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
AWS News Blog
AWS News Blog
Cloudbric
Cloudbric
N
News and Events Feed by Topic
PCI Perspectives
PCI Perspectives
S
Securelist
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Vulnerabilities – Threatpost
S
Secure Thoughts

LengYue's Blog

在 Android 中实现 iOS 液态玻璃效果(二:折射与渗透效果) | LengYue's Blog 在 Android 中实现 iOS 液态玻璃效果 | LengYue's Blog Dart 单线程模型的本质 | LengYue's Blog 使用 Rust 开发 Android 底层库,并简化 Java 与 Rust 相互操作 | LengYue's Blog 将 Rust 编译为可在 Android 上使用的二进制文件 | LengYue's Blog 使用 GoMobile 创建 Android、iOS 跨平台 WebSocket Library | LengYue's Blog Flutter error not found dart html | LengYue's Blog AndroidStudio新版本Logcat | LengYue's Blog Typecho to Hugo | LengYue's Blog GetX Router 设置返回值 | LengYue's Blog Flutter 基础 | Dart 语法 mixin | LengYue's Blog 不使用第三方软件实现Obsidian多平台实时同步 | LengYue's Blog Android 使用字节码 hook 修复第三发 bug | LengYue's Blog 在 Jetpack Compose 中使用输入框(TextField )遇到的一些问题 | LengYue's Blog Android-使用@AutoService实现spi | LengYue's Blog RecyclerView使用优化的ListAdapter | LengYue's Blog ⾃定义布局流程 | LengYue's Blog ViewModel之自定义构造函数 | LengYue's Blog Android Jetpack Compose 超快速上手指南 | LengYue's Blog android 11 [api 30] 适配指南 | LengYue's Blog Android 声明式 UI 框架 Litho 初探 —— Sections API | LengYue's Blog Android 声明式 UI 框架 Litho 初探 —— Layout | LengYue's Blog Android声明式UI框架 Litho 初探——基础使用 | LengYue's Blog Android声明式UI框架 Litho 初探 ——两种数据类型 | LengYue's Blog
Android声明式UI框架 Litho 初探 —— MountSpec的使用 | LengYue's Blog
LengYue · 2020-09-05 · via LengYue's Blog

Mount Specs

Mount Specs 用来生成渲染具体 View 或者 Drawable 的组件。 Mount spec 必须使用@MountSpec 注解来标注,并至少实现一个标注了@onCreateMountContent 的方法。 Mount Spec 相比于 Layout Spec 更复杂一些,它拥有自己的生命周期:

  • @OnPrepare,准备阶段,进行一些初始化操作。
  • @OnMeasure,负责布局的计算。
  • @OnBoundsDefined,在布局计算完成后挂载视图前做一些操作。
  • @OnCreateMountContent,创建需要挂载的视图。
  • @OnMount,挂载视图,完成布局相关的设置。
  • @OnBind,绑定视图,完成数据和视图的绑定。
  • @OnUnBind,解绑视图,主要用于重置视图的数据相关的属性,防止出现复用问题。
  • @OnUnmount,卸载视图,主要用于重置视图的布局相关的属性,防止出现复用问题 Android 小伙伴应该对上面这几个状态比较熟悉 这里引用一张 [美团技术博客][1] 的图: ![1697f2d29373ad9d.jpg][2] 下面这个代码,只是一个单纯的 ColorDrawable,你也可以替换成你需要实习的 View 例如 ImageView:
/**
 * 挂载操作有一个非常类似于 Android 的 RecyclerView Adapter 的 API。
 * 它有一个 onCreateMountContent 方法,用于在回收池为空时创建和初始化 View 和 Drawable 内容 onMount 使用当前信息对复用的内容进行更新。
 *
 * 预分配
 * 当挂载 MountSpec 组件时,其 View 或 Drawable 内容需要从回收池中初始化或重用。
 * 如果池为空,那么将创建一个新实例,这可能会使 UI 线程过于繁忙并丢弃一个或多个帧。为了缓解这种情况,Litho 可以预先分配一些实例并放入回收池中。
 *
 */
@MountSpec(poolSize = 0, canPreallocate = true, isPureRender = true)
class MainColorViewSpec {
    private const val TAG = "MainColorViewSpec"
    // onCreateMountContent 的返回类型应该始终与 onMount 的第二个参数的类型匹配。它们必须是 View 或 Drawable 子类。参数在构建时进行校验。
    // onCreateMountContent 不能接收 @Prop 或任何带有其他注解的参数。
    @OnCreateMountContent
    fun onCreateMountContent(context: Context): ColorDrawable {
        Log.d(TAG, "OnCreateMountContent() 在组件挂接到宿主 View 之前运行")
        return ColorDrawable()
    }
    /**
     * 挂载必须在主线程,因为需要处理 Android View。
     * @OnMount 方法不知执行耗时操作,原因跟上面类似,Android 主线程不能执行耗时操作
     * 在任何 @MountSpec 方法中使用 Output <?> 会自动为之后的阶段创建一个输入。在这种情况下,@OnPrepare 输出为 @OnMount 的输入。
     */
    @OnMount
    fun onMount(
        context: ComponentContext,
        colorDrawable: ColorDrawable,
        @FromPrepare color: Int // 名称必须对应
    ) {
        Log.d(TAG, "OnMount() 在组件挂接到宿主 View 之前运行")
        colorDrawable.color = color
    }
    // 该方法在执行布局计算之前只运行一次,并且可以在后台线程中执行。
    @OnPrepare
    fun onPrepare(
        context: ComponentContext,
        @Prop colorName: Int,
        color: Output<Int> // 名称必须对应
    ) {
        Log.d(TAG, "onPrepare() 在布局测量之前运行")
        color.set(colorName)
    }
    /**
     * 如果要在布局计算过程中自定义组件的测量,就要实现 @OnMeasure 方法。
     * 假设想要 ColorComponent 具有默认宽度,并在其高度未定义时强制执行特定的高宽比。
     */
    @OnMeasure
    fun onMeasure(
        context: ComponentContext,
        layout: ComponentLayout,
        widthSpec: Int,
        heightSpec: Int,
        size: Size
    ) {
        Log.d(TAG, "onMeasure() 在布局测量期间选择性运行")
        if (SizeSpec.getMode(widthSpec) == SizeSpec.UNSPECIFIED) {
            size.width = 40
        } else {
            size.width = SizeSpec.getSize(widthSpec)
        }
        // If height is undefined, use 1.5 aspect ratio.
        if (SizeSpec.getMode(heightSpec) == SizeSpec.UNSPECIFIED) {
            size.height = (size.width * 1.5).toInt()
        } else {
            size.height = SizeSpec.getSize(heightSpec)
        }
    }
    @OnBoundsDefined
    fun onBoundsDefined(c: ComponentContext, layout: ComponentLayout) {
        Log.d(TAG, "onBoundsDefined() 在布局测量之后运行")
    }
    @OnBind
    fun onBind(c: ComponentContext, view: ColorDrawable) {
        Log.d(TAG, "onBind() 在组件挂接到宿主 View 后运行")
    }
    @OnUnbind
    fun onUnbind(c: ComponentContext, view: ColorDrawable) {
        Log.d(TAG, "onUnbind() 在将组件从宿主 View 分离之前运行")
    }
    @OnUnmount
    fun onUnmount(context: ComponentContext, mountedView: ColorDrawable) {
        Log.d(TAG, "OnUnmount() 在组件从宿主 View 分离后,选择性运行")
    }
    /**
     * Mount Spec 可以使用@ShouldUpdate 注释定义一个方法来避免在更新时进行重新测试和重新挂载。
     * @ShouldUpdate 的调用的前提是 component 是"纯渲染函数'。
     * 一个组件如果是纯渲染函数,那么它的渲染结果只取决于它的 prop 和状态.
     * 这意味着在@OnMount 期间,组件不应该访问任何可变的全局变量。
     * 一个@MountSpec 可以通过使用@MountSpec 注释的 pureRender 参数来定自己为"纯渲染的"。
     * 只有纯渲染的 Component 可以假设当 prop 不更改时就不需要重新挂载
     */
    @ShouldUpdate(onMount = true)
    fun shouldUpdate(@Prop(optional = true) someStringProp: Diff<String>): Boolean {
        return someStringProp.previous.equals(someStringProp.next)
    }
}

使用:

val component2  = MainColorView.create(c)
            .widthDip(26f)
            .heightDip(46f)
            //colorName 就是我们定义的属性
            .colorName(Color.GREEN).build()

运行后打印的 log:

MainColorViewSpec: onPrepare() 在布局测量之前运行
MainColorViewSpec: onBoundsDefined() 在布局测量之后运行
MainColorViewSpec: OnCreateMountContent() 在组件挂接到宿主 View 之前运行
MainColorViewSpec: OnMount() 在组件挂接到宿主 View 之前运行
MainColorViewSpec: onBind() 在组件挂接到宿主 View 后运行
MainColorViewSpec: onUnbind() 在将组件从宿主 View 分离之前运行

到这里 MountSpec 的基本用法就讲完了。有了这两个 Component 就乐意做很多事了。下篇讲一下如何实现一个滑动的 View [1]: https://tech.meituan.com/ [2]: https://raw.githubusercontent.com/appdev/gallery/main/img/60dfab833b1840999e9165e83501295d~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp