MotionLayoutConstraintLayout的子类,所以它是一种布局类型,但是它能够为布局属性添加动画效果,是开发者实现动画效果的另一个新的选择。

MotionLayout基础

让动画跑起来

在入门练习的例子中,我们先利用MotionLayout实现一个View从左下(x25%,y75%)的位置移动到右上(x75%,y25%)的位置,如下图所示:

  • 添加依赖
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'

MotionLayoutconstraintlayout2.0以后才有的功能。

  • ConstraintLayout转为MotionLayout

打开布局文件进入Design模式,选中ConstraintLayout右击弹出选项列表,点击Convert to MotionLayout,这样就进入了MotionLayout Editor界面。

MotionLayout EditorAndroidStudio 4.0提供的功能。以前的版本是没有这个功能的。

ConstraintLayout转换成MotionLayout后,我们会发现在res -> xml 下面多了一个MotionScene文件,动画的相关配置都是储存在这个文件中,而静态的布局依然在layout布局文件中。

MotionScene文件的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"xmlns:motion="http://schemas.android.com/apk/res-auto"><Transitionmotion:constraintSetEnd="@+id/end"motion:constraintSetStart="@id/start"motion:duration="1000"><KeyFrameSet></KeyFrameSet></Transition><ConstraintSet android:id="@+id/start"></ConstraintSet><ConstraintSet android:id="@+id/end"></ConstraintSet>
</MotionScene>

我们目前只先看一看该文件的内容,主要包括Transition和两个ConstraintSet。至于他们分别代表的意义后面再做介绍。

  • 设置开始状态的位置

我们回到MotionLayout Editor,在右上部分可以看到两个可以点击的区域,一个名字是start,一个是end。 他们代表的是动画效果两个状态—开始状态结束状态

我们需要对imageButton进行动画,所以我们在start状态中勾选上imageButton

设置方法: 点击start -> 选择imageButton -> 点击ConstraintSet( start ) 后面的 编辑按钮 -> 选择 Create Constraint

  • 编辑结束状态的位置

接下来我们编辑结束状态的位置,让imageButton位于右上(x75%,y25%)的位置。

开始状态我们不需要进行修改就是左下(x25%,y75%)的位置,

这时候我们点击startend 这两个ConstraintSet就能看出View位置的差别了。

  • Click触发动画

MotionLayout动画可以点击OnClick触发,也可以滑动OnSwipe触发。

点击startend 这两个ConstraintSet之间的Transition连线,然后勾选OnClick

最后的效果如下:

  • 设置只能点击imageButton触发动画

上面设置的是点击触发动画,我们可以设置成只有点击imageButton触发动画。

设置方式:点击OnClick后面的**+** -> 选择targetId,属性值为idimageButton

MotionScene介绍

经过一系列操作后,我们实现了imageButton的位移动画。
这些是如何实现的呢?我们就回过来看看MotionScene文件的内容变化。

<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"xmlns:motion="http://schemas.android.com/apk/res-auto"><Transitionmotion:constraintSetEnd="@+id/end"motion:constraintSetStart="@id/start"motion:duration="1000"><KeyFrameSet></KeyFrameSet><OnClick motion:targetId="@id/imageButton" /></Transition><ConstraintSet android:id="@+id/start"><Constraintandroid:id="@+id/imageButton"motion:layout_constraintEnd_toStartOf="@+id/guideline"android:layout_width="wrap_content"android:layout_height="wrap_content"motion:layout_constraintBottom_toTopOf="@+id/guideline4"motion:layout_constraintTop_toTopOf="@+id/guideline4"motion:layout_constraintStart_toStartOf="@+id/guideline" /></ConstraintSet><ConstraintSet android:id="@+id/end"><Constraintmotion:layout_constraintVertical_bias="1.0"android:layout_height="wrap_content"motion:layout_constraintEnd_toStartOf="@id/guideline2"motion:layout_constraintStart_toStartOf="@id/guideline2"motion:layout_constraintTop_toTopOf="@id/guideline3"motion:layout_constraintBottom_toTopOf="@id/guideline3"motion:layout_constraintHorizontal_bias="0.0"android:layout_width="wrap_content"android:id="@+id/imageButton" /></ConstraintSet>
</MotionScene>

