RE: 从零开始的车载Android HMI(一) - Lottie

2023-05-16

1.前言

多年以前汽车还是以机械仪表主体的年代,各大汽车主机厂商并不十分关注操作系统UI的交互功能,但是随着车载SOC算力的不断提高以及主机厂商对汽车座舱竞争的白热化。座舱的HMI在设计上在强调功能性的同时也开始关注UI的艺术性,HMI的设计师们期望艺术与功能应该协同工作,让用户沉浸在“第三空间”的体验中。

有了需求程序员就需要关注如何实施和落地,然而Android应用本身虽然有着完整的动画框架支持,但是开发复杂、调试耗时,大型的gif或逐帧动画对于CPU&内存占用都不太理想,所以许多Android的手机应用基本上不怎么有动画。而且车载HMI上越来越多的开始引入各种光影、粒子效果,如果基于Android的原生控件来实现这些粒子效果,难度非常大,这就需要今天的主角Lottie来实现了。

2.Lottie概述

Lottie是一种基于JSON的动画文件格式,它使设计师能够在任何平台上发布动画,就像发布静态资产一样简单。它们是在任何设备上工作的小文件,可以在不进行像素化的情况下放大或缩小。

GitHub:https://github.com/airbnb/lottie-android

官方文档:http://airbnb.io/lottie/

Lottie在车载HMI中的优势

适量图形,不会出现失真

占用空间比序列帧动画小

可以修改属性,动态生成可交互的动画(使用视频动画难以实现交互功能)

节省HMI的开发、调试时间

可以更轻松的实现粒子、光影等特效

Lottie的使用方法

  1. 在build.gradle中添加依赖
dependencies {
  def lottieVersion = "5.2.0"
  implementation 'com.airbnb.android:lottie:$lottieVersion'
}
  1. 使用LottieAnimationView
    首先将lottie动画的json文件放在assets文件夹下

然后就可以在布局文件中使用LottieAnimationView了

<com.airbnb.lottie.LottieAnimationView
    android:id="@+id/dynamic_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:lottie_fileName="HamburgerArrow.json"
    app:lottie_autoPlay="true"
    app:lottie_loop="true"/>

然后运行APP就可以看到动画效果

3.Lottie的常用属性&API

LottieAnimationView继承自AppCompatImageView,所以ImageView支持的属性,LottieAnimationView都是支持的,这部分就不再介绍了。

  • lottie_fileName
    设定lottie动画所对应的json文件地址。json文件默认需要放置在assets下,设定时不需要再强调assets
app:lottie_fileName="HamburgerArrow.json"

如果设定 app:lottie_fileName=“other/HamburgerArrow.json”,那么lottie就会读取assets/other/HamburgerArrow.json。
void setAnimationFromJson(String jsonString, @Nullable String cacheKey)

  • lottie_rawRes
    设定lottie动画的json文件地址。json文件除了可以放置assets文件夹下,还可以放在raw文件夹下。使用时需要注意,利用lottie_rawRes引入资源时,json文件名前需要加上@raw,并且文件名不带.json后缀
