Flutter 高性能、多功能的全场景滚动容器,一定要看!

Flutter 流畅度优化实践总结 希望大家能有一个好心态,想进什么样的公司要想清楚,并不一定是大公司,我选的也不是特大厂。当然如果你不知道选或是没有规划,那就选大公司!希望我们能先选好想去的公司再投或内推,而不是有一个公司要我我就去!还有就是不要害怕,也不要有压力,平常心对待就行,但准备要充足。最后希望大家都能拿到一份满意的 offer!如果目前有一份工作也请好好珍惜好好努力,找工作其实挺累挺辛苦的。这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。 阅读详情

目前闲鱼的主要业务场景都已经使用 Flutter 来实现,其中流式布局是最常见的页面布局场景(如搜索、商品详情等)。随着业务的快速迭代和业务复杂度的不断提升,对流式场景的能力和性能要求也越来越高;

在能力方面,最常见的如卡片曝光、滚动锚点、瀑布流布局等能力,随着业务和需求的不断变化,Flutter原生和一些开源解决方案,渐渐无法满足我们需求。

性能方面,流式场景下的列表滚动流畅度问题随着业务复杂度的增加而逐渐恶化,亟需解决以提升用户的使用体验。

针对以上在业务中面临的问题,我们设计了一套流式场景下通用的页面布局解决方案,我们将其命名为 PowerScrollView。

整体架构设计

在架构设计之前,我们充分调研了原生 Native 的滚动容器:UICollectionView(iOS) 和 RecyclerView(Android)。其中UICollectionView 的 Section(段落)理念令我们印象深刻,RecyclerView 的架构设计也启发了我们。由于 Flutter 的独特性,我们不能将其照搬过来,所以我们的目标是结合 Native 成熟的滚动容器,加以 Flutter 的特点,设计出更加优秀的滚动容器。

Flutter 原生有常用的 ListView、GridView,他们布局较为单一,功能较为简单。官方也提供了CustomScrollView的进阶Widget,CustomScrollView由多个 Sliver 进行拼接,以适应更复杂的使用场景,我们将基于 CustomScrollView 进行设计。

从使用角度出发,整个列表由若干个 Section 组成,又将 Section 分为 header、content、footer 三部分,header 为段落的头部,一般可作为 Section 的头部装饰,支持是否吸顶;footer 为段落的尾部,作为 Section 的尾部装饰。列表拥有下拉刷新与加载更多能力;content 为 Section 的正文,支持常见的布局方式:列表、网格、瀑布流以及自定义。Section 的 content 由任意个 cell 组成,cell 即为列表最小粒度的 item。

从 Flutter 原生容器出发,CustomScrollView 支持任意多个 Sliver 的组合,Sliver 提供了 SliverList、SliverGrid、SliverBox 等,已基本符合了我们要求。我们将 Section 的 header 和 footer 各对应一个 SliverBox,content 对应 SliverList 或SliverGrid,再单独为瀑布流布局开发一个 SliverWaterfall;再在整个列表的头部和尾部插入用于刷新加载更多的 Sliver。

