全网最详!暗黑模式在 Trip.com App 的实践

2023-10-30

作者简介

 

本文为联合撰稿,作者为携程国际业务研发部UED团队静静,公共研发团队祥星、旭仔、俊仔、增翼。


一、背景

在 2019 年,随着 iOS 13 与 Android Q 的推出,Apple 和 Google 同时推出主打功能暗黑模式,分别为 Dark Mode(iOS)/Dark Theme(Android) ,下文我们统称为 Dark Theme。在前期预研中,我们发现 66% 的 iOS 13 用户选择打开Dark Theme,可见用户对暗黑模式的喜爱和期待。

那么 Dark Theme 能带来哪些好处呢?

  • 更加省电,当代手机大部分都是OLED屏(OLED屏黑色下不发光更省电),配合Dark Theme 能耗更低;

  • 提供一致性的用户体验,当用户从Dark Theme的环境切换到我们的App,仍然能够享受黑色的宁静,避免亮眼的白色带来的刺激感;

  • 提升品牌形象,及时跟进系统新特性,在享受新特性带来美好之外还能获得Apple Store和Google Play推荐位机会,提升整体品牌形象;

  • 为弱视以及对强光敏感的用户提高可视性,让用户在暗环境中轻松使用App。

接下来,我们从视觉设计、实现方案和开发效率三个角度来介绍 Dark Theme 在 Trip.com App的实践。


二、视觉设计

暗黑模式是一套全新的设计风格,非简单的颜色明暗处理。我们将设计理念归结为三大要点,并介绍我们整体的设计思路。


2.1 三大要点

1)元素层级越高,表面颜色越浅

UI视觉层次致力于以一种用户能够快速理解的方式呈现产品内容,那么在 Dark Theme 下如何保证视觉层级依然有效呢?在 Light 模式中,我们使用带投影的白色卡片来模拟现实世界的空间深度感,而切换到 Dark 模式,则需要通过较浅的颜色表面来表示高度。层级越高,越接近于光源,表面的颜色就越浅。 

2)降低饱和度,提升可读性

设计 Dark Theme 时,尽量避免使用高饱和度的颜色,因为这些颜色会在深色背景上产生视觉抖动,导致人眼产生疲劳。以 Trip.com 的品牌蓝为例,若颜色不做调整,直接展示在深色背景上,不仅信息的清晰度降低了,而且识别的费力度还增高了。这显然不是我们所希望的,所以在 Dark Theme 下我们选择更低饱和的颜色来达到更好的可读性。 


3)增加对比度,提升可用性

依据 WCAG2.0 AA 设计标准,文本的视觉呈现以及文本图像至少要有4.5:1的对比度。深色表面选取白色文字达不到 AA 标准。 

2.2 设计方案

遵循上述设计要点,我们制定了 Trip.com 的颜色映射和插画设计方案。


2.2.1 颜色映射方案

为了规范化管理颜色库,保证产品、设计、开发的理解一致性,我们采用最直观的方式来命名颜色。这种方式既统一了 Light 和 Dark 的颜色命名,又降低了各方的沟通难度。具体的映射效果如下: 

UI中的彩色,统一进行了降饱和处理,这些彩色会应用于不同的场景,可能是背景,行动点,标签,或者是图标等等地方,那么当彩色用于背景时,为了确保文字和背景色有足够对比度,低饱和度的浅色背景就需要配合深色字一起使用。 


2.2.2 插画系统的设计

开启 Dark Theme,就像是我们把房间的窗帘拉上了,打开了一盏灯,不同层级高度的物体表面会受到不同的光照,表现出不同明暗的颜色。我们插画系统中的物体和人物沿用这种设计,在暗环境中,由于光线不够充足,人物的肤色会跟着变暗,衣服的颜色也会发生微妙的变化。比如白色、鲜亮的衣服,到了暗环境下,就会呈现灰色、低饱和度的暗色。

三、实现方案

Trip.com App 使用原生系统与 React Native 混合开发的模式。我们在各系统方案的基础上,结合 Trip.com 自身的特性,制定了一套iOS、Android和React Native三端的Dark Theme适配方案。


3.1 iOS

我们为 iOS 13 以上用户提供了两种主题模式的选择:

  • 自适应模式:跟随系统展示 Light/Dark 主题 

  • 强制 Light 模式:App 保持 Light 主题,不随系统主题变化