和我们刚才手动设置的对比,我们应该大概可以猜测出每个字段的含义了。

  1. Transition表示动画的过渡
  • constraintSetStart的值是开始的状态对应的id,表示从那个id对应的状态开始动画;
  • constraintSetEnd的值是结束的状态对应的id,表示过渡到那个id对应的状态;
  • duration表示的事动画的时长,单位是毫秒
  • 子标签OnClick 表示是点击触发。targetId表示点击对应的View触发。
  1. ConstraintSet表示约束数组,里面是一系列的View的约束值。代表的是动画的一个状态。
  2. KeyFrameSet表示的是关键帧的数组,设置的是动画中的某个关键帧的值,后面再做介绍。

OnClick设置

刚才我们点击imageButtonimageButtonstart状态和end状态来回切换,这是因为默认的motion:clickActiontoggle,即来回切换。它有5个值:

  • toggle:来回切换
  • jumpToStart:瞬间跳转到start状态,没有动画
  • jumpToEnd:瞬间跳转到end状态,没有动画
  • transitionToStart:动画过渡到start状态
  • transitionToEnd:动画过渡到end状态

接下来我们设置为transitionToEnd,这样点击的效果就是:如果当前是start状态,会过渡到end状态,如果当前是end状态,点击没有效果。

设置方法:点击OnClick后面的加号(+) -> 设置tools:clickAction的值为transitionToEnd

最后的效果如下:

弧形运动 Arc Motion

我们上面的运动轨迹是直线运动,我们也可以设置弧线运动。

设置方法为给start状态的imageButton添加motion:pathMotionArc属性。它有几个常用的值:

  • startHorizontal:运动的前部分时间水平方向会移动更多的比例

  • startVertical:运动的前部分时间竖直方向会移动更多的比例

  • none:直线运动

多个ConstraintLayout属性同时动画

ConstraintSet能修改ConstraintLayout属性这个是很显然的,包括位置大小等。
主要但不限于:

  • alpha
  • visibility
  • elevation
  • rotation
  • rotationX
  • rotationY
  • translationX
  • translationY
  • translationZ
  • scaleX
  • scaleY

这些属性是可以同时设置动画的,譬如我们想位置移动的时候还进行旋转270度,然后透明度逐渐变为0。

设置方法end状态的imageButton添加alpha属性和rotation属性。

效果如下所示:

自定义属性

其实除了上面的属性,还能对任何自定义的属性进行动画。这里的自定义属性就是除了上面的属性之外的属性。

如何找到能进行动画属性呢?其实很简单,找到对应Viewgetter/setter 方法,把get/set前缀去掉,把第一个字母小写就是对应的属性了。

譬如setBackgroundColor()方法,找到对应的属性就是 backgroundColor

设置方式:点击CustomAttributes后面的加号 -> 弹出的框中选择属性并输入属性值。

MotionLayout的一些开发属性

我们目前还没研究MotionLayout如何和MotionScene关联起来的,我们来看看MotionLayout文件。

MotionLayout提供了一些开发的属性,可以方便查看动画的一些属性。

<androidx.constraintlayout.motion.widget.MotionLayoutxmlns: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"tools:context=".Demo1Activity"app:layoutDescription="@xml/layout_activity_demo1_scene">...
</androidx.constraintlayout.motion.widget.MotionLayout>

我们可以看到layoutDescription指向了MotionScene。此外,MotionLayout还有其他一些属性,可以帮助开发中方便查看动画的信息。

  • app:showPaths:显示动画的轨迹
  • app:motionProgress:设置动画进度的某个关键点
  • app:motionDebug:显示动画的信息

如果我们设置app:motionDebug="SHOW_ALL"则我们可以看到一些辅助信息。

<androidx.constraintlayout.motion.widget.MotionLayoutxmlns: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"tools:context=".Demo1Activity"app:layoutDescription="@xml/layout_activity_demo1_scene"app:motionDebug="SHOW_ALL">...
</androidx.constraintlayout.motion.widget.MotionLayout>

图片变换

MotionLayout提供了可以对显示的图片进行动画切换的类ImageFilterViewImageFilterButton

