一种简单的Android骨架屏实现方案----0侵入0成本
对骨架屏的理解
什么是骨架屏
所谓骨架屏,就是在页面进行耗时加载时,先展示的等待 UI, 以告知用户程序目前正在运行,稍等即可。 等待的UI大部分是 loading 转圈的弹窗,有的是自己风格的小动画。其实大同小异。而骨架屏无非也是一个等待的UI。基本是由各种灰色块组成,夹杂着一些代表特殊样式的其他浅颜色的色块。骨架屏的不用之处就在于这些灰色块的排列组合和真正展示出来的页面样式基本一致。因此骨架屏的展示除了告知用户程序正在加载外,还能让用户大概知道稍后将要展示的内容是什么,给了用户一些期待,从心理上,让用户更愿意等待一会。
明明等2秒钟就会等够了返回离开,现在的一点期待刺激着可以等3秒钟了。解决不了页面加载耗时,就解决用户的等待意愿啊,哈哈。
就像减肥一样,坚持的困难性就在于你哼哧哼哧一顿,不知道到底减了多少,效果是什么。缺少刺激的机制。

(就像我先把这两个对比图丢这,你有了一个心理预期,就更有意愿继续读下面这些枯燥的纯文字了,(*^▽^*))
对骨架屏功能的探究
目前的各种骨架屏框架,有的需要各种配置,有的需要在正常的代码逻辑之外,再编写展示这一堆灰色块的逻辑,比如上图左侧的正常页面,为了展示其骨架屏,需要对照着左侧页面的结构,手写一个各种灰色块的 xml 文件,然后在加载等待前后进行两个布局的切换。对于列表来说,还需要编写空的adapter来展示。此上种种少不了各种编码,既增加繁琐的工作量,又和正常的业务逻辑交织在一起,很不友好啊。程序员是拒绝的。
另外,通过调研发现,
1、大部分的骨架屏是不支持交互的,包括带列表的页面,只有美团的骨架屏可以正常交互操作。其实不支持交互也是情理之中,毕竟说到底就是个等待UI,停留时间就1-2秒,再长,那真的说明这个页面的加载该优化了。美团这个只能说是牛掰了。
2、对于列表的填充展示,骨架屏的列表样式和最终的item样式还是有较大区别的,有的可以说是差别很多。归根结底还是因为有工作量,映射个大概就差不多了。此外实际的业务逻辑会有各view的展示隐藏,所以无法一一对应。
理想的骨架屏框架
那骨架屏的使用可以有多简单呢?
1、希望没有一丝一毫的额外代码量。
2、对正常业务逻辑毫无侵入性,引入后,想用就用,不想用就不用,插拔式操作。
最终设计的骨架屏框架满足了以上0工作量0侵入的需求,但是同时也为此舍弃了一些细节。
骨架屏原理简介
其主要原理是:
解析正常页面的各 view 元素的布局位置,然后在已有页面的上面增加一层蒙层skeletonview,然后通过draw方法,将解析出的各个view 的 rect位置在skeletonview上画出来。
对于普通的view:
比如 MainActivity 加载的xml文件是 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns: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:id="@+id/root_ll"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/content_tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="20dp"android:layout_margin="15dp"android:text="骨架屏的实现原理"/>
</LinearLayout>
在页面 layout 的时候就可以计算出 content_tv 的 x、y坐标,以及长h、宽w。即view 的rect
然后根布局 root_ll 的上面新增一个铺满的空白view,在此view draw的时候,调用
canvas.drawRoundRect(rectF, radius, radius, paint)
将rect画出来,至此就实现了骨架屏的效果

view 筛选
在真正的实现中,对灰色块的样式进行了处理,一是加了圆角,而是缩减了它的宽和高,也就是展示出来的灰色块要比view看起来短细一些。
主要原始是有些view是没有margin,紧挨着的,而内部有自己的padding,所以展示出来的灰色块连成了一片,另外一个原因就是将页面上的一些小view过滤掉,通过设置阈值,view 的 rect缩减后的大小小于这个阈值时,就直接丢弃了。
对于列表view(RecyclerView、ListView):
舍弃了item样式的准确性。采用模糊处理的方式来填充列表灰块。
即事先编写好几套item样式的灰色块组合,遇到列表,选择类似的item样式画出来即可。
主要原因:
1、复杂度的限制
如果去展示真正的item,那么必须要加载这个item 的xml布局,一旦要这么做,那目前的骨架屏框架结构就被推翻了,无法通过一个蒙层view来显示页面所有元素灰块的绘制。另外就必须要走列表加载Adapter的流程了,增加了工作量,做不到0侵入,0代码的目的了。。
2、样式的限制
对于一个app来说,因为业务的统一,所以app中的列表样式基本上可以归纳为几种,不会有太多的发散。加载一个真正的item,和使用一个预设好的灰块组合,差别不是很大。
所以在展示骨架屏时遇到列表,直接配置一个最相似的 item样式即可。
比如