3.1.1 适配原理

iOS系统为 UIWindow、UIViewController、UIView 提供了overrideUserInterfaceStyle 属性来控制 Light/Dark 主题,所以我们只要控制 KeyWindow 的该属性,就可以控制整个 App 的主题。

3.1.2 适配方案


1)设置开关

App主题设置逻辑如图,KeyWindow 只有在App和系统都开启 Dark Theme 时,才会开启 Dark 主题。

跟随系统切换主题需要考虑到 App 运行时,系统主题被切换的情况:

  • 前往系统设置页手动切换 

  • 开启自动切换后,系统会自动更新主题 

这两种情况都需 App 进入后台,所以只需要添加 App 进入前台的监听,重复1的逻辑即可完成跟随系统变换主题的功能。


2)颜色适配

系统提供了 colorWithDynamicProvider 方法来适配 Light/Dark 模式下的颜色,我们依照视觉颜色映射方案封装颜色,覆盖绝大多数场景。部分无法通过动态色适配的场景,如 CGColor、RGB 颜色,可以通过 resolvedColorWithTraitCollection 方法解析出当前上下文所需要的颜色进行使用。


3)图片适配

系统早在 iOS12 就为 UITraitCollection 增加了 userInterface 属性,我们只要向 ImageAssets 注册 Light/Dark 下两种主题的图片,而后 UIImageView 根据 traitCollectionDidChange 变化自动获取 Light/Dark 图片。

App 内的静态图片资源可以通过 Images.xcassets 直接配置,通过网络下发或代码动态生成的图片可以通过 registerImage:withTraitCollection: 的方式进行动态注册。


4)注意事项

动态色或 ImageAssets 的原理都是根据容器的 userInterface 取得对应的内容,视图上的动态颜色或 ImageAssets 将根据视图的 userInterface 取值,App 内直接进行颜色计算或者图片处理的将会根据 UITraitCollection.currentColletion 进行取值。

设置 Window 的主题来完成 App 主题适配的工作,会存在 App 主题与系统主题不同步的情况,例如系统主题为 Dark,App 主题为 Light。此时直接对动态颜色或 ImageAssets 进行操作会取得错误的结果。所以对于这种场景,都不使用动态色或 ImageAssets,仅在发生主题切换时机进行视图刷新操作。


3.2 Android

我们不仅在 Android Q 上实现 Dark Theme,在 Android Q 以下的版本也适配了 Dark Theme。在 Android Q 上,用户可以选择跟随系统来展示 Dark Theme 或者强制关闭 Dark 保持 Light 主题。

在 Android Q 以下,我们也支持了 Dark Theme,用户可以选择强制打开或者强制关闭 Dark Theme。


3.2.1 适配原理

Android App 启动时会根据系统的配置加载不同的资源,以加载图片为例,高分辨率系统加载三倍图,低分辨率系统加载二倍图。同样地,系统也会根据 Dark Theme 的打开或者关闭来加载 Dark 或者 Light 资源。

我们会往 App 的 value 和 value-night 文件目录下放置 UED 提供的 Light 和 Dark 两套资源。当 App 打开 Dark Theme,系统选择从 value-night 目录加载资源,展示 Dark 界面;当 App 关闭 Dark Theme,系统选择从 value 目录加载资源,展示 Light 界面。


3.2.2 适配方案

我们通过开关设置、颜色适配、图片适配和其他注意事项四小节来介绍Android的Dark Theme适配方案。

1)开关设置

从上述代码可以看出,只有使用 AppCompat 的代码才具有 Dark Theme 特性,例如继承 AppCompatAcivity 和 AppCompatDialog 才支持 Dark Theme,而普通的 Activity 和 Dialog 不会展示 Dark Theme,同样地 Application 也不支持。

// 打开darkmode 
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODENIGHTYES);


// 关闭darkmode 
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODENIGHTNO);


// darkmode跟随系统 
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODENIGHTFOLLOW_SYSTEM);
2)颜色适配

在 value 和 value-night 目录下定义 Light 和 Dark 相同名字的颜色,如下图: 

在 XML 或者代码中使用

//xml 
android:textColor="@color/colorbrandingblue"