app:lottie_rawRes="@raw/name"

  • lottie_autoPlay
    设定是否自动播放,取值为true | false

  • lottie_loop
    设定是否循环播放,取值为true | false

  • lottie_url
    当需要加载在线资源时,就可以使用lottie_url
    void setAnimationFromUrl(String url)
    void setAnimationFromUrl(String url, @Nullable String cacheKey)

  • lottie_fallbackRes
    设置一个drawable,如果lotticomposition由于任何原因未能加载,则将呈现该drawable。
    如果这是网络动画,可以使用它向用户显示错误,也可以添加一个失败的监听器重试下载。
    void setFallbackResource(@DrawableRes int fallbackResource)

  • lottie_repeatMode
    设定循环播放的顺序。取值为restart | reverse 。restart表示正常循环播放,reverse表示倒序播放
    void setRepeatMode(@LottieDrawable.RepeatMode int mode)
    int getRepeatMode()

  • lottie_repeatCount
    设定循环播放次数,取值为整数类型。
    void setRepeatCount(int count)
    int getRepeatCount()

  • lottie_imageAssetsFolder
    设定图片文件在assets文件夹下的访问路径。有的时候使用AE导出lottie的json时也会导出一些图片,这时候就需要该属性设定图片的地址。
    void setImageAssetsFolder(String imageAssetsFolder)
    String getImageAssetsFolder()

  • void setFrame(int frame)
    将进度设置为指定的帧。将进度设置为指定的帧。如果尚未设置合成,则进度将在设置时设置为帧。
    通过int getFrame()可以获取当前渲染的帧。

  • void setMaxFrame(int endFrame)
    设置播放或循环时动画将结束的最大帧。
    该值将被钳制到合成边界。例如,设置整数最大值将产生与合成相同的结果。
    通过float getMaxFrame()可以获取当前设定的最大帧

  • void setMinFrame(int startFrame)
    设置播放或循环时动画开始的最小帧。
    设定最大、最小帧可以只播放lottie动画中的一部分,例如下面的两张图,第一张是完整的从0播放到183帧,第二张则是从60播放到100帧。

  • lottie_progress
    设定动画初次显示时的进度,类型为float。取值范围0.0 ~ 1.0
    void setProgress(@FloatRange(from = 0f, to = 1f) float progress)
    float getProgress()

  • lottie_speed
    设定播放速度,取值类型为float。当速度<1时,动画会慢放,当速度<0时,可以实现倒序播放。
    void setSpeed(float speed)
    float getSpeed()
    void reverseAnimationSpeed():反转当前动画速度。这不会播放动画。

    速度是一个比较重要的属性,与progress、frame等属性一起灵活运用,我们就可以轻松地在HMI上实现炫酷而复杂的仪表盘效果,这对车载HMI尤为重要。

  • lottie_enableMergePathsForKitKatAndAbove
    设定是否开启MergePath属性,取值为true | false。默认为false
    void enableMergePathsForKitKatAndAbove(boolean enable)
    boolean isMergePathsEnabledForKitKatAndAbove()

  • void playAnimation()
    从头开始播放动画。如果速度<0,它将从终点开始,并向起点播放。必须在主线程中调用。

  • void cancelAnimation()
    取消动画,必须在主线程中调用。

  • void pauseAnimation()
    暂停动画,必须在主线程中调用。

  • void resumeAnimation()
    从当前位置继续播放动画。如果速度<0,它将从当前位置向后播放。必须在主线程中调用。

  • long getDuration()
    获取动画的播放时长。

  • void setTextDelegate(TextDelegate textDelegate)
    设置此选项可在运行时用自定义文本替换动画文本

  • lottie_cacheComposition
    设定是否开启缓存,取值 true | false,默认开启。开启缓存可以提升动画的加载效率。
    void setCacheComposition(boolean cacheComposition)

  • lottie_ignoreDisabledSystemAnimations
    允许忽略系统动画设置,因此即使禁用动画,也允许运行动画。取值 true | false,默认为false。
    void setIgnoreDisabledSystemAnimations(boolean ignore)

  • lottie_clipToCompositionBounds
    设置lottie是否应剪辑到原始动画合成边界。设置为true时,父视图可能需要禁用clipChildren,以便Lottie可以在LottieAnimationView边界之外进行渲染。默认为true。
    void setClipToCompositionBounds(boolean clipToCompositionBounds)

  • lottie_renderMode
    设定渲染模式,取值为 automatic | hardware | software。设定渲染模式为hardware时,可以显著提升动画的渲染效率,但是有些系统函数可能并不支持硬件加速,实际使用时需要结合调试时的效果选择是否开启。
    void setRenderMode(RenderMode renderMode)
    RenderMode getRenderMode()

  • void addAnimatorListener(Animator.AnimatorListener listener)
    添加动画的属性监听。
    对应也提供了removeUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener)用来移除指定的监听。或者也可以使用removeAllAnimatorListeners()移除所有监听。