就是预设好的一组 rect

然后在 RecyclerView 的位置 Rect 内,不停的重复draw 几个这样的灰色即可。
对于骨架屏的展示,基本就是以上这两个方面。
下面是实际使用中的效果:

使用方式
页面级别
如何使用呢?
其实还是需要一点工作量,这里的0代码,夸张了一点
比如要对下面的布局使用骨架屏:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns: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:id="@+id/root_ll"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/content_tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="20dp"android:layout_margin="15dp"android:text="骨架屏的实现原理"/>
</LinearLayout>
只要在布局文件中增加一层父view即可:
<?xml version="1.0" encoding="utf-8"?>
<com.haodf.skeleton.SkeletonLayoutxmlns: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:id="@+id/skeleton_sl"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/content_tv"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"android:background="#dcdcdc"android:textSize="20dp" /></LinearLayout>
</com.haodf.skeleton.SkeletonLayout>
然后在代码里使用:
//requester请求网络数据
skeleton_sl.loading()
requester.success { data ->skeleton_sl.normal() initUI(data)}.failed{ error ->skeleton_sl.normal()toast(error) }.reqeuset()
其实 skeleton_sl.loading() 出现的地方原本就是你发起网络请求时,展示loading弹窗的地方,只要替换一下即可。
说白了,代码上的修改量,就是把你现在的 LoadingDialog.show() 和LoadingDIalog.dismiss() 替换为 skeleton_sl.loading() 和 skeleton_sl.normal()
简单吧。
局部使用
到这里你会发现,骨架屏是通过对目标view进行包裹实现的。这也说明这个骨架屏不仅可以让整个页面实现骨架屏效果,还可以让任何一个view实现这个效果,无非就是包裹住那一层view即可。
比如:

原理简介
首先需要 SekeltonLayout 标签包裹住目标view 标签 target
<SkeletonLayout><LinearLayoutid:target......>......</LinearLayout>
</SkeletonLayout>
在 SkeletonLayout 内,在view 绘制流程的 OnLayout 阶段,解析 以 target 为 root 的 view 树,遍历找出所有的 View,和特殊的 ViewGroup,比如 RecyclerView 等,
然后通过
view.getGlobalVisibleRect(rect)
获取到这些view 在屏幕上的位置。
于是所有要绘制灰块的view变成了一个 rect 列表 rectList。
接下来 为 SkeletonLayout 添加一个子 view :skeletonView ,作为绘制灰块的蒙层。
addView(skeletonView, 1)
skeletonView?.layoutParams = LayoutParams(this.measuredWidth,this.measuredHeight
)
skeletonView 就是 SkeletonLayout 标签的宽高,既挡住了下面的正常UI,又作为一个画布,在其上画各个rect.
然后,在 skeletonView 的 onDraw方法中,遍历 rectList,针对每一个 rect 进行绘制即可。
canvas.drawRoundRect(rectF, radius, radius, paint)
rectF: RectF 就是rect,只不过转换一下,支持圆角绘制罢了。
骨架屏的 loading 和 normal 状态,就是 skeletonview 的 展示隐藏切换。
一些优化效果:
灰块颜色:
没有特殊处理,所有的灰块都是灰色的。而实际页面可能会有 一些其他颜色的圆角背景之类,还有一些特殊颜色的文字等等,有时候将这些 view 的灰块按照原来的颜色和形状展示出来,会更好一些。即让页面不那么呆板,又能给用户一些颜色上的激励,刺激用户的想象(这里是橘色圆角,肯定一会展示一个可以点的按钮吧,嗯~ 等等看)
所以,在解析view 的rect时,也对每个view 的颜色、样式、背景进行了解析。
对于背景,通过view.background来解析
is GradientDrawable, is StateListDrawable -> {val d = view.background.constantState?.newDrawable()d?.colorFilter = PorterDuffColorFilter(0x88ffffff.toInt(), PorterDuff.Mode.SRC_ATOP)d?.bounds = rectgrayLands.add(GrayLand(this).apply {landType = GrayLand.LAND_TYPE_DRAWABLEdrawable = d})return false
}
在背景的处理过程中,也需要注意不能原样绘制,那样颜色太鲜艳了,和整个骨架屏灰块色系不搭,所以对解析出的drawable 又进行了颜色的淡化处理。
最终,这些有背景的 view 的绘制,则通过
drawable.draw(canvas)
来绘制。
对于文字颜色:
无特殊处理,均处理成灰色。有些特殊颜色的 textView ,希望在灰块展示时就显示其特殊性,希望能以它本来的文字颜色作为灰块展示。
这个问题是通过 tag 标签来实现的。某个 textView 想以文字颜色来展示灰块,只需要在xml文件中声明自己的tag标签值即可。
<TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="19dp"android:textColor="#48aeff"android:text="这是一行蓝色的文字"android:layout_centerHorizontal="true"android:layout_marginTop="10dp"android:tag="sk_text_color"/>
对比一下效果:
正常UI