//Java kotlin 
ContextCompat.getColor(activity, R.color.colorbrandingblue)

 

注意:Activity 必须是 AppCompatActivity 实例,不能是 ApplicationContext/Activity。另外由于带透明度的颜色必须一个一个在 XML 声明,为了减轻开发工作量,我们提供了一个脚本可以快速生成 Light 和 Dark 下的透明度颜色。


3)图片适配

图片适配工作分资源图片适配和自定义 drawable 适配:

  • drawable/mipmap:在 drawable-xxhdpi 和 drawable-night-xxhdpi 目录下放置Light和Dark相同名字的图片,系统根据Light/Dark加载图片。

  • IconFont/自定义Shape/自定义Selector/SVG:因为绘制使用颜色,所以用法同颜色。


4)注意事项
  • 在非 AppCompatActivity 内展示 Dark Theme ,利用下面的代码可在非 AppCompatActivity 内展示 Dark 颜色。

public class IBUDarkModeDelegate {


    public static void applyNight(Context activity) {
          Activity conreteActivity = null;
          if (activity instanceof Activity) {
              conreteActivity = (Activity) activity;
          } else if (activity instanceof ThemedReactContext) {
              conreteActivity = (Activity) ((ThemedReactContext) activity).getBaseContext();
          }
          if (conreteActivity != null) {
              AppCompatDelegate appCompatDelegate = AppCompatDelegate.create(conreteActivity, new AppCompatCallback() {
              public ActionMode onWindowStartingSupportActionMode(ActionMode.Callback callback) {
                  return null;
              }
          });
          appCompatDelegate.applyDayNight();
      }
  }
}


// Activity创建前调用即可 
protected void onCreate(Bundle savedInstanceState) { 
    IBUDarkModeDelegate.applyNight(this); 
    super.onCreate(savedInstanceState); 
}
  • 颜色名必须全App唯一。

  • 切换手机系统的Dark Theme,会导致Activity重建,业务线按需做好状态保存恢复。

  • 做好全机型测试,防止个别机型出现异常展示问题。


3.3 ReactNative


3.3.1 适配方案

RN 桥接 Native 端,通过直接获取和动态监听两种方式获取 Native 端的主题变化。


1)从 Native 端获取当前的 theme 值

使用 Native Modules 的同步方法在 JS 端获取当前 theme 值,JS 端方法调用能直接得到 Native 同步方法的返回值,而非一个 Promise。

同步方法于 2017 年 1 月和 10 月先后被引入 ReactNative 的 Android 端和 iOS 端, 但直到现在,仍然没有被写入文档:

  • iOS: 使用 RCTEXPORTSYNCHRONOUSTYPEDMETHOD() 替换 RCTEXPORTMETHOD()(v0.51.0 及以上版本支持Commit)

  • Android: 在 @ReactMethod annotation 后面添加 (isBlockingSynchronousMethod = true) (v0.42.0 及以上版本支持Commit)

同步方法的缺点是无法在 Debug Remotely 时调用,所以必须在 Debug Remotely 时,提供默认值。我们接入 dark theme 时,选择了 dark 作为默认值。


2)theme 值变化监听

我们使用RN事件监听Theme变化。


3)RN业务方调用 theme

我们提供 IBUThemeContext & IBUThemeProvider 两个类供产线获取主题。 Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。IBUThemeContext 是 Context 在 Theme 上的一个应用, IBUThemeProvider 负责同步 Theme 值,并将其传递给 IBUThemeContext.Provider。

// IBUThemeContext
export const IBUThemeContext = React.createContext<'light' | 'dark'>('light');
//IBUThemeProvider
export class IBUThemeProvider extends Component<IBUThemeProviderProps, IBUThemeProviderState> {
  // 引入文件时同步获取一次 theme
  static theme: 'light' | 'dark' = isInChromeDebugMode ? 'dark' : IBUTheme.getTheme();
  constructor(props: IBUThemeProviderProps) {
    super(props);
      // 实例创建时, 再次同步一次theme
      const theme = isInChromeDebugMode ? 'dark' : IBUTheme.getTheme();
      IBUThemeProvider.theme = theme;
      this.state = {
        theme,
      };
 }
  render(): JSX.Element {
    const { theme } = this.state;
    const { children } = this.props;
    return <IBUThemeContext.Provider value={theme}>{children}</IBUThemeContext.Provider>;
  }
}