binding.animationView.addAnimatorUpdateListener(object : ValueAnimator.AnimatorUpdateListener {
    override fun onAnimationUpdate(animation: ValueAnimator?) {

    }
})
  • void addAnimatorPauseListener(Animator.AnimatorPauseListener listener)
    添加动画暂停/恢复监听。
    对应也提供了removeAnimatorPauseListener(Animator.AnimatorPauseListener listener)用来移除指定的监听。
binding.animationView.addAnimatorPauseListener(object : Animator.AnimatorPauseListener{
    override fun onAnimationPause(animation: Animator?) {
        
    }

    override fun onAnimationResume(animation: Animator?) {
        
    }
    
})
  • void addAnimatorUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener)
    添加动画发生更新时的监听
    对应也提供了removeUpdateListener(ValueAnimator.AnimatorUpdateListener updateListener)用来移除指定的监听。或者也可以使用removeAllUpdateListeners()移除所有监听。
binding.animationView.addAnimatorUpdateListener(object : ValueAnimator.AnimatorUpdateListener{
    override fun onAnimationUpdate(animation: ValueAnimator?) {
        
    }
})
  • void addValueCallback(KeyPath keyPath, T property, LottieValueCallback callback)
    监听lottie动画json中某个片段的属性。
    keypath可以解析为多个内容,在这种情况下,回调的值将应用于所有回调。在内部会首先检查是否已使用resolveKeyPath(KeyPath)解析keypath,如果尚未解析,则将对其进行解析。

    Lottie动画的Json中属性都是英文简写,我们很难把json中key与实际的属性对应起来,所以有了第二个参数LottieProperty,它的内部定义了大量的属性,当我们需要修改json时,只需要传入LottieProperty中属性即可。
    例如,需要监听json中LeftArmWave的持续时间,就可以这么写
animationView.addValueCallback(KeyPath("LeftArmWave"), LottieProperty.TIME_REMAP) { frameInfo ->

} 

4.Lottie的常见用法

Lottie的Demo中内置了很多官方自己开发的动画效果,目的是为我们展示Lottie的常见用法,作为开发者我们必须掌握,并在适当的时候运用到我们的应用中。

动态属性效果

该效果展示了lottie支持动态修改json,让动画中的一小部分属性发生改变。

  1. 修改局部动画的速度
binding.animationView.addValueCallback(KeyPath("LeftArmWave"), LottieProperty.TIME_REMAP) { frameInfo ->
2 * speed.toFloat() * frameInfo.overallProgress
}

KeyPath中的LeftArmWave是Json中的一个属性

修改的效果如下。注意看右手的摆动频率X3后比X1高,以至于录制的GIF直接丢帧了。

  1. 修改局部动画的颜色
val shirt = KeyPath("Shirt", "Group 5", "Fill 1")
val leftArm = KeyPath("LeftArmWave", "LeftArm", "Group 6", "Fill 1")
val rightArm = KeyPath("RightArm", "Group 6", "Fill 1")

binding.animationView.addValueCallback(shirt, LottieProperty.COLOR) { COLORS[colorIndex] }
binding.animationView.addValueCallback(leftArm, LottieProperty.COLOR) { COLORS[colorIndex] }
binding.animationView.addValueCallback(rightArm, LottieProperty.COLOR) { COLORS[colorIndex] } 

修改后的效果如下

  1. 修改局部动画的运动范围
val point = PointF()
binding.animationView.addValueCallback(
    KeyPath("Body"),
    LottieProperty.TRANSFORM_POSITION
) { frameInfo ->
val startX = frameInfo.startValue.x
    var startY = frameInfo.startValue.y
    var endY = frameInfo.endValue.y

    if (startY > endY) {
        startY += EXTRA_JUMP[extraJumpIndex]
    } else if (endY > startY) {
        endY += EXTRA_JUMP[extraJumpIndex]
    }
    point.set(startX, lerp(startY, endY, frameInfo.interpolatedKeyframeProgress))
    point
} 

修改后的效果如下


动画文字效果