没有特殊设置样式

设置了蓝色和绿色两个 textview 的 android:tag="sk_text_color"

动画效果:
动画主要有等待时的loading动画,和状态切换时的动画
loading动画:
是通过 draw方法和 ValueAnimator来实现的。
draw来绘制每一时刻的样子,ValueAnimator 来不断的改变动画的属性,比如从左到右的位置变化。
默认提供了两个loading动画。一个是从左到右一闪一闪而过的光条。这个其实就是自定义了一个颜色渐变的 drawable 文件,首先将它进行一点角度的旋转,然后通过 drawable.draw()方法绘制,而 ValueAnimator负责改变它从左到右的位置。
另一个是透明不断循环变化的动画,这个是通过 ValueAnimator来不断改变 paint 的透明度,进而影响整个骨架屏灰色块的透明度。
另外,将动画的逻辑抽取出来,提供了一个自定义loading 动画的接口 LoadingAnimator。
实现它即可快速自定义loading动画。
切换动画:
即页面从loading到normal时的,这个简单了 就是一个 骨架屏 skeletonView 透明度的渐变。
自定义配置:
skeletonLayout.config {listviewItemType = ItemRect.ITEM_TYPE_3loadingAnim = ILoadingAnimtor.TYPE_BAI_JV_GUO_XIskeletonEnable = truecustomLoadingAnim = ILoadingAnimtor的子类
}
listviewItemType :设置列表展示的 item样式,可以根据自己的项目,提前定义好几种item 样式,在不同页面设置不同的样式即可
样式1:

样式2:

样式3:

skeletonEnable :是否开启 骨架屏效果
对于已经在 xml文件中添加 SkeletonLayout 标签的页面,如果不想用骨架屏效果了,不需要再去修改xml文件,直接一行配置就可以开关骨架屏效果。
loadingAnim:选择默认提供的两种loading动画效果
customLoadingAnim :使用自定义的loading 动画效果,
这个骨架屏有什么遗憾的地方呢:
1、列表样式的妥协,放弃了准确性。
改为事先写好几种item 灰块样式,使用时选择类似的展示。
带来的好处就是无需关系任何页面的具体UI,无任何额外代码量。
补救措施
其实这个问题也有一个补救的措施,在实际使用的时候,可以扩展自定义的item样式。针对某个页面的列表,根据它的 item的样式,再写一组同样组合的灰块,展示时通过listviewItemType 配置一下,即可达到 item样式的准确性。
2、tools属性不友好
骨架屏的textview的灰块展示完全依赖于 xml 页面的默认样式,我们在 xml中写一个
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="20dp" android:text="骨架屏的不同之处" />
就会展示它的灰块。
但是如果把 android:text="骨架屏的不同之处" 替换为 tools:text="骨架屏的不同之处" , 因为 tools 属性只是方便我们预览的,在页面加载的时候,这个 textView 的text还是“”,就不会展示出来,因此 view 的绘制流程就会计算出这个textview 的 rect 是 (0,0,0,0),因此它对应的骨架屏上就没有灰块。
这个问题的一个小解决方案是设置 layout_width="match_parent",
这样会计算出textview 的高度为字高,宽度为父view宽度,就能正常展示灰块。当然,并不是所有的 textview 都可以设置 match_parent 的。
这是一个很矛盾的问题,一方面使用 tools 很安全,既能预览实际展示时的页面UI效果,又不会因疏漏导致页面正式展示时展示了一些开发人员写的占位文案。
如果单纯为了骨架屏的展示,把 tools:text 换成android:text ,是不合理的,得不偿失。而使用tools:text,就会使预览看起来很丰满的一个页面,在展示骨架屏时,只有寥寥无几的几个灰块。
补救措施
在SkeletonLayout 的 onLayout之前,遍历到的这些 textview,如果其 text值是空的,则为他们赋一个默认的文案值,这样,在OnLayout 的时候就会计算出他们的有效 Rect。然后再把他们的text值恢复为空。
实测这个方案是可行的,但是是不安全的,数据加载过程中,骨架屏是在 loading和normal之前切换的,而这又同时伴随着各页面根据自己的实际业务逻辑,在数据返回后对UI进行正式的赋值渲染。
因为无法确保为 textview赋默认值的操作和实际的UI赋值操作是否冲突,万一先进行了实际赋值,又进行了骨架屏的赋值,所以把这一个方案暂时关掉了。
关于我实现的这个骨架屏框架基本介绍完了,=框架已投入项目使用了一段时间,效果还不错。主要还是提出一种思路,如何简单实效骨架屏效果。仅供大家参考。
如果想要框架完整代码使用的,可以点我。
相关文章:
一种简单的Android骨架屏实现方案----0侵入0成本
对骨架屏的理解 什么是骨架屏 所谓骨架屏,就是在页面进行耗时加载时,先展示的等待 UI, 以告知用户程序目前正在运行,稍等即可。 等待的UI大部分是 loading 转圈的弹窗,有的是自己风格的小动画。其实大同小异。而骨架屏无非也是一…...
【Kubernetes 架构】了解 Kubernetes 网络模型
Kubernetes 网络使您能够在 k8s 网络内配置通信。它基于扁平网络结构,无需在主机和容器之间映射端口。 Kubernetes 网络支持容器化组件之间的通信。这种网络模型的主要优点是不需要在主机和容器之间映射端口。然而,配置 Kubernetes 网络模型并不是一件容…...
shell
一、判断当前磁盘剩余空间是否有20G,如果小于20G,则将报警邮件发送给管理员,每天检查一次磁盘剩余空间。 二、判断web服务是否运行 三、使用curl命令访问第二题的web服务,看能否正常访问,如果能正常访问,…...
springboot+ssm+java校园二手物品交易系统vxkyj
样需要经过市场调研,需求分析,概要设计,详细设计,编码,测试这些步骤,基于Java语言、Jsp技术设计并实现了校园二手物品交易系统。系统主要包括个人中心、商家管理、用户管理、商品分类管理、商品信息管理、商…...
Android系统内置应用
Android系统内置应用 背景 客户提供APK,需要集成进系统,并且不可卸载 Android原生是怎么做的? 已Launcher3为例,apk是位于/system/priv-app/Launcher3目录下 AOSP系统内置app步骤 1.在package/apps/目录下创建相应的文件夹如&…...
CMMI实施需要准备什么:
1. 人力资源 实施中会涉及到EPG过程改进小组、QA、试点项目团队等人力资源: 1) 专职人员:1-2名 即在CMMI实施推广期内,基本上100%的时间投入。 2) 质量人员:1-更多名 组建质量管理部门,实施体系执行的监控&#x…...
【ARM AMBA AXI 入门 1 - AXI 握手协议】
文章目录 1.1 AXI 双向握手机制简介1.1.1 信号列表1.1.2 双向握手目的1.1.3 握手过程 1.2 数据通路的握手要求1.2.1 读数据通路1.2.2 读地址通路1.2.3 写数据通路1.2.4 写地址通路1.2.5 写回复通路1.2.6 全信号 1.3 不同数据通路间的约束关系1.3.1 读操作约束关系1.3.2 写操作约…...
详解uni-app应用生命周期函数
详解uni-app应用生命周期函数 详解uni-app应用生命周期函数 文章目录 详解uni-app应用生命周期函数前言一、应用生命周期函数二、页面生命周期函数总结 前言 UNI-APP学习系列之详解uni-app应用生命周期函数 一、应用生命周期函数 函数名说明onLaunch当uni-app 初始化完成时触…...
【WebFlux】List指定bean引用对象更新后同步到List
Java 8的流式API实现 如果你想在WebFlux中更新List中指定bean的引用对象并将其同步到List中,你可以使用Java 8的流式API来完成这个任务。 以下是一个例子: List<MyBean> myBeanList new ArrayList<>(); MyBean myBean1 new MyBean(); My…...
【JavaSE】Java基础语法(二十六):Collection集合
文章目录 1. 数组和集合的区别2. 集合类体系结构3. Collection 集合概述和使用【应用】4. Collection集合的遍历【应用】5. 增强for循环【应用】 1. 数组和集合的区别 相同点 都是容器,可以存储多个数据不同点 数组的长度是不可变的,集合的长度是可变的 数组可以存基本数据类型…...
jmeter做接口压力测试_jmeter接口性能测试
jmeter是apache公司基于java开发的一款开源压力测试工具,体积小,功能全,使用方便,是一个比较轻量级的测试工具,使用起来非常简单。因为jmeter是java开发的,所以运行的时候必须先要安装jdk才可以。jmeter是免…...
网络编程 lesson5 IO多路复用
select 当需要在一个或多个文件描述符上等待事件发生时,可以使用select函数。 select函数是一个阻塞调用,它会一直等待,直到指定的文件描述符上有事件发生或超时。 select函数详解 int select(int nfds, fd_set *readfds, fd_set *writefd…...
码出高效_第一章 | 有意思的二进制表示及运算
目录 0与1的世界1.如何理解32位机器能够同时处理处理32位电路信号?2.如何理解负数的加减法运算3.溢出在运算中如何理解4.计算机种常用的存储单位及转换5.位移运算规则6.有趣的 && 和 & 浮点数1.定点小数(为什么会出现浮点数表示?…...
测试类型(单元、集成、系统或手动测试)
测试类型(单元、集成、系统或手动测试) 单元测试 单元是系统的单个组件,例如类或单个方法。孤立地测试单元称为单元测试。 优点:速度快/易控/易写 缺点:缺乏现实性/无法捕获所有错误(例如与其他组件或服务的交互) 单元…...
【笔试强训编程题】Day3.(字符串中找出连续最长的数字串 69385)和(数组中出现次数超过一半的数字 23271)
作者简介:大家好,我是未央; 博客首页:未央.303 系列专栏:笔试强训编程题 每日一句:人的一生,可以有所作为的时机只有一次,那就是现在!!!! 文章目录…...
学懂缓存雪崩,缓存击穿,缓存穿透仅需一篇,基于Redis讲解
在了解缓存雪崩、击穿、穿透这三个问题前,我们需要知道为什么我们需要缓存。在了解这三个问题后,我们也必须知道使用Redis时,如何解决这些问题。 所以我将按照"为什么我们需要缓存"、"什么是缓存雪崩、击穿、穿透"、&qu…...
Android 12.0SystemUI 状态栏下拉和通知栏始终居中
1.概述 在12.0的产品定制化开发中,在系统原生的SystemUI 状态栏下拉和通知栏,默认是根据手势的x 坐标的位置居中显示,但是如果太靠两边感觉不太好,下拉太靠边不太好看所以产品提出不管手势在哪里下滑 都要去下拉和通知栏居中显示 会比较好看些 下面就来实现这个需求 2.Sy…...
面向过程编程和面向对象编程的区别
目录 一、面向过程编程 举个栗子: 二、面向对象编程 继续举个栗子: 三、区别 面向过程编程和面向对象编程是两种不同的编程范式,它们在代码的组织和结构上有所不同。 一、面向过程编程 面向过程编程(Procedural Programmin…...
2023年数学与人工智能国际会议——火热征稿中~
会议简介 Brief Introduction 2023年数学与人工智能国际会议(CFMAI 2023) 会议时间:2023年9月22 -24日 召开地点:中国杭州 大会官网:www.cfmai.org 2023年数学与人工智能国际会议(CFMAI 2023)由中山大学主办,CoreShare科享学术交流…...
格式化数字的实用命令:numfmt
在 Linux 系统中,numfmt 是一个用于格式化数字的实用工具。它可以将数字转换为不同的表示方式,如十进制、二进制、字节单位等。本文将详细介绍 numfmt 命令的使用方法,并提供一些适合初学者的示例。 Numfmt 命令语法 numfmt 命令的基本语法如…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