将IBUThemeProvider 嵌入App 的根节点, 组件树便能通过如下两种方法,获取theme值: 

通过IBUThemeProvider.theme 读取全局theme。声明了static contextType=IBUThemeContext 的类中使用 this.context,获取theme值。

4)颜色适配

我们提供下列方法供产线使用颜色,方法支持透明度的设置:

export declare class IBUColor{
  static red(theme?: 'light' | 'dark', alpha?: number): string;
  static green(theme?: 'light' | 'dark', alpha?: number): string;
  static blue(theme?: 'light' | 'dark', alpha?: number): string;
}

所有方法均接受 theme 和 alpha 两个可选参数, 方法会先根据 theme 选择对应颜色的 hex 字符串色值,如果 theme 值为空, 则 fallback 到 IBUThemeProvider.theme , 之后再根据 alpha 值计算颜色的的 alpha hex 值,并拼接到 hex 字符串色值之后。如 alpha 为空,则不拼接 hex 色值。最后将对应的 hex 色值字符串返回。


5)图片适配

我们使用 lazy getters 解决 Light/Dark 图片展示的问题。方式如下:

RN端图片之前已经作了统一的静态资源管理:

export const images = {
  button: require('./images/button.png'),
  logo: require('./images/logo.png'),
}

使用 lazy getters,稍作改造后,即能完美适配:

export const images = {
  get button() {
    const theme = IBUThemeProvider.theme;
    return theme === 'dark' ? require('./images/button_dark.png') : require('./images/button.png');
  },
  get logo() {
    const theme = IBUThemeProvider.theme;
    return theme === 'dark' ? require('./images/logo_dark.png') : require('./images/logo.png');
  }
}

6)DynamicStyle

ReactNative 导出的 StyleSheet 只会在文件引入时,初始化一次,不会随着 App DarkTheme 的变化而变化这就导致系统主题发生变化时,RN 无法更新 styles,导致 RN 页面与 Native 不一致的问题。为此我们提出 DynamicStyleSheet 来解决该问题。

type IBUNamedStyles<T> = { [P in keyof T]: ViewStyle | TextStyle | ImageStyle };
export function IBUDynamicStyleSheet<T extends IBUNamedStyles<T> | IBUNamedStyles<any>>(
  callback: () => T | IBUNamedStyles<T>
): (theme?: 'light' | 'dark') => T {
  const cache: { light?: T; dark?: T } = {
    light: undefined,
    dark: undefined,
  };
  return (theme?: 'light' | 'dark'): T => {
    const currentTheme = theme || IBUThemeProvider.theme;
    let style = cache[currentTheme];
    if (!style) {
      style = StyleSheet.create(callback());
      cache[currentTheme] = style;
    }
    return style;
  };
}

IBUDynamicStyleSheet 是一个 Function,它接受一个返回值是 style 的 Function 作为参数,并且返回一个 Function。这种 Function 也被称High Order Function

StyleSheet 创建 style 的代码被包在参数的 Function 中,这样可以保证每次取值都会取到当前的 theme 对应的 style。每次 render 前, 将返回的 Function 执行一次,并将这个 Function 的返回值作为真正的 style 使用。

IBUDynamicStyleSheet 内部对 light 和 dark 下的 style 作了缓存,这样大部分情况下 style 仍然只会被创建一次, theme 发生变化时 style 被创建两次, theme 发生多次变化时,style 最多只被创建两次。

采用DynamicStyleSheet这种方式,代码改动量不仅小, 而且性能损失少, 达到实时切换Theme的目的。


7)Examples

App 开启dark theme

export default class App extends Component{
  render(){
    return (
      <IBUThemeProvider>
        // ...
      </IBUThemeProvider>
    )
  }
}

Class Component 接入

class MyClass extends React.Component {
  //需要声明contextType, 否则该组件可能不会随theme变化而重新绘制
  static contextType = IBUThemeContext;