该效果展示了动画文字效果。这个效果实现起来其实不难,从程序中捕获输入的字母,再替换成lottie的资源文件即可。

val letter = "" + Character.toUpperCase(event.unicodeChar.toChar()) 
val fileName = "Mobilo/$letter.json"
LottieCompositionFactory.fromAsset(context, fileName)
    .addListener { addComposition(it) } 

动态文字效果


该效果展示动态替换动画中的文字。使用setTextDelegate就可以在动画运行中修改lottie动画中的文字

val textDelegate = TextDelegate(binding.dynamicTextView)
binding.nameEditText.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        textDelegate.setText("NAME", s.toString())
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
binding.dynamicTextView.setTextDelegate(textDelegate)

注意,这里其实用了两个lottieView,分别设定了不同的文字。

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:orientation="horizontal">

    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/originalTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="16dp"
        app:lottie_rawRes="@raw/name"
        app:lottie_autoPlay="true"
        app:lottie_loop="true"/>

    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/dynamicTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:lottie_rawRes="@raw/name"
        app:lottie_autoPlay="true"
        app:lottie_loop="true"/>
</LinearLayout>

手势交互效果


该效果展示了Lottie的手势交互。其实和第一个效果实现思路相同,都是通过addValueCallback修改json中的属性来实现的。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val largeValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f))
    binding.animationView.addValueCallback(KeyPath("First"), LottieProperty.TRANSFORM_POSITION, largeValueCallback)

    val mediumValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f))
    binding.animationView.addValueCallback(KeyPath("Fourth"), LottieProperty.TRANSFORM_POSITION, mediumValueCallback)

    val smallValueCallback = LottieRelativePointValueCallback(PointF(0f, 0f))
    binding.animationView.addValueCallback(KeyPath("Seventh"), LottieProperty.TRANSFORM_POSITION, smallValueCallback)

    var totalDx = 0f
    var totalDy = 0f

    val viewDragHelper = ViewDragHelper.create(binding.containerView, object : ViewDragHelper.Callback() {
        override fun tryCaptureView(child: View, pointerId: Int) = child == binding.targetView

        override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {
            return top
        }

        override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {
            return left
        }

        override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) {
            totalDx += dx
            totalDy += dy
            smallValueCallback.setValue(getPoint(totalDx, totalDy, 1.2f))
            mediumValueCallback.setValue(getPoint(totalDx, totalDy, 1f))
            largeValueCallback.setValue(getPoint(totalDx, totalDy, 0.75f))
        }
    })

    binding.containerView.viewDragHelper = viewDragHelper
}

在RecyclerView中使用


该效果展示通过监听点击事件来播放不同的lottie动画。这个效果最常见,APP中的点赞效果大多都是这样的实现思路。


5.总结

在车载HMI开发中往往我们会在实现、调试UI上花费大量的时间,如果能够灵活的运用Lottie,就可以显著节省程序的开发时间。例如,光影、粒子等特效虽然可以也考虑用Kanzi等3D引擎实现,但是3D引擎会消耗成倍的SOC性能,实际开发过程中,简单的特效使用Lottie实现,可以极大的优化应用的性能,给用户一个更优秀的体验。

当然这一切的前提是,UI设计师愿意为程序员切出一套Lottie的动画(F**K!)

本篇很多内容参考了《Android自定义控件高级进阶与精彩实例(博文视点出品)》(启舰)【摘要 书评 试读】- 京东图书 这本书的内容,写得相当不错,非常值得认真阅读。

下一篇来讲讲车载HMI开发时都会用到的一个系统组件 - Widget

参考资料

还不知道什么是汽车HMI设计?进来带你快速了解