我们将 PowerScrollView 分成数据源管理器、控制器、事件回调和刷新配置四大部分。如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D1xKyHzS-1612336882844)(https://upload-images.jianshu.io/upload_images/24957688-22994e86d54d0e54.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

数据源管理器:用于数据的管理,里面就涉及 Sections 初始化与通常的增删改查。

控制器:主要用于控制 PowerScrollView 的刷新、加载更多,控制滚动到某个位置等。

事件回调:我们将事件分类,外部使用时可只监听需要的回调。

刷新配置:为了提升刷新的灵活性,我们将刷新单出抽出,既可以使用我们提供的标准刷新组建,也可自定义。

功能完善

我们为 PowerScrollView 完善了业务使用的核心诉求,包括自动曝光、滚动到某个 index 、瀑布流、刷新加载更多等能力。下面将重点介绍前两部分。

自动曝光能力

在 Flutter 中,通常不得不将曝光放在 build 函数中,这使得曝光会错乱,不在屏幕上但是在屏幕缓冲区的部分将会被错误曝光,且有多次曝光问题,代码臃肿混乱,这都使得业务层非常头疼。曝光能力是各种业务都必须的核心诉求,我们在 PowerScrollView 中统一进行了封装,通过事件回调给使用者。

前面我们知道,在 PowerScrollView 中,我们用 cell 封装了最小粒度的 item,因为对 item 的封装,使得我们的掌控力大大增强。正因为此,我们自定义了 cell 的 StatefulElement,在 element 的生命周期中 mount、unmount 记录当前 element,利用 InheritedWidget ,将树上的 element 维护在外面的列表中。

在 PowerScrollView 的滚动过程中,我们会遍历检查 element 数组,筛选屏幕中的元素进行曝光回调。其中被筛选掉的即为缓冲区的元素,同时维护个数组避免单元素当次屏幕中多次曝光。

为了减少滚动中的多次遍历检查 element 数组,我们加入了控制滚动采样率的可配参数,通过此参数,我们可以控制滚动一定距离后才进行检查。

在复杂场景中,会存在 cell 高度先为 0,下载模板渲染后再撑开的情况,这种情况下整个 element list 数据会非常大,且数据并不正确,我们需要过滤掉这种。但是当 cell 刷新之后,有了真实的高度,我们需要进行正确的曝光。所以我们在 cell 中监听了 size 的变化,当高度由 0 到非 0 的时候,通知上层进行一次曝光。

滚动到某个index

Flutter 本身提供了滚动到 position 距离的能力,但一般业务场景下,我们不知道要滚动的距离,最多知道要滚动到第几个,这使得在 Flutter 侧很多交互无法实现。这个问题我们会分几种场景进行分析。

场景一:当要滚动的目标 index 的 cell 在视图树中(当前屏幕及缓冲区),由于我们已经维护了一个屏幕及缓冲区的element数组,我们可以遍历找到,然后将其滚动到可见区域即可。

场景二:当要滚动的目标 index 的 cell 不在视图树中时,首先我们根据当前屏幕的 index 与目标 index 进行比较,判断是需要往上滚动还是往下滚动。然后,以较快的速度进行特定距离的滚动,滚动之后再递归,直到找到目标 index。由于滚动距离与时间的不确定性,极端情况下会没有动画效果,普通的动画效果可能也会有些生硬。

性能优化

为什么要做局部刷新

在实际的流式业务场景中,经常会因为数据源的更新而刷新整个列表容器:例如加载了下一页的数据、删除或者插入某一个 cell,甚至某个 cell 的一个按钮状态的变化;

刷新范围过大往往是造成列表容器卡顿、流畅度降低的主要原因,严重影响了用户的操作体验。所以我们需要尽量减少 Widget tree 打脏刷新的范围,减少 Element rebuild 的调用,实现局部刷新的能力。

Viewport 刷新的过程

为什么说整个列表容器打脏刷新会带来这面严重的耗时呢?我们来简单看一下 Viewport 的刷新过程。

列表容器被打脏之后,会做两个关键的操作:

Viewport 所有 sliver 的 Element 都会 rebuild;

Viewport 也会重新 layout,进而所有的 sliver 也会重新 layout;

我们来先看 Viewport layout 的过程:这个方法的核心,首先找到当前的 center sliver(默认是第一个child)的位置,然后向上、向下遍历Viewport每一个sliver;每个 child sliver 根据当前 Viewport 在 Scrollview 中的 scrollOffset,Viewport的大小以及cacheExtent大小等信息 (SliverConstraints),计算当前需要展示的child的index范围,layout 每一个在可显示范围的child;

以下图例,SliverList可视范围内需要layout的child index为23;SliverGrid需要layout的child index为03;

再来看 Viewport 所有 sliver 的 Element rebuild 的过程,这个过程才是列表容器刷新耗时的关键;

我们先来看一下常见的几种布局 SliverList、SliverGrid 以及我们自定义的瀑布流布局 SliverWaterfall 的实现,它们都继承自SliverMultiBoxAdaptorWidget,一个管理多 child(Box模型)的 sliver 的基类;它对应的 Element 是 SliverMultiBoxAdaptorElement,主要负责 child 的创建、更新、移除等生命周期相关的工作,这正是局部刷新需要精细处理的地方。

SliverMultiBoxAdaptorElement 内部维护两个 Map,缓存 child element 以及 child widget,在 ViewPort 需要的时候(上面提到的layout过程)lazily build 自己的 child;

rebuild 过程之所以耗时是因为要清空所有 child widget 缓存,重新 build child widget,update child Element;如果遇到数据的变化,例如 insert、delete,很有可能导致 element 无法复用,这样 rebuild 的成本会更高。

局部刷新的实现原理

摸清了基本原理之后,我们就在思考,当列表容器内容发生变化的时候(比如 insert、delete、LoadMore),是否可以做出一些优化,只让发生变化的部分去 build、layout 呢?

首先我们认为 sliver 的 Element 全部 rebuild 的做法过于简单粗暴,我们可以通过更精准的控制 sliver element 中,childWidgets 与 childElements,来实现局部刷新的目的;

下面我们来看看针对与具体的场景,如何实现精准的 childWidgets 与 childElements 控制,实现局部刷新的能力的。

可变的 child count

在常见的需要局部刷新的场景,容器元素的数量往往会发生变化。在常见的 CustomScrollview 使用中,childCount 都是创建时指定的,当 childCount 方式变化,就需要重新 build 列表容器;

第一步就是避免因为 sliver 内部元素数量变化,必须重新build整个容器的问题;

虽然也可以使用childCount为空,根据builder返回null来决定是否为最后一个child的方式实现可变childCount的目的,但这种方式并不太符合常用的习惯,对使用方也会增加额外成本,所以并未采用这种方式。

做法比较简单,通过继承自SliverChildBuilderDelegate,修改childCount获取方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D4Lrk4S7-1612336882848)(https://upload-images.jianshu.io/upload_images/24957688-78c067342b61a505.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

局部刷新之 LoadMore

LoadMore的实现相对会比较简单,需要做的主要有两点:

  1. 清理widgets缓存,防止不算加载的过程中内存占用过大;保存与 _childElements 中 index 相同的 widget;这里有一个需要特别注意的点:要过滤为 null 的 widget,否则这个位置的 widget 无法正常展示;(_childWidgets 最后一个 index 会是一个为 null 的值,具体为什么插入一个为 null 的 widget 大家可以阅读源码寻找答案)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryEHpvfJ-1612336882849)(https://upload-images.jianshu.io/upload_images/24957688-0efe4d1ea3d7e692.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

  1. 最后打脏sliver,重新layout children:

使用 Dart DevTools 的 TimeLine 数据对比两种 LoadMore 方式的耗时情况如下图:

SetState 的 timeline:

LoadMore 的 timeline:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCCr2kh3-1612336882851)(https://upload-images.jianshu.io/upload_images/24957688-aca4817353ae164a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

局部刷新之 Delete

首先整理 childWidgets 的内容,根据 delete 的 index,重新调整 childWidgets 中 widget 与 index 的对应关系;

接下来是 _childElements 的处理,如果需要删除的 index 还未创建,只需要把当前 sliver 的 RenderObject 的 layout 信息标脏,重新 layout 自己即可。注意这个过程是不会重新 layout 当前 viewport 已经展示的 child 的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uUJu4Otl-1612336882852)(https://upload-images.jianshu.io/upload_images/24957688-86350bab365b7ef3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

否则要找到要删除的 child element,deactivate 对应的 element,其对应的 RenderObject 从 Render tree 上移除:

这个过程同时会维护好 child 的 RenderObject 中 ParentData 的 previousSibling 和 nextSibling 的关系;

接下来调整 _childElements 中 Element 与 index 的对应关系;

最后更新每一个 child 的 slot:

最后将sliver的RenderObject标脏,下一帧重新layout刷新。

局部刷新之 Insert

Insert的实现过程与上面的类似,可以根据上面的过程自行实现,这里就不做赘述;

Element 复用能力

不管是 iOS 的 UITableView、UICollectionView 还是 Android 的 RecyclerView,都支持 cell 的复用能力;在 Flutter 的列表容器中,在不修改 framework 层的情况下,是否能够实现 element 的复用呢?

首先我们来分析 element 被回收的过程,SliverMultiBoxAdaptorElement 通过 _childElements 来缓存 elements,当滚动超出 viewport 的显示以及预加载范围或者数据源发生变化,会通过调用 collectGarbage 方法回收不需要的 elements;

我们可以通过重写 collectGarbage 的方式,在不使用 keepAlive 的情况下,截获本该 deactive 的 child element,放入缓冲池中;在需要创建 element 的时候,优先从缓冲池获取;

虽然原理比较简单,也会遇到一些需要注意的点:需要缓存的 element 需要通过 remove 方法,将它从 childList 中移除,而不是真正的销毁 element, 如果将它被置为 defunct 状态,这样就无法复用了。

因为业务中卡片布局基本相同,这里面复用的逻辑做的相对简单,事实上针对卡片类型复用才能发挥出最好的效果。

分帧渲染

在实际的滑动过程中,如果一帧的时间内需要 build 过多的 cell ,很容易引起掉帧的情况,用户会感觉到卡顿。为了减少这种情况,我们在 cell 层面引入了 placeholder 的机制:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3pg4qdx8-1612336882855)(https://upload-images.jianshu.io/upload_images/24957688-bfbc2c011999ced6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

使用方可以为每个 item 定制较为简单的 Widget,这样在一帧任务较多时,通过一定的策略,先 build placeholder 进行渲染,延迟到之后几帧再进行实际 cell 的 build。由于 viewport 上下都有缓冲区,在延后的帧设置较少时,用户并没有机会看到 placeholder,所以业务上并不会有影响。placeholder 最明显的作用是削峰,较长的一帧耗时会被下几帧瓜分。

下面数据是使用复杂商品 card 在瀑布流中的场景,使用机型为 Pixel XL。从数据上看,分帧使平均耗时有所增加,但是90、99、最长帧耗时,都有明显的降低,丢帧数也有所减少。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ugeuBaT-1612336882855)(https://upload-images.jianshu.io/upload_images/24957688-1a00854a96bdffb4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

值得注意的是,对于 cell 过于复杂的场景,即使一帧 build 一个都会超时,那么以 cell 为最小粒度的分帧就没有优化效果了,类比到在性能非常差的手机上,普通复杂的 cell 的分帧可能会使流畅度降低。这个时候需要降低 cell 复杂度或者缩小分帧的粒度。

实际应用场景

PowerScrollView 已经在闲鱼多个核心页面线上全量使用,如下图:

完善的能力、优良的性能、较低的接入成本,都使得使用方受益颇多。

总结和展望

经过对列表容器能力的不断完善、流畅度方面不断优化,目前 PowerScrollView 已经能够更好的支撑闲鱼流式布局下的业务,给用户提供更好的使用体验。

但在一些低端机型上,长列表的表现仍然不能让人满意;瀑布流等一些需要复杂布局计算的场景,如何更好的优化布局计算过程,这些都是需要我们继续探索的方向。

目前复用实现还比较粗糙,未来也会深入到Flutter引擎,寻找提升复用能力的方法,让 PowerScrollView 真正成为一个高效流式布局的解决方案。

另外在端到端研发方面,我们在探索将列表容器与动态模板相结合,实现端云一体的页面搭建解决方案。

作者:闲鱼技术
链接:https://juejin.cn/post/6917484706585903118
来源:掘金

淘特 Flutter 流畅度优化实践 · 二期 淘特端架构联合业务团队共同发起“基础链路极致体验优化”的项目,目标在时长与流畅度方面获得极致体验,本文将为大家详细解析淘特 Flutter 流畅度优化实践二期部分。 阅读详情

相关推荐

在RecyclerView,安卓面试问题

ViewBinding 当AutomaticBindingAdapter无法满足需求时,可使用MutableBindingAdapter + ViewBindingHolder //创建Adapter,继承自MutableBindingAdapter,共3个泛型参数,分别是: // T -> 实现Identifiable接口的数据类 // VH -> ViewBindingHolder的实现类 // VB -> 根据布局自动生成的ViewBinding或ViewDataBinding

m0_65321095的博客 948

悄悄精通 RecyclerView,flutter瀑布流布局加载更多

然后,创建一个类,名字为 Datasource,并且可以接收一个 Context 类型的参数。创建一个叫做 getFlowerList() 的函数,它负责返回花的名称列表。 class Datasource(val context: Context) { fun getFlowerList(): Array { return context.resources.getStringArray(R.array.flower_array) } } 在 MainActiv.

m0_66265052的博客 889

Flutter 高性能、多功能的全场景滚动容器,一定要看,android开发app

控制器:主要用于控制 PowerScrollView 的刷新、加载更多,控制滚动到某个位置等。 事件回调:我们将事件分类,外部使用时可只监听需要的回调。 刷新配置:为了提升刷新的灵活性,我们将刷新单出抽出,既可以使用我们提供的标准刷新组建,也可自定义。 功能完善 我们为 PowerScrollView 完善了业务使用的核心诉求,包括自动曝光、滚动到某个 index 、瀑布流、刷新加载更多等能力。下面将重点介绍前两部分。 自动曝光能力 在 Flutter 中,通常不得不将曝光放在 build 函数中,这使得

m0_65146205的博客 316

Flutter 高性能、多功能的全场景滚动容器,一定要看!(1)

各行各样都会淘汰一些能力差的,不仅仅是IT这个行业,所以,不要被程序猿是吃青春饭等等这类话题所吓倒,也不要觉得,找到一份工作,就享受安逸的生活,你在安逸的同时,别人正在奋力的向前跑,这样与别人的差距也就会越来越遥远,加油,希望,我们每一个人,成为更好的自己。BAT大厂面试题、独家面试工具包,资料包括 数据结构、Kotlin、计算机网络、Framework源码、数据结构与算法、小程序、NDK、Flutter《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取。

FZBH_Q的博客 375

ListView流畅度翻倍!Flutter卡顿分析和优化方案

最近因为在做Flutter中相关的性能优化,在收集很多性能数据之后发现ListView组件在一些场景下(例如加载更多)容易引起页面卡顿,看到了闲鱼的Flutter 高性能、多功能的全场景滚动容器。但奈何该组件没有开源,因此准备从文章给出的思路尝试研究和开发一个高性能的ListView。这个系列预计会分为4-5篇文章,前两篇主要对现有问题研究和分析,后三篇实际的进行开发。 原理篇: 1、Widget、Element、Render树究竟是如何形成的? 2、ListView的构建过程与性能问题分析 实战篇: 1、

A_pyf的博客 3487

Flutter学习资料集合(开发必备)

Flutter学习资料,包含了Flutter学习社区、入门学习资料、进阶学习资料、性能优化、面试资料和书籍等资料

henhenha的博客 4053

Flutter 高性能、多功能的全场景滚动容器,一定要看!,2024Android者真的太难了

笔者之前工作是在金融公司可能并不是特别追求技术,而笔者又是喜欢追求技术的人,所以格格不入,只能把目标放在互联网大厂了。也希望大家都去敢于尝试和追逐自己的梦想!BATJ大厂Android高频面试题《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取![外链图片转存中…(img-ny6JHfOg-1712511064768)][外链图片转存中…(img-7hUs8Ltv-1712511064768)]

2301_79655511的博客 512

线性模型:AR、MA、ARMA、ARMAX、ARX、ARARMAX、OE、BJ等

线性模型:AR、MA、ARMA、ARMAX、ARX、ARARMAX、OE、BJ等

zephyr_wang的博客 1万+

Flutter 高性能、多功能的全场景滚动容器,一定要看!(1),百度Android岗一面+二面内容

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!Android前沿技术大纲全套体系化高级架构视频Android高级架构资料、源码、笔记、视频。

2401_84149530的博客 365

三菱PLC FX3U温度控制系统案例

U0\G100是平均次数的设置,设置为100个数据取平均,这样数据显示更加稳定,通道1的数据显示再U0\G10里面,温度模块的数据对应是0~150°输出0~10V,对应到0~10000的数据10000是150°的66.667倍,因此把数据传送到D0后转换成浮点数,再去除以66.667,就得到实际的温度了,数据存放到D2。触摸屏软件使用EBpro软件,设计界面如下,右边的是温度的实时曲线图,左边的是温度报警表,当当前温度超过了报警温度这种时,就会在这个表格上显示对应对应的报警。(1)实时显示温度值;

u010670857的博客 4058

麒麟系统下PaddleOCR的离线部署实战:从环境搭建到模型优化

本文详细介绍了在国产麒麟操作系统上离线部署百度PaddleOCR的完整实战流程。从系统环境准备、Conda虚拟环境搭建,到PaddlePaddle框架和PaddleOCR的离线安装,再到预训练模型的本地部署与优化技巧,提供了全面的文字识别解决方案。重点解决了离线环境下依赖包管理、模型量化加速等核心问题,并附带了批量处理与内存监控的进阶代码,适用于对数据安全有严格要求的政务、金融等场景。

docker7nomad的博客 963

Flutter 高性能、多功能的全场景滚动容器,一定要看,移动端网页开发工具

滚动到某个index Flutter 本身提供了滚动到 position 距离的能力,但一般业务场景下,我们不知道要滚动的距离,最多知道要滚动到第几个,这使得在 Flutter 侧很多交互无法实现。这个问题我们会分几种场景进行分析。 场景一:当要滚动的目标 index 的 cell 在视图树中(当前屏幕及缓冲区),由于我们已经维护了一个屏幕及缓冲区的element数组,我们可以遍历找到,然后将其滚动到可见区域即可。 场景二:当要滚动的目标 index 的 cell 不在视图树中时,首先我们根据当前屏幕的 i

wa2231a的博客 320

python入门:Anaconda和Jupyter notebook的安装与使用

一、安装和使用Anaconda 1、anaconda是什么? Anaconda在英文中是“巨蟒”的意思,与python的意思相近,它是python的包管理器和环境管理器。 2、为什么需要安装anaconda? 原因有以下几点: 附带一大批常用的数据科学包,方便立即开始处理数据 管理包 anaconda是在conda(一个包管理器和环境管理器)上发展来的;而conda可以很方便地对第三方的包进行安装、卸载和更新。 管理环境 conda可以为不同的项目建立不同的运行环境,避免不同的项目或包版本

PY洋洋 1万+

Flutter for OpenHarmony:Flutter 滚动核心SingleChildScrollView 组件详解

在移动应用开发中,如果说 TextField是“用户表达的窗口”,那么滚动容器就是“内容展示的舞台”。无论是长表单、商品详情、新闻文章,还是设置列表、个人主页、聊天记录,只要内容超出屏幕范围,就离不开滚动。

2503_93740796的博客 1026

计算机组成原理实验logisim(四)之运算器实验

注意,上半张图中所有连入32位宽多路选择器的器件和的“数据位宽”都是32,而中间部分连入1位多路选择器的器件的“数据位宽”皆为1(除了连向“32位加法器”的那一个非门,是32位)。第二到第五个实验的测试,具体怎么测试好吧我也不太清楚,听室友说就是让你分析各种亮灯的原理,比如你点这个,那个为什么颜色要变等等,我就不粘图了,恩大家加油。连图如下,需要在连线之前格外注意“异或门”和“与门”的位宽,将16改为1,不然数据无法通行。关于学术问题,为何这样连什么的,就别问我了,我也没有搞的那么透彻。

qq_54422132的博客 2万+

Flutter for OpenHarmony:Flutter 高级滚动引擎CustomScrollView 组件详解

如果说 ListView 是“信息高速公路”,GridView是“视觉陈列橱窗”,那么 CustomScrollView 就是一座精心规划的“城市综合体”:它不仅能容纳多种滚动内容(Banner、Tab、列表、网格、广告位),还能让它们协同工作、物理连贯、性能卓越,共同构建出符合现代设计规范的高级 UI。

2503_93740796的博客 1007

RK3568平台开发系列讲解(NPU篇)什么是RKNPU

📢本篇章将介绍RKNPU。

内核笔记 4354

基于HarmonyOS API 24 Flutter for OpenHarmony 实战:嵌套滚动视图实现

本项目通过 NestedScrollView 与 SliverAppBar 的组合,实现了一个具有可折叠头部效果的滚动页面。虽然代码量不大,但涉及到 Flutter 中比较高级的滚动机制,是理解 Flutter Sliver 体系的绝佳入门示例。从技术实现来看,项目展示了 Flutter Sliver 体系的强大能力。Sliver 是 Flutter 滚动系统的基石,理解 Sliver 的工作原理对于开发复杂滚动界面至关重要。

2301_79516858的博客 510

用 SAP CDS View 和 Fiori Elements 快速打造 CRM Service Order 应用──十分钟跑通从数据模型到上线的全流程

通过CDS 视图 + Fiori Elements的组合,我们得以在极短时间内把传统 CRM Service Order 场景迁移到现代化的 Fiori 界面,而且代码量极小、可维护性高。若后续需要离线能力,可考虑将同一 OData 服务暴露给 Mobile Services,再配合 MDK 或 UI5 Mobile Toolkit;若要支持复杂审批流,则可以在同一 Launchpad 内嵌入 Process Visibility Dashboard,实现端到端可视化。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 388

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Flutter开发者必备学习资料

Flutter学习资料,包含了Flutter学习社区、入门学习资料、进阶学习资料、性能优化、面试资料和书籍等资料

the_cleaner的博客 1301

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

上一篇: HR:作为一个Android开发者,开源框架设计思想你搞不懂,你走吧!
下一篇: 98%Android开发者已收藏的“80”个开源库
紫苏不是菜
博客等级 码龄6年 40粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值