  constructor(props, context) {
    super(props, context)
    // context can be accessed now, https://github.com/facebook/react/issues/6598
    const theme = this.context;
    // ....
  }
  // ...
  render() {
    const theme = this.context; // 'light'|'dark'
    /* render something based on the value of IBUThemeContext */
    const styles = dynamicStyles(theme);
    return(
      <View style={{ backgroundColor: IBUColor.orange(theme), flex: 1 }}>
        <View style={styles.icon}/>
        {/* render something else */}
      </View>
    )
  }
}
const dynamicStyles = IBUDynamicStyleSheet(() => ({
  icon: {
    backgroundColor: IBUColor.quaternaryGray(),
    height: 20,
  },
}));

Functional Component接入

export const MyComponent = () => {
  const theme = React.useContext(IBUThemeContext);  // 'light'|'dark'
  const styles = dynamicStyles(theme);
  return (
    <View style={{ backgroundColor: IBUColor.orange(theme), flex: 1 }}>
        <View style={styles.icon}/>
        {/* render something else */}
    </View>
  )
}
const dynamicStyles = IBUDynamicStyleSheet(() => ({
  icon: {
    backgroundColor: IBUColor.quaternaryGray(),
    height: 20,
  },
}));

注意:Component必须声明contextType, 否则不能在theme发生变化时触发render重绘。


四、工具&效率

在建立颜色规范到方案落地的过程中,我们发现新的颜色命名虽然容易理解,由于对使用的名字命名,开发在使用时需要对照视觉稿查找对应的颜色命名,造成开发效率上的浪费。

例如视觉稿上显示 #287DFA,开发根据色值查找此颜色的映射名称 brandingBlue,再将颜色设置成 brandingBlue。

为了解决此问题,我们扩展了 Sketch Measure 插件,颜色一栏不再展示颜色的色值,取而代之的是颜色的命名。这样开发能依照视觉稿直接获取颜色名,大大减少工作量。

插件效果如下 :

至此完美解决了开发适配 Dark Theme 的效率问题。


五、结语

Dark Theme适配是一项涉及多职能部门合作的项目。在规范的设计指导、完善的落地方案和便捷的效率工具加持下,我们的适配成本和资源大大降低。在各端仅投入一位研发人员的情况下,在两周内完成了从方案制定到方案落地,并推进产线接入。

Trip.com一直致力于追随前沿新特性,带给用户最好的体验,让用户更舒适,旅行从此简单。

参考资料

1)Apple Dark Mode介绍:

https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/dark-mode/ 

2)Implementing Dark Mode on iOS - WWDC2019:

https://developer.apple.com/videos/play/wwdc2019/214/ 

3)Android Dark Theme 介绍:

https://developer.android.com/guide/topics/ui/look-and-feel/darktheme

4)React Native 参考:

https://github.com/react-native-community/discussions-and-proposals/pull/11#discussion_r210370835 https://github.com/facebook/reactnative/commit/63fa3f21c5ab308def450bffb22054241a8842ef#diff-55c2992d993407398c62bf19f803088f 

https://github.com/Lxxyx/react-native-dynamic-stylesheet https://developer.mozilla.org/enUS/docs/Web/JavaScript/Reference/Functions/get 

https://medium.com/fantageek/how-to-structure-your-project-and-manage-static-resources-in-react-native-6f4cfc947d92 

https://willowtreeapps.com/ideas/react-native-tips-and-tricks-2-0-managing-static-assets-with-absolute-paths 

5)WCAG21视觉标准:

https://www.w3.org/TR/WCAG21/#contrast-enhanced

团队招聘信息

携程国际业务基础研发部,负责移动应用 Trip.com 的开发和维护。感兴趣的小伙伴欢迎投递简历到yu.yinga@trip.com,邮件标题:【携程国际业务基础研发部】+【姓名】

【推荐阅读】

 “携程技术”公众号

  分享,交流,成长

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