设置方法

  1. ImageFilterView设置srcCompataltSrc, 他们分别代表开始显示的第一张图片和第二张图片。
<androidx.constraintlayout.utils.widget.ImageFilterViewandroid:id="@+id/image_filter_view"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.0"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:srcCompat="@drawable/xixi"app:altSrc="@drawable/duoduo"/>
  1. 显示第一张图片时自定义属性crossfade设置为0, 显示第一张图片时自定义属性crossfade设置为1.

效果如下:

当然ImageFilterView还提供了其他一些自定义属性可以进行操作:

  • warmth:
  • saturation
  • contrast
  • round
  • roundPercent

例如我们可以修改saturation,达到如下的效果:

关键帧 Keyframes

我把关键帧单独拎出来分析是因为我觉得它是动画的灵魂。为什么这么说呢?因为我们前面的内容只是定义了开始状态和结束状态,中间状态的修改就需要关键帧来实现了。

如果有印象的话应该还记得MotionScene文件中有见到过KeyFrameSet,没错它就是用来存放关键帧的数组。

接下来我们就来分别看下几种关键帧的使用。

我们创建关键帧是点击Transition后面的那个带加号的计时器按钮,点击后会弹出来供选择一种关键帧。

位置关键帧 Position Keyframes

位置关键帧是在某一帧的位置修改View的位置,想想应该知道这个关键帧类型也就只能修改位置相关的属性了。

改变位置就需要标定位置的坐标系,有三种坐标系:

  • parentRelative:x,y的坐标是相对父容器,值是从0.0-1.0

]

  • deltaRelative:x,y的坐标系是相对于起始点位置,起点的坐标为(0, 0),终点的位置为(1, 1)。x为水平方向,y为垂直方向。

  • pathRelative:起点和终点的连线方向是X轴,的坐标是(0, 0),终点的坐标是(1.0, 0),然后和X轴垂直的方向为Y轴。

]

以上三个图片来源的参阅地址

我们做个例子设置两个位置关键帧:

在0.25的时候位于父坐标的(0.75, 0.75)

在0.75的时候位于父坐标的(0.25, 0.25)

设置完两个关键帧后的预览效果如下:

哈哈,有没有感觉有问题,有点和基线没对齐哦~~ 目前我也不知道为什么,看有没有哪位大哥知道什么问题可以留言给我~~

这两个点关键镇的点用deltaRelative坐标系去定位能够定位准确位置。所以如果你感觉位置不准确可以用deltaRelative坐标系试试。

实际效果:

这些位置的变换都是匀速的,可以设置transitionEasing的值改变变化速率。

  • Standard easing

  • Accelerate easing

  • Decelerate easing

]

以上三个图片来源的参阅地址

属性关键帧 Attribute Keyframes

属性关键帧是改变属性值,它既可以位置相关的属性,也可以改变自定义属性。

我们做个例子在.5这个关键帧位置设置,scaleX1.5scaleY1.5alpha0.5,backgroundColor为**#FF00FF**

具体的设置方法和位置关键帧类似,这里不做过多介绍,只介绍下设置结果:

代码如下:

<KeyFrameSet><KeyAttributemotion:motionTarget="@+id/imageButton"motion:framePosition="50"android:alpha="0.5"android:scaleX="1.5"android:scaleY="1.5"><CustomAttributemotion:attributeName="backgroundColor"motion:customColorValue="#FF00FF" /></KeyAttribute><KeyPositionmotion:motionTarget="@+id/imageButton"motion:framePosition="25"motion:keyPositionType="parentRelative"motion:percentX="0.75"motion:percentY="0.75" /><KeyPositionmotion:motionTarget="@+id/imageButton"motion:framePosition="75"motion:keyPositionType="parentRelative"motion:percentX="0.25"motion:percentY="0.25" />
</KeyFrameSet>

关键触发关键帧 KeyTrigger Keyframes

这个关键帧是当动画时间轴到达某个时间点后触发调用View的某个方法。当然也会有方向的考虑。

我们做个例子,当正向动画时间轴超过90%以后imageButton替换图片;当反向动画时间轴超过90%以后imageButton恢复图片。