《Android自定义控件高级进阶与精彩实例(博文视点出品)》(启舰)【摘要 书评 试读】- 京东图书

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RE: 从零开始的车载Android HMI(一) - Lottie 的相关文章

  • JKS、BKS 和 PKCS12 文件格式

    我正在设置一个无头服务器 该服务器使用用户提供的数据 JS CSS HTML 密钥库 为 Android 构建 Phonegap 混合应用程序 我想进行一些基本的客户端检查 以确保上传的密钥库有效 对于 JKS 文件 我发现可以通过确保提供
  • Android 的 GCM 推送通知[关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 最近 我尝试学习 GCM 并制作一个测试应用程序 该应用程序将通过 androids 官方网站获取 an
  • 无法打开本地终端

    我尝试在 Android Studio 中初始化我的终端 但它给了我错误 它说 无法打开本地终端 java io IOException 无法创建 PTY 我的电脑运行的是 Windows 10 如何解决这个问题 当您从 Github 存储
  • Android Material主题alpha颜色问题

    我已经创建了一个构建版本为 5 0 的应用程序 我在下面编写了主题
  • 要在进程中运行 dex,Gradle 守护进程需要更大的堆。目前有 910 MB

    实际上主要错误是 java exe完成非零退出值 1 首先我告诉你安装 studio 后遇到的每个问题 三天前 我刚刚安装了 android studio 并创建了新项目 1 首先它抛出错误 插件太旧 请更新到更新的版本 在谷歌上搜索后我改
  • 在 doInBackground 方法中启动活动

    在下面的代码中 我从互联网下载 json 并希望显示在列表中 如果列表为空 则转到另一个活动 但其他活动未启动 没有错误 但没有启动活动 感谢您的帮助 package ir mohammadi android nightly import
  • 如何从另一个应用程序向一个应用程序添加视图

    我的应用程序叫做我的好应用 MyNiceApp 主要只是一个加载视图的核心coreView在主活动中onCreate coreView由用户根据需要下载的其他插件的视图填充 我定义了核心视图上的各个区域 这些区域可以通过 MyNiceApp
  • 在android中以编程方式创建布局 - 问题

    我正在使用以下代码动态创建 FrameLayout mylayout java FrameLayout layout new FrameLayout this FrameLayout LayoutParams layoutparams ne
  • Android:每秒更新蓝牙 RSSI

    我试图每秒显示蓝牙信号强度 RSSI Timer 来自检测到的设备 但我无法调用onRecive 多次因为接收器生命周期 http developer android com reference android content Broadc
  • 首选项活动中的广告“没有足够的空间来显示广告!需要:<480, 75>,拥有:<432, 1073741823>”

    我试图在偏好活动中展示广告 但它从未出现 Logcat 始终显示消息 没有足够的空间来显示广告 想要 有 这就是我制作广告的方式 我对广告有自定义偏好 public class AdmobPreference extends Prefere
  • 创建用于 Android 库分发的 JAR

    我正在开发一个 android 库 并希望导出一个 JAR 文件 我可以分发该文件供其他人在他们的应用程序中使用 我不想分发源代码 因为它包含有关发布到我的网络服务器的详细信息 我尝试使用在 bin 目录中创建的 JAR 文件并将该 jar
  • SDK 管理器中缺少 Google Play 服务

    我想在我的应用程序中使用 Google 地图 我想在 SDK 管理器中安装 Google Play 服务 但是当我转到 SDK 管理器时 我没有看到 Google Play 服务 为什么 我该如何安装这个 我可以独立于 SDK Manage
  • Android Youtube API 可用吗? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有适用于 Android 的 YouTube API 吗 如果不是 除了通过网络浏览器之外 如何从 Yo
  • 上次更新arm64-v8a后,模拟器在M1 Mac上显示离线

    昨天模拟器运行得很好 系统镜像为arm64 v8a 我不太记得工作系统映像的版本名称 但是昨天我接受了 ARM 64 系统映像的更新 版本名称是 S 我可以从 AVD 管理器启动模拟器 当我按 运行应用程序 按钮时模拟器也会启动 但是 该应
  • 如何将 currentTimeMillis 转换为可读的日期格式? [复制]

    这个问题在这里已经有答案了 我想用currentTimeMillis两次 这样我就可以计算持续时间 但我也想以用户可读的格式显示时间和日期 我遇到了麻烦currentTimeMillis有利于计算 但我看不到内置函数可以转换为合适的时间或时
  • 当创建 Android Jetpack Compose AndroidView 的参数发生变化时,如何替换它?

    我有一个应用程序 显示封装在其中的几个不同视图AndroidView 在下面重现的简单示例中 这些只是TextView实例 问题是更改文本 在本例中循环显示三个不同的值 似乎不会更新应用程序显示的内容 sealed class AppVie
  • 日志记录在 Android 设备上实际上有什么作用?

    我一直在 Android 示例中看到这样的代码 try catch Exception e Log e Error e getMessage 什么是Log e实际上在物理设备上做什么 它进入系统日志 开发人员可以通过 SDK 工具访问该日志
  • 如何在Webview中保存用户名和密码

    目前 我还在学习Android开发的过程中 所以如果我的这个问题对你来说不太容易理解 请原谅 我创建了一个 Android 应用程序 它使用 RecyclerView 显示一组列表 当用户单击列表中的每个名称时 它会将它们重定向到一组不同的
  • Android 使用非公历

    我正在创建一个DatePickerDialogFragment用户将在其中选择出生日期 我想确保我可以处理非公历日期 我无法更改在我的设备上使用的日历类型 Android 是否允许用户切换日历类型 如果是的话 步骤是什么 到目前为止我还没有
  • 如何检测文本是否可读?

    我想知道是否有一种方法可以告诉给定的文本是人类可读的 我所说的人类可读的意思是 它有一些含义 格式就像某人写的文章 或者至少是由软件翻译器生成的供人类阅读的文章 这是背景故事 最近我正在制作一个应用程序 允许用户将短文本上传到数据库 在部署

随机推荐

  • 红包随机算法,给定一定的金额,一定的人数,保证每个人都能随机获得一定的金额。...

    前段时间做了一个笔试题 xff0c 觉得很有意思 xff0c 特此记录下来 题目如下 题目 请编写一个红包随机算法 需求为 xff1a 给定一定的金额 xff0c 一定的人数 xff0c 保证每个人都能随机获得一定的金额 比如100元的红包
  • linux下 ftp服务器如何设置上传文件的权限

    先用vi打开 vsftpd conf vsftpd的配置文件在Ubuntu下是vi etc vsftpd conf在centos 下是vi etc vsftpd vsftpd conf这个在不同的系统下可能不同原理一样 找到umask默认是
  • 敏捷之旅大连2013总结回顾

    12月21日 xff0c 敏捷之旅大连站如期召开 xff0c 这是今年我在大连组织的第九次程序员社区活动 xff0c 在此简单总结一下 这次活动考虑到参会人员会比平时多一些 xff0c 所以选择了中山区的比较大的会议室 xff0c 从十二点
  • 1062 Talent and Virtue

    About 900 years ago a Chinese philosopher Sima Guang wrote a history book in which he talked about people 39 s talent an
  • 演说(zhi)之法

    近年来 xff0c 参加了很多各种各样的技术会议 xff0c 在其中也听了很多高手和牛人们的演说 在总结了自己的一些经验之后 xff0c 也会在一些场合和大家分享 在以上的过程中 xff0c 越来越觉得 xff0c 想要为听众们奉献一场精彩
  • 窗体继承,然后实现按钮点击事件的重写

    做了一阵子Winform的程序之后 xff0c 越来越能够做到把窗体 控件等都看作类来对待了 以前做VB的时候 xff0c 对这些控件都是有一种敬畏的心理 xff0c 根本就不敢对其做什么 xff0c 而且当时也的确做不了什么 xff0c
  • 参加百度轻应用编程马拉松总结

    上个周末 xff0c 我到北京参加了百度举办的轻应用编程马拉松大赛 xff0c 感觉非常不错 xff0c 在此总结一下 这是我第一次参加编程马拉松的活动 xff0c 对此充满了好奇也充满了期望 xff0c 更是希望自己以后也能够组织类似的活
  • 前天奶奶来了 xff0c 把屋子里面的东西都收拾了一下 xff0c 尤其是佳佳的玩具 xff0c 有好多毛绒玩具 xff0c 都放在一个柜子的层里面了 早上佳佳醒来 xff0c 发现了新大陆 xff01 美羊羊都碰头了 xff01 维尼的碰
  • 超级简单的抽奖工具

    昨天快到中午的时候接到业务部门的一个需求 xff0c 要求对现有的抽奖软件进行改进 问题是 xff1a 现在的抽奖软件每次只能够抽出一个中奖号码 xff0c 而此次设置的各种奖项的中奖人数加起来有500人 xff0c 如果使用原有的软件 x
  • 程序员应知——把小事做好

    在从事软件开发的这些年中 xff0c 近期越来越多地听到这样的论点 xff1a 当前的程序员越来越浮躁 我的感觉也是如此 xff0c 由于在软件公司中 xff0c 人才流动特别快 xff0c 因此很多人的职位也变化的比较快 xff0c 很可
  • 程序员应知——学习、思考与分享

    有人说 xff0c 程序员是个苦差事 xff0c 一辈子总是要不停地学习 xff0c 学习新的技术 xff0c 学习新的架构 xff0c 学习新的工具 xff0c 一旦一段时间不学习 xff0c 就会发现其他人嘴里冒出来的新鲜词 xff0c
  • Evernote和有道云笔记的比较

    每个人可能都有随手记录一些事情的习惯 xff0c 可能是为了不忘记 xff0c 也可能是随时闪现在头脑中的一些想法 xff0c 因此就有了便利贴 xff0c 而在计算机或者说互联网的时代 xff0c 我们就有了更多选择 xff0c 可以随时
  • 软件开发中的哲学——世界的本原是物质(一)

    在这个系列博客的第一篇中 xff0c 首先要涉及到的哲学原理就是 世界的本原是物质 在IT领域 xff0c 有硬件和软件之分 xff0c 而二者之间的关系 xff0c 就和物质与精神类似 没有硬件的存在 xff0c 那么软件就没有能够发挥作
  • 在Prezi中输入简体中文的完美解决方案

    Prezi是一种在线制作演示文档 xff08 PPT xff09 的工具 xff0c 它与传统的Powerpoint或者Keynote的表现形式完全不同 xff0c 被称为 powerpoint的颠覆者 xff0c 在36Kr上曾经有过多篇
  • 1001 A+B Format

    Calculate a 43 b and output the sum in standard format that is the digits must be separated into groups of three by comm
  • 打印机打印列队中打印状态为错误的解决方式之一

    右键 我的电脑 xff08 win7以上为 计算机 xff09 xff0c 点击 管理 xff0c 展开 服务和应用程序 xff0c 点击 服务 找到右侧的 print spooler 项 xff0c 右键选择 停止 win 43 R打开运
  • Android版DailyInsist(五)——业务逻辑和数据操作SettingFragment & 小结

    最后一部分是提醒以及每天任务刷新 xff0c 两者都用到了 AlarmManager 这个系统管理类 提醒 提醒功能就是一个闹钟的效果 xff0c 只是这里是启动服务 xff0c 在服务里发一条notification作为提醒 设置时间时
  • IDEA项目的结构以及创建

    IDEA的项目结构应该怎么创建 一 在创建项目之前应该先知道IDEA项目的结构二 创建项目的步骤 一 在创建项目之前应该先知道IDEA项目的结构 idea项目的结构由三个部分组成 xff1a 分别是 项目 xff08 project xff
  • python插件安装--

    图像处理 安装opencv方式1 下载opencv xff0c 把cv2 pyd 放到 site packages pycharm ctrl 43 alt 43 s 找到 opencv python 直接安装 点击右下角的应用 Apply
  • RE: 从零开始的车载Android HMI(一) - Lottie

    1 前言 多年以前汽车还是以机械仪表主体的年代 xff0c 各大汽车主机厂商并不十分关注操作系统UI的交互功能 xff0c 但是随着车载SOC算力的不断提高以及主机厂商对汽车座舱竞争的白热化 座舱的HMI在设计上在强调功能性的同时也开始关注