全网最详!暗黑模式在 Trip.com App 的实践 的相关文章

  • 云计算虚拟化技术未来发展的趋势及应用(来源: 比特网)

    近两年 随着一些国际知名的虚拟化软件厂商在中国市场的大力开拓 虚拟化及云计算的概念越来越热 虚拟化软件市场大幅升温 同时也带动了一批国内的虚拟化软件企业迅速的发展起来 虚拟化技术从早期的企业应用 逐步过渡到公有云应用 应用范围越来越广泛 那
  • 刷题day22:逆波兰表达式求值

    题意描述 给你一个字符串数组 tokens 表示一个根据 逆波兰表示法 表示的算术表达式 请你计算该表达式 返回一个表示表达式值的整数 类似于二叉树 可以将运算符号看做二叉树的中间节点 遇到运算符之后计算前两个数字与该运算符的运算结果 C
  • 电阻的精度和温漂

    转载自 http blog sina com cn s blog 68b345970100jc2h html 电阻的精度和温漂 1 电阻温度系数 TCR 表示电阻当温度改变1度时 电阻值的相对变化 当温度每升高1 时 导体电阻的增加值与原来
  • python 字符串去除 等等转义空格换行字符

    python去除html特殊转义字符 下面这些是一些我在网上找的一些 好像都不管用 网络上面的一些方法好像都不管用 比如 str text replace str text replace u xa0 等等一些 我使用的时候好像都不管用 下
  • 匿名内部类方式实现线程创建的三种方式

    小demo package demo04 author wh date 2021年08月30日15 57 匿名内部类方式实现线程创建的三种方式 public class demo02 public static void main Stri
  • 企业级springboot项目架构模板V4.0,开箱即用

    此次 4 0 更新点 1 添加线程池的使用 2 Springboot版本更新2 7 0 解决部分冲突的依赖 3 所有表结构去除 ID 字段 不在使用ID作为主键 主键直接使用算法生成 4 解决过滤器 引起Druid监控无法访问的问题 5 F
  • 3.4 流式操作

    1 简介 1 概念 Stream是数据渠道 用于操作数据源所生成的元素序列 它可以实现对集合的复杂操作 例如过滤 排序和映射等 集合是一种静态的数据结构 存储在内存中 而Stream是通过CPU来实现计算的 并不会存储数据 Stream不会
  • linux 出错 “INFO: task xxxxxx: 634 blocked for more than 120 seconds.”的3种解决方案

    1 问题描述 最近搭建的一个linux最小系统在运行到241秒时在控制台自动打印如下图信息 并且以后每隔120秒打印一次 仔细阅读打印信息发现关键信息是 hung task timeout secs 第一次遇到这样的问题 首先百度 通过翻看
  • 在 Java 中,读取 resources 下的文件方式(四种)

    1 测试文件具体位置 2 方式一 项目内路径 使用项目内路径读取 该路径只在开发工具中显示 所以只能在开发工具中使用 项目部署之后无法读取 不通用 SpringBootTest public class ResourcesFIleTest
  • 爱快路由器设置上网

    进入以太网络设置里面查看详细信息 也可cmdipconfig 找出网管 打开电脑的浏览器 地址栏输入 10 100 10 1 在登录界面爱快路由器帐号和密码 默认帐号 admin 默认密码 admin 点击登录 忘记密码可长按路由器重置键
  • rust入门材料清单

    rust book rustbook 配套视频教程 cargo 文档 crate io 标准库api 官方资料索引 rust 编程规范
  • js实现Math.sqrt()方法

    之前面试的时候 面试有一道题 要记算10的平方根 并且精确到0 01 我也是想了一会才想到了一种简单粗暴的方法 也算是完成了 squareRoot num gt let s 1 d 0 1 x 0 01 while s s
  • MyEclipse 8.0 中vssplugin的安装

    今天试用了myeclipse8 0 多了很多新特性 挺不错的 由于本司用的是vss库 所以vssplugin是必装项 可是安装的时候发布目录结构有所变动与myeclipse6 0有所区别 具体安装详见如下步骤 1 下载vss plugin
  • Python画爱心——谁能拒绝用代码敲出来会跳动的爱心呢~

    还不快把这份浪漫拿走 节日就快到来了 给Ta一个惊喜吧 今天给大家分享一个浪漫小技巧 利用Python中的 HTML 制作一个立体会动的心动小爱心 成千上百个爱心汇成一个大爱心 从里到外形成一个立体状 给人视觉上的冲击感 浪漫极了 V hw
  • div页面垂直居中方法方法

    基本思路 1 首们需要position absolute 绝对定位 而层的定位点 使用外补丁margin负值的方法 负值的大小为层自身宽度高度除以二 例子 一个层宽度是400 高度是300 使用绝对定位距离上部与左部都设置成50 而marg
  • 带你详细了解ADSS光缆

    ADSS 全介质自承式光缆 光缆是一种自支撑架空光缆 专为架空安装和部署而设计 适用于各种户外应用 本文将提供ADSS光缆的一些知识 什么是ADSS光缆 ADSS 光缆是一种光缆 其强度足以在结构之间支撑自身 而无需使用导电金属元件 该光缆
  • 创建qml自定义视频源(Qt5.15.2+设备帧)

    前言 若是看过笔者之前的文档 应该会注意到 笔者显示自定义视频 使用的是QQuickImageProvider和Image类型实现 本次笔者记录另一种显示方式 创建视频源 然后使用VideoOutput显示 因为Qt5和Qt6多媒体模块发生
  • QML自定义模块及qmldir的使用

    前言 在开发QtQuick项目中 当项目文件很多的情况下 可能会分成多级文件夹来进行分类 还有一些通用类型文件 如公共组件 通用配置等等 需要在各个不同的文件中进行调用 这种情况下 一种方式是在当前需要引用的文件中通过关键字import 加
  • TCP-三次握手

    文章目录 三次握手 简单示意图 详细分析 一些思考 为什么是三次握手 SYN 攻击 什么是SYN 攻击 如何防止SYN 攻击 数据包丢失了该怎么办 初始序列号为什么随机产生 为什么 SYN 段不携带数据却要消耗一个序列号呢 每次握手可以确定
  • 数据库驱动和数据库连接(MySQL)

    数据库驱动和数据库连接 MySQL 一 数据库驱动 什么是数据库驱动 二 数据库连接 jdbc连接的方式以及区别 1 通过DriverManager 驱动管理类 的静态方法获取 2 通过DataSource 数据源 对象获取 3 两种创建连