注意:时间轴的时间都是按照正向计算的,正向和反向只是动画的内容,和时间轴的进度计算没有关系。

实现这个功能需要自定义两个方法,所以我们来自定义ImageButton

  • 自定义ImageButton并添加两个方法
class MyImageButton @JvmOverloads constructor (context: Context, set: AttributeSet? = null, style: Int = 0) : AppCompatImageButton(context, set, style) {// 替换图片fun changeImage() {this.setImageDrawable(resources.getDrawable(R.drawable.duoduo))}// 恢复图片fun revertImage() {this.setImageDrawable(resources.getDrawable(R.drawable.emoji))}}

我们自定义了MyImageButton,并且实现了两个方法changeImagerevertImage

  • 将布局文件中的ImageButton替换成MyImageButton
<com.johnny.motionlayoutdemo.MyImageButtonandroid:id="@+id/imageButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/emoji"app:layout_constraintBottom_toTopOf="@+id/guideline4"app:layout_constraintEnd_toStartOf="@+id/guideline"app:layout_constraintStart_toStartOf="@+id/guideline"app:layout_constraintTop_toTopOf="@+id/guideline4" />
  • 添加KeyTrigger Keyframes

添加KeyTrigger Keyframes的方法和上面类似,这里不做过多介绍,只介绍下设置结果:

MotionScene文件中对应的代码如下:

<KeyFrameSet><KeyTriggermotion:motionTarget="@+id/imageButton"motion:framePosition="90"motion:onPositiveCross="changeImage"/><KeyTriggermotion:motionTarget="@+id/imageButton"motion:framePosition="90"motion:onNegativeCross="revertImage"/><KeyTriggermotion:motionTarget="@+id/imageButton"motion:framePosition="90"motion:onNegativeCross="show"/>...
</KeyFrameSet>

最后的效果如下所示:

代码启动动画和监听动画

我们可以通过ClickSwipe触发动画,也可以通过代码触发动画。

motionLayout.setTransitionDuration(3000)
motionLayout.transitionToState(R.id.end)

我们通过以上代码先修改动画时长,然后开始动画。

提示:motionLayout就是MotionLayout

我们有可以通过代码监听动画的进程

motionLayout.setTransitionListener(object: MotionLayout.TransitionListener {override fun onTransitionTrigger(motionLayout: MotionLayout?,triggerId: Int, positive: Boolean, progress: Float) {// Called when a trigger keyframe threshold is crossed}override fun onTransitionStarted(motionLayout: MotionLayout?,startId: Int, endId: Int) {// Called when the transition starts}override fun onTransitionChange(motionLayout: MotionLayout?,startId: Int, endId: Int, progress: Float) {// Called each time a property changes. Track progress value to find // current position}override fun onTransitionCompleted(motionLayout: MotionLayout?,currentId: Int) {// Called when the transition is complete}})

MotionLayout案例分析

案例1:点赞和取消点赞

问题分析:

  • 替换图片

(ImageFilterViewcrossfade组合实现)

  • 正向动画的时候有一些放大缩小的过度效果

加一些属性关键帧(Attribute Keyframes),在几个关键帧加入ScaleXScaleY属性变化值

    <Transitionmotion:constraintSetEnd="@+id/end"motion:constraintSetStart="@id/start"motion:duration="500"android:id="@+id/forward"><KeyFrameSet><KeyAttributemotion:motionTarget="@+id/praise_image_view"motion:framePosition="20"android:scaleX="0.3"android:scaleY="0.3"motion:transitionEasing="decelerate"><CustomAttributemotion:attributeName="crossfade"motion:customFloatValue="1" /></KeyAttribute><KeyAttributemotion:motionTarget="@+id/praise_image_view"motion:framePosition="80"android:scaleX="1.2"android:scaleY="1.2"/><KeyAttributemotion:motionTarget="@+id/praise_image_view"motion:framePosition="90"android:scaleX="0.9"android:scaleY="0.9"/></KeyFrameSet></Transition>
  • 反向动画的时候没有过渡动画,只是替换了图片

不能复用正向的过渡动画即使用(toogle),目前也不提供用代码将动画修改成jumpToEnd的方法,只能新建一个反向的Transition

  1. 新建Transition

  1. 修改代码
class PraiseActivity : AppCompatActivity() {private var praised = falseprivate var isAnimation = falseoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_praise)// 1. 点击触发动画praise_image_view.setOnClickListener {if (isAnimation) return@setOnClickListenerisAnimation = trueif (praised) {// 2. 设置动画的Transition然后开始动画motionLayout.setTransition(R.id.revert)motionLayout.transitionToEnd()} else {motionLayout.setTransition(R.id.forward)motionLayout.transitionToEnd()}}// 3. 监听动画的完成后记录状态值motionLayout.setTransitionListener(object : MotionLayout.TransitionListener{override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {}override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {}override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {praised = !praisedisAnimation = false}override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {}})}
}

案例2:循环旋转和文字切换

问题分析:

  • 文字切换

自定义TextView然后添加两个方法,在关键帧触发

class MyTextView@JvmOverloads constructor (context: Context, set: AttributeSet? = null, style: Int = 0) : AppCompatTextView(context, set, style) {// 文字变成加号fun changeTextToAdd() {this.text = "+"this.setTextSize(TypedValue.COMPLEX_UNIT_SP, 80.0F)}// 文字变成Mfun changeToM() {this.text = "M"this.setTextSize(TypedValue.COMPLEX_UNIT_SP, 50.0F)}}
           <KeyTriggermotion:motionTarget="@+id/circle_tv"motion:framePosition="35"motion:onCross="changeTextToAdd" /><KeyTriggermotion:motionTarget="@+id/circle_tv"motion:framePosition="85"motion:onCross="changeToM" />
  • 旋转

加一些属性关键帧

       <KeyFrameSet><KeyAttributemotion:motionTarget="@+id/circle_iv"motion:framePosition="20"android:rotation="0" /><KeyAttributemotion:motionTarget="@+id/circle_iv"motion:framePosition="50"android:rotation="360" /><KeyAttributemotion:motionTarget="@+id/circle_iv"motion:framePosition="70"android:rotation="360" /><KeyAttributemotion:motionTarget="@+id/circle_tv"motion:framePosition="20"android:scaleX="1.0"android:scaleY="1.0"/><KeyAttributemotion:motionTarget="@+id/circle_tv"motion:framePosition="35"android:scaleX="0.1"android:scaleY="0.1"/><KeyAttributemotion:motionTarget="@+id/circle_tv"motion:framePosition="50"android:scaleX="1.0"android:scaleY="1.0"/><KeyAttributemotion:motionTarget="@+id/circle_tv"motion:framePosition="70"android:scaleX="1.0"android:scaleY="1.0"/><KeyAttributemotion:motionTarget="@+id/circle_tv"motion:framePosition="85"android:scaleX="0.1"android:scaleY="0.1"/>
  • 不断的循环

监听动画完成后,让进度跳转调第一帧

class CycleCircleActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_cycler_circle)// 进入页面开始动画motionLayout.post {motionLayout.setTransition(R.id.start, R.id.end)motionLayout.transitionToEnd()}motionLayout.setTransitionListener(object : MotionLayout.TransitionListener {override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {p0?.isInteractionEnabled = false}override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {}override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {p0?.isInteractionEnabled = true// 跳转到第一帧p0?.progress = 0.0F// 开始动画p0?.transitionToEnd()}override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {}})}}

JetPack知识点实战系列十一:MotionLayout让动画如此简单相关推荐

  1. ElasticSearch实战系列十一: ElasticSearch错误问题解决方案

    前言 本文主要介绍ElasticSearch在使用过程中出现的各种问题解决思路和办法. ElasticSearch环境安装问题 1,max virtual memory areas vm.max_ma ...

  2. 云计算实战系列十一(软件包管理)

    软件包管理 知识点: Linux 软件包管理机制 Yum 使用官方源 Yum 管理软件包 Yum 缓存机制[扩展] Rpm 工具管理 RPM 包 源码包应用场景[定制] 源码包管理软件包 nginx ...

  3. 爬虫实战系列(十一):Win10下手机爬虫工具appium的安装与测试

    一.前言 之前介绍的都是关于网页爬虫的相关内容,今天博主想跟大家分享一个非常牛的手机爬虫工具Appium,首先我会介绍它的安装方法,然后给出一个Appium连接手机app例程. 二.详细安装过程 2. ...

  4. MP实战系列(十一)之封装方法详解(续一)

    之前写的封装方法详解,比较简要. 今天我主要讲增加和删除及其修改.查的话得单独再详讲. 增删改查,无论是Java或者C#等等,凡是对数据库操作的都离不开这四个. 一.增加方法讲解 MyBatis Pl ...

  5. xen虚拟化实战系列(六)之xen虚拟机破解密码

    xen虚拟化实战系列文章列表 xen虚拟化实战系列(一)之xen虚拟化环境安装 xen虚拟化实战系列(二)之xen虚拟机安装 xen虚拟化实战系列(三)之xen虚拟机复制 xen虚拟化实战系列(四)之 ...

  6. xen虚拟化实战系列(一)之xen虚拟化环境安装

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://koumm.blog.51cto.com/703525/1284516 xen目前 ...

  7. xen虚拟化实战系列(二)之xen虚拟机安装

    xen虚拟化实战系列文章列表 xen虚拟化实战系列(一)之xen虚拟化环境安装 xen虚拟化实战系列(二)之xen虚拟机安装 xen虚拟化实战系列(三)之xen虚拟机复制 xen虚拟化实战系列(四)之 ...

  8. 利用Spring扩展点模拟MyBatis的注解编程「知识点多多」「扩展点实战系列」- 第448篇

    历史文章(文章累计440+) <国内最全的Spring Boot系列之一> <国内最全的Spring Boot系列之二> <国内最全的Spring Boot系列之三> ...

  9. WF4.0实战系列索引

    从WF4.0 betal1出来的时候就开始使用WF4.0,由于资料不多,学习过程也非常艰苦.今年四月份的时候打算写WF4.0实战系列,由于今年是本命年故坚持写了24篇文章.这个系列的文章都有一个特点, ...

最新文章

  1. linux线程并不真正并行,Linux系统编程学习札记(十二)线程1
  2. Shrio 自定义算法登录认证
  3. 如何通过命令终端访问本地/局域网/远程的MySQL数据库_访问数据库_连接数据库_登录数据库
  4. 用随机整数填充缺失值_输入一个整数值并在C中用零填充进行打印
  5. 【Makefile由浅入深完全学习记录8】条件判断语句
  6. oracle 怎么创建类型,ORACLE—002:Create之创建类型
  7. datagridview选中获取行号_DataGridView控件显示行号的正确代码及分析
  8. 4安全框_压花玻璃与安全玻璃有哪些特点?玻璃隔断的介绍
  9. 体验VMware Converter Client 6.2与Veeam BR 10迁移ESXi 6.0 vm到vCenter 6.7 u3
  10. 基于Javaweb的机房预约管理系统
  11. 机器翻译评价指标BLEU介绍
  12. LVDS信号与TTL信号
  13. 安装软件提示需要重启电脑的处理方法
  14. android更改键盘布局,如何通过按Shift键更改android的键盘布局
  15. python写一个类方法_重写python脚本,在脚本的每个类中注入一个方法 - python
  16. 空洞骑士复活歌女玛丽莎的方法(复活其他灵魂NPC同理)
  17. MATLAB 判断字符串中是否含有特定的字符
  18. Intellij IDEA安装golang插件
  19. 多个时间序列之间的DTW
  20. mac 安装更新出现了 -2003F

热门文章

  1. 嵌入式(十三):嵌入式系统概念
  2. VUE React Angular
  3. ROS-2Dslam算法比较
  4. win7 win7 我的win7
  5. 企业引入人脸识别考勤 想要代打卡?没门!
  6. Ajax与Json的使用
  7. 2020身高体重标准表儿童_2020儿童标准身高表出炉,对照下!
  8. Win7蓝牙被禁用怎么办?
  9. Fiddler简介与功能
  10. Python爬虫—爬取网易云音乐【热歌榜】歌曲的精彩评论(写入txt文本文件或者MySQL数据库)