随机推荐

  • SpringBoot常用注解和原理简介说明

    转自 SpringBoot常用注解和原理简介说明 下文笔者讲述SpringBoot常见注解的简介说明 如下所示 启动注解 SpringBootApplication Target ElementType TYPE Retention Ret
  • C++——QT之信号与槽

    通俗的理解信号与槽 信号与槽 Signal Slot 是 Qt 编程的基础 也是 Qt 的一大创新 因为有了信号与槽的编程机制 在 Qt 中处理界面各个组件的交互操作时变得更加直观和简单 信号 Signal 就是在特定情况下被发射的事件 例
  • 设计模式:快照模式

    快照模式也叫做备忘录模式 但是我觉得如果是了解快照的话 我觉得比备忘录更形象一点 毕竟现在快照有各种 有页面快照 有系统快照等 相当于是一个备份 备忘录其实也是备份的意思 我觉得快照更贴切一点现在的描述 快照模式是一种行为模式 行为模式可以
  • Python利用Opencv读取图片

    首先 要先确认PyCharm里面有没有装opencv python 如果没有 点右侧 号搜索添加 确认有opencv之后 便可以使用cv2读取图片了 import cv2 image path C Desktop image jpg 图片路
  • 数字化时代-23:中国人工智能企业100强排行榜

    综合实力TOP100 成长能力TOP100 创新能力TOP100
  • React组件卸载、路由跳转、页面关闭(刷新)之前进行提示

    React组件卸载生命周期 路由跳转和页面关闭三者看起来有些类似的地方 比如都是当前组件即将从视口消失 但实际上所触发的事件均不相同 以一个实际案例出发 某单页应用的 文章编辑页用户正在编辑文章 此时尚未保存 当用户不小心要跳转到另外一个路
  • SpringBoot——内置数据源

    简单介绍 在之前我们介绍SpringBoot的数据层解决方案的时候 曾说到过在数据层是由数据源 持久化技术和数据库组成的 之前我们一直使用的都是Druid MyBatis MySQL组合的解决方案 这三种方案在之前我们都介绍过如何整合以及基
  • 进阶:变量 1.系统变量 说明:变量由系统定义,不是用户定义,属于服务器层面。 注意:(1) 全局变量需要添加global关键字 (2) 会话变量需要添加session键字 (3)

    进阶 变量 1 系统变量 说明 变量由系统定义 不是用户定义 属于服务器层面 注意 1 全局变量需要添加global关键字 2 会话变量需要添加session关键字 3 如果不写 默认session会话级别 使用步骤 1 查看所有系统变量
  • 大话西游2人数最多服务器,大话2大话各个服务器狂人榜排名 看大话如今服务器...

    大话2大话各个服务器狂人榜排名 看大话如今服务器 给大家带来各个服务器的练功排行等等 以下图均为服务器练功排行榜目前前三名 有些错误之处还望指正 冠希会及时修改 大话精灵有时候也问不到了 52 根据以下数据分析得出 1 目前超过3年以上没合
  • DM@数理逻辑@命题和联结词@形式化命题

    文章目录 abstract 命题和联结词基本概念 命题 陈述句 命题真值 真假记号 原子命题 命题分解 复合命题 联结词命题 例 半形式化命题和形式化语言 形式逻辑的抽象性 联结词形式化 否定 合取 析取 相容或的表示 排斥或的表示 蕴含
  • GPIO八种工作模式

    GPIO8种工作模式 一 输出模式 1 推挽输出 Out PP 推挽输出模式下 单片机输出高电平直接接到VDD 3 3V 输出低电平直接接到VSS 0V 具有强的驱动能力 2 开漏输出 Out OD 开漏输出模式下 单片机输出低电平也是接到
  • yolov5在plotting labels时停止并显示Process finished with exit code 1

    最近在训练yolov5模型 但在执行train py时 执行到plotting labels停止 显示错误 解决办法 报错信息里traceback里第二行 把这个注释掉就可以进入训练了
  • 用好GPT关键诀窍之上下文学习

    大家好 我是herosunly 985院校硕士毕业 现担任算法研究员一职 热衷于机器学习算法研究与应用 曾获得阿里云天池比赛第一名 CCF比赛第二名 科大讯飞比赛第三名 拥有多项发明专利 对机器学习和深度学习拥有自己独到的见解 曾经辅导过若
  • Spring AOP无法调用自身方法的原因

    this是当前对象 调用当前对象因为不是从IOC托管对象中 获取所以自然也是不能被AOP捕获 所有AOP的必须用代理对象执行 在同一个类中使用 Transaction Async并不能实现事务和异步 道理就是这样的 新建一个类 Compon
  • 深圳大学第三期“飞鹰计划”正式开班

    金秋九月 丹桂飘香 在这个充满着收获的季节里 迎来了期待已久的深圳大学机电与控制工程学院飞鹰计划2022级第三期开班典礼 受疫情影响 虽然典礼只能在线上举行 但是丝毫不影响电巢专家及学生们的热情 9月17日下午 百余位同学通过线上参加了此次
  • uCOS-II信号量OSSemCreate(0)和OSSemCreate(1)详解

    在ucos II中 为了实现任务之间的同步 用到的同步机制有 信号量 邮箱和消息队列 其中这里我主要说下对信号量的使用经验 信号量在创建时 调用OSSemCreate INT16U cnt 函数 cnt为信号量的初始值 对cnt赋予不同的值
  • Mac OS无法进入系统/数据备份/重装系统方法步骤

    之前我的Mac Mini不知为什么突然间不能进入到系统了 开机的时候基本都时进度条走到2 3的时候就会自动死机 打电话给客户 客服是不错 TM殊不知 聊了N就之后 发现这是要话费的 NM足足磨了1个多小时也没能解决我的问题 好吧 苹果系统时
  • Service Pack 6 for Visual Basic 6.0, Visual C++ 6.0 with Visual Source Safe 6.0d下载

    下载VC6 0的SP6补丁 网址http www microsoft com en us download details aspx id 9183 补充 英文版 http download microsoft com download 1
  • Java设计模式——适配器模式

    文章目录 介绍适配器模式 类适配器模式 对象的适配器模式 接口的适配器模式 介绍适配器模式 适配器主要用于接口的转换或者将接口不兼容的类对象组合在一起形成对外统一接口 是一种结构性模式 其本质是是一个中间件 适用于类及其对象 原理 通过继承
  • 全网最详!暗黑模式在 Trip.com App 的实践

    作者简介 本文为联合撰稿 作者为携程国际业务研发部UED团队静静 公共研发团队祥星 旭仔 俊仔 增翼 一 背景 在 2019 年 随着 iOS 13 与 Android Q 的推出 Apple 和 Google 同时推出主打功能暗黑模式 分