是时候拥抱ViewBinding了~

2023-05-16

是时候拥抱ViewBinding了!!

    • 一、前言
    • 二、初识ViewBinding
    • 三、拥抱ViewBinding
      • 3.1、环境要求
      • 3.2、开启ViewBinding功能
      • 3.3、Activity中ViewBinding的使用
        • 3.3.1、布局中直接的控件
        • 3.3.2、布局中使用include
        • 3.3.2、布局中使用include和merge
      • 3.4、Fragment中使用ViewBinding
      • 3.5、自定义Dialog中使用ViewBinding
      • 3.6、自定义View中使用ViewBinding
        • 3.6.1 使用的layout文件不包含merge
        • 3.6.2 使用的layout文件根标签为merge
      • 3.7、Adapter中使用ViewBinding
    • 四、关于封装
    • 五、总结

沉舟侧畔千帆过,
病树前头万木春。
– 唐·刘禹锡

一、前言

随着Android Studio 3.6的正式发布,我义无反顾的走在了更新尝鲜的前列。AS的升级一如往常的顺利,重启后就进入了令人血脉喷张的 Gradle 升级的环节,需要从3.5.1升级到3.6.0。果不其然,出问题了!!

ButterKnife居然报错,日志如下:

D:\xxx\libbase\component\dialog\BottomDialog.java:33:     : Attempt to use @BindView for an already bound ID 0 on 'mTvNegative'. (com.xxx.libbase.component.dialog.BottomDialog.mLayoutContent)
    ViewGroup mLayoutContent;

我真是摸不着头脑啊。解决吧,升级ButterKnife、翻资料、找issue、看源码等等等等。最终老天不负有心人,我将Gradle版本回退了,一切都回归平静。【如果有解决办法的请告知我,感激不尽】

二、初识ViewBinding

它和ButterKnife一样都是为了省去findViewById()这样的重复代码。其实在2019谷歌开发者峰会上对ViewBinding就已经有所耳闻了,layout中更新控件ID后立刻可以在Activity中引用到,这绝对比ButterKnife需要编译、需要区分R和R2要舒服的多。
上面升级到3.6.0就是为了使用它,然而现实永远这么的残酷,十之八九不尽人意,ViewBinding和ButterKnife看来只能二选一了。

三、拥抱ViewBinding

关于ViewBinding的文档,官方写的很详细,请看 视图绑定 。本文一切从简,主要说下Google官方没有提到的一些问题。

3.1、环境要求

  • Android Studio版本3.6及以上
  • Gradle 插件版本3.6.0及以上

3.2、开启ViewBinding功能

ViewBinding支持按模块启用,在模块的build.gradle文件中添加如下代码:

android {
        ...
        viewBinding {
            enabled = true
        }
}    

3.3、Activity中ViewBinding的使用

//之前设置视图的方法
setContentView(R.layout.activity_main);

//使用ViewBinding后的方法
mBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());

可以看到,当你使用了ViewBinding后,针对你的activity_main.xml文件,会自动帮你生成一个ActivityMainBinding.java文件(该文件在build/generated/data_binding_base_class_source_out/xxx…目录下),也就是布局文件的驼峰命名法加上一个Binding后缀,然后在Activity中直接使用就可以。

3.3.1、布局中直接的控件

当我们在布局中添加一个id为 tv_text 的TextView后,直接在Activity中使用mBinding.tvText即可拿到该控件。如下所示,可以看到也是以控件ID的驼峰命名法来获取的:

mBinding.tvText.setText("是你得不到的ViewBinding");

3.3.2、布局中使用include

例如我们有个layout_comment.xml的布局,布局中有id为tv_include的TextView,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_include"
        android:text="这就是测试啊"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

然后在activity_main.xml文件中include该布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns: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=".MainActivity">

    <TextView
        android:id="@+id/tv_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <include
        android:id="@+id/layout_include"
        layout="@layout/layout_comment" />

</androidx.constraintlayout.widget.ConstraintLayout>

那么此时我们如何使用到layout_comment.xml布局中的TextView控件呢,首先include标签需要声明id,例如layout_include,然后Activity中代码如下:

mBinding.layoutInclude.tvInclude.setText("这就是你的不对了");

是不是很神奇,是不是很简单。

注意:
当你给layout_comment.xml的根布局再添加id(比如添加了layout_xxx的ID)的时候,此时会报错:

java.lang.NullPointerException: Missing required view with ID: layout_xxx

3.3.2、布局中使用include和merge

我们将上文的layout_comment.xml稍作修改,根布局使用merge标签,其他不做修改:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_include"
        android:text="这就是测试啊"
        android:gravity="end"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</merge>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns: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=".MainActivity">

        <include
            android:id="@+id/layout_include"
            layout="@layout/layout_comment" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_main.xml文件中使用include添加该布局后,在java代码中依旧是可以正常使用以下代码的:

mBinding.layoutInclude.tvInclude.setText("会不会出现问题呀");

但是但是!!!运行就会报错:

java.lang.NullPointerException: Missing required view with ID: layoutInclude

要是把include标签的id去掉的话,这时mBinding中也是找不到tvInclude这个控件呀,怎么办??
之前是不是说过,每个layout文件都会对应一个Binding文件,那么layout_comment.xml,肯定也有一个LayoutCommentBinding.java文件,我们去看下这个文件的源代码,里面有个可疑的方法,bind()方法:

 @NonNull
  public static LayoutCommentBinding bind(@NonNull View rootView) {
    // The body of this method is generated in a way you would not otherwise write.
    // This is done to optimize the compiled bytecode for size and performance.
    String missingId;
    missingId: {
      TextView tvInclude = rootView.findViewById(R.id.tv_include);
      if (tvInclude == null) {
        missingId = "tvInclude";
        break missingId;
      }
      return new LayoutCommentBinding(rootView, tvInclude);
    }
    throw new NullPointerException("Missing required view with ID: ".concat(missingId));
  }

所以对于含有merge标签的布局我们可以使用bind()方法来绑定到根布局上,在这里,根布局就是mBinding.getRoot()了。所以代码如下:

        //这么写不可以
        //mBinding.layoutInclude.tvInclude.setText("会不会出现问题呀");

        LayoutCommentBinding commentBinding = LayoutCommentBinding.bind(mBinding.getRoot());
        commentBinding.tvInclude.setText("这就不会出现问题了吧");

同时需要注意: include标签不可以有id

3.4、Fragment中使用ViewBinding

在Fragment的**onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)**方法中:

//原来的写法
return inflater.inflate(R.layout.fragment_blank, container, false);

//使用ViewBinding的写法
mBinding = FragmentBlankBinding.inflate(inflater);
return mBinding.getRoot();

拿到FragmentBlankBinding的对象后,更新数据的都和之前一样了。

请注意官方的提示( 务必在onDestroyView() 方法中清除对绑定类实例的所有引用),感谢 @lingHui_1314 的反馈:

注意:Fragment 的存在时间比其视图长。请务必在 Fragment 的 onDestroyView() 方法中清除对绑定类实例的所有引用。

3.5、自定义Dialog中使用ViewBinding

dialog中使用和Activity以及Fragment一样,直接使用单参数的inflate()方法即可,伪代码如下:

public class MyDialog extends Dialog {

    protected View mView;
    protected DialogBottomBinding mBinding;
    
    public MyDialog(@NonNull Context context, @StyleRes int themeResId) {
        super(context, themeResId);

        //原来的写法
		mView = View.inflate(getContext(), getLayoutId(), null);

		//使用ViewBinding的写法
        mBinding = DialogBottomBinding.inflate(getLayoutInflater());
        mView = mBinding.getRoot();
        
        setContentView(mView);
    }
}

3.6、自定义View中使用ViewBinding

我在重构工程的时候发现了自定义视图中其实有很多问题,这里把这两种常见的方法总结下:

3.6.1 使用的layout文件不包含merge

这里直接贴出来代码吧,就是自定义了一个LinearLayout然后往其中添加了一个布局,该布局是view_my_layout.xml文件,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="这是自定义布局"
        android:textSize="50sp" />

</androidx.constraintlayout.widget.ConstraintLayout>

会生成一个对应的ViewMyLayoutBinding.java文件,看下文MyLinearLayout 代码:
init1、2、3、4是使用inflate来导入layout布局的写法,全部可以正常显示自定义的布局。
init10、11、12是使用ViewBinding的写法,10无法正常显示视图,11和12是两种不同的写法,道理一样。


public class MyLinearLayout extends LinearLayout {
    public MyLinearLayout(Context context) {
        this(context, null);
    }

    public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

//        init1();
//        init2();
//        init3();
        init4();
    }

    private void init1() {
        inflate(getContext(), R.layout.view_my_layout, this);
    }

    private void init2() {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.view_my_layout, this);
    }

    //和init2()方法相等
    private void init3() {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.view_my_layout, this, true);
    }

    private void init4() {
        View view = LayoutInflater.from(getContext()).inflate(R.layout.view_my_layout, this, false);
        addView(view);
    }

    //视图异常,布局无法填充满
    private void init10() {
        ViewMyLayoutBinding binding = ViewMyLayoutBinding.inflate(LayoutInflater.from(getContext()));
        addView(binding.getRoot());
    }

    private void init11() {
        ViewMyLayoutBinding binding = ViewMyLayoutBinding.inflate(LayoutInflater.from(getContext()), this, true);
    }

    private void init12() {
        ViewMyLayoutBinding binding = ViewMyLayoutBinding.inflate(LayoutInflater.from(getContext()), this, false);
        addView(binding.getRoot());
    }

}

3.6.2 使用的layout文件根标签为merge

我们添加一个view_my_layout_merge.xml文件,根标签为merge:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="这是自定义merge"
        android:textSize="50sp" />

</merge>

此时在MyLinearLayout.java中使用的话,正确写法是init20()方法:


    private void init20() {
        ViewMyLayoutMergeBinding binding = ViewMyLayoutMergeBinding.inflate(LayoutInflater.from(getContext()), this);
    }

    //没有效果,可以理解为还没有rootView
    private void init21() {
        ViewMyLayoutMergeBinding binding = ViewMyLayoutMergeBinding.bind(this);
    }

我们对比下使用merge标签和不使用merge标签所对应的Binding文件:
使用merge标签生成的代码大致如下,inflate()方法最终调用了bind()方法:

  @NonNull
  public static ViewMyLayoutMergeBinding inflate(@NonNull LayoutInflater inflater,
      @NonNull ViewGroup parent) {
    if (parent == null) {
      throw new NullPointerException("parent");
    }
    inflater.inflate(R.layout.view_my_layout_merge, parent);
    return bind(parent);
  }

  @NonNull
  public static ViewMyLayoutMergeBinding bind(@NonNull View rootView) {
    if (rootView == null) {
      throw new NullPointerException("rootView");
    }
    return new ViewMyLayoutMergeBinding(rootView);
  }

不使用merge标签的Binding代码如下,inflate(@NonNull LayoutInflater inflater) 调用了 inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent) 方法,最终调用了**bind(@NonNull View rootView)**方法:

  @NonNull
  public static ViewMyLayoutBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, null, false);
  }

  @NonNull
  public static ViewMyLayoutBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup parent, boolean attachToParent) {
    View root = inflater.inflate(R.layout.view_my_layout, parent, false);
    if (attachToParent) {
      parent.addView(root);
    }
    return bind(root);
  }

  @NonNull
  public static ViewMyLayoutBinding bind(@NonNull View rootView) {
    if (rootView == null) {
      throw new NullPointerException("rootView");
    }
    return new ViewMyLayoutBinding((ConstraintLayout) rootView);
  }

这里基本就把所有的自定义视图中使用ViewBinding的方法总结了一下,主要是inflate方法的使用,其实就是帮我们封装了下inflate方法,如果不知道使用哪个方法的话可以查看生成的ViewBinding源代码,一眼就能明了我们之前的写法对应的是现在的哪个方法了。
如果还不熟悉的话,请翻阅其他inflate的相关资料,相信你会有很大收货。当然了当你熟悉inflate方法之后,下面的文章其实可以没必要看了。

3.7、Adapter中使用ViewBinding

在RecyclerView结合Adapter的例子中我们再使用ViewBinding来尝试下,直接贴Adapter的代码:

public class MainAdapter extends RecyclerView.Adapter<MainAdapter.ViewHolder> {

    private List<String> mList;

    public MainAdapter(List<String> list) {
        mList = list;
    }

    @NonNull
    @Override
    public MainAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        //之前的写法
        //View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_comment, parent, false);
        //ViewHolder holder = new ViewHolder(view);

        //使用ViewBinding的写法
        LayoutCommentBinding commentBinding = LayoutCommentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
        ViewHolder holder = new ViewHolder(commentBinding);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull MainAdapter.ViewHolder holder, int position) {
        holder.mTextView.setText(mList.get(position));
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {

        TextView mTextView;

        //之前的写法
        //public ViewHolder(@NonNull View itemView) {
        //    super(itemView);
        //    mTextView = itemView.findViewById(R.id.tv_include);
        //}

        //使用ViewBinding的写法
        ViewHolder(@NonNull LayoutCommentBinding commentBinding) {
            super(commentBinding.getRoot());
            mTextView = commentBinding.tvInclude;
        }

    }
}

只需要注意两方面:

  • ViewHolder的构造器参数改为使用的Binding对象
  • 实例化ViewHolder的时候传入相应的Binding对象

四、关于封装

大概了解了ViewBinding后,我们可以考虑将其完全封装在BaseActivity(BaseFragment、BaseDialog、BaseView等)等底层的公共类中,省去手动实例化相应ViewBinding类的这一过程。
首先可以使用泛型类,每个具体的Activity继承BaseActivity,并传递进去对应的ViewBinding,然后反射对应的inflate()方法获取到ViewBinding实例。拿到实例后就可以对控件为所欲为了不是!!

五、总结

使用ViewBinding的话,其实很简单,新建xxx.xml布局后就会产生一个对应的 xxxBinding.java的文件,实例化xxxBinding只需要调用它自身的inflate()方法即可。
注意不同情况下使用不同的inflate()方法,以及使用了merge标签情况下的bind()方法,以及使用merge标签布局和其他正常xxxLayout布局所产生的不同的inflate()方法。

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

是时候拥抱ViewBinding了~ 的相关文章

  • ViewBinding和ButterKnife

    一 ViewBinding和ButterKnife比较 xff1a 传送门 1 ButterKnife一个好处就是不用写findViewById xff0c 另一个好处就是不用写setOnClickListener之类的 xff0c 满屏幕
  • Viewbinding自动生成XML的一个对应绑定类

    当你在项目 Module 的build gradle中的android 中设置 buildFeatures viewBinding true 设置完sync一下 xff0c 然后会在项目中看到对应的XML文件的一个继承了ViewBindin
  • Android ViewBinding 替换 findViewById 的神器

    ViewBinding中文官网 ViewBinding 的出现就是为了替代 findViewById 的 以前我们写完布局后就要在代码中使用 findViewById 方法找到 xml 文件中对应的 view xff0c 这样耗时费力 xf
  • kotlin-android-extensions过时了,迁移到ViewBinding

    前言 回顾历史 xff0c kotlin android extensions插件让我们省去了很多findViewById的代码 xff0c 直接使用控件id操作控件 不过在Android Studio 4 1及以上IDE新建项目的时候 x
  • ViewBinding - Jetpack 视图绑定委托封装及使用示例

    通过视图绑定功能 xff0c 您可以更轻松地编写可与视图交互的代码 在模块中启用视图绑定之后 xff0c 系统会为该模块中的每个 XML 布局文件生成一个绑定类 绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用 在大多数情况下
  • Android框架 使用ViewBinding和ButterKnife

    ButterKnife 10 2 3 Github 网站 xff1a https github com JakeWharton butterknife 进入 github 网站就可以看到 xff1a Attention This tool
  • ViewBinding的用法

    1 启用Viewbinding功能 xff1a 在模块build gradle文件android节点下添加如下代码 android span class token punctuation span span class token pun
  • Android开发:Fragment中优雅使用ViewBinding【Java】

    目录 前言 官网示例 封装 前言 ViewBinding可以帮助我们减少代码中的大部分findViewById xff0c 官网中提到了它的优点和缺点 xff1a Null 安全 xff1a 由于视图绑定会创建对视图的直接引用 xff0c
  • Jetpack之ViewBinding

    ViewBinding 的作用 xff1a 代替findViewById xff0c 还可以保证空安全和类型安全 基本原理 会为每一个布局xml文件生成一个视图绑定类 xff0c 类名称是布局文件名转化为UpperCamelCase 43
  • Android——ViewBinding总结

    ViewBinding 一旦启动了ViewBinding功能之后 xff0c Android Studio会自动为我们所编写的每一个布局文件都生成一个对应的Binding类 启用ViewBinding需要在app build gradle中
  • 是时候拥抱ViewBinding了~

    是时候拥抱ViewBinding了 xff01 xff01 一 前言二 初识ViewBinding三 拥抱ViewBinding3 1 环境要求3 2 开启ViewBinding功能3 3 Activity中ViewBinding的使用3
  • ViewBinding使用入门

    ViewBinding 参考资料 新技术 ViewBinding 最佳实践 amp 原理击穿 更多 ViewBinding 的封装思路 1 kotlin android extensions KAE 的问题 根据Google官方的说法 KA
  • ViewBinding 初探-在Activity和Adapter中使用

    升级android studio后 xff08 主要是gradle升级后 xff09 xff0c butterknife 不再被支持 xff1b 现在google主推ViewBinding 一下就是我用ViewBinding 做的一个小de
  • android ViewBinding

    一 kotlin android extensions 在使用ViewBinding之前 xff0c 我们一直使用的是kotlin android extensions xff0c 使用kotlin android extensions可以
  • Android ViewBinding的使用详解

    一 什么是view binding 视图绑定会替代 findViewById 通过视图绑定功能 xff0c 您可以更轻松地编写可与视图交互的代码 在模块中启用视图绑定之后 xff0c 系统会为该模块中的每个 XML 布局文件生成一个绑定类
  • Android Studio 3.6新功能ViewBinding解析

    Android Studio使用技巧 看这一篇就够了 Google官方在2020年3月份发布了Android Studio3 6的版本 xff0c 此版本增加了很多新的功能 xff0c 其中一大亮点功能就是ViewBinding ViewB
  • 初探ViewBinding

    视图访问的方式有常用的findViewById xff0c ButterKnife等多种方式 xff0c 这些方式的各方面对比如下 如上图所示 xff0c 在简明 编译安全和编译速度上都各有优势 xff0c 那么有没有一种方式可以一石 34
  • Jetpack系列:ViewBinding的使用及原理

    ViewBinding是什么 ViewBinding是Android Studio3 6推出的新特性 xff0c 旨在替代findViewById来查找视图 xff0c 为开发者减少编写重复的模板代码 xff0c 提高代码的安全性 原理 在
  • 【Android Jetpack系列】一、ViewBinding的使用

    关于本系列的说明 作为学习Jetpack的系列文章 可能会更新得很慢 本系列文或者应该称之为学习笔记 观看本文的同学 应该已经有具备开发简单Android App的能力了 若是零基础 那么阅读本文可能有些难懂 我只能尽量简单解释 本文所用开
  • 【Android】ViewBinding+DataBinding+MVVM新手快速上手

    为什么写这篇博客 网上大部分博客 代码量都比较大 把实际的业务都代入进去了 这篇博客的目的 就是为了讲解基本原理和使用思路 然后给出一个最简单的Demo 这里不讲解具体用法 那样篇幅会太长 直接看Demo代码就行 什么是ViewBindin

随机推荐

  • Mac Folx

    Folx Mac Folx free 参考 folx
  • macOS SFTP 传输文件

    FileZilla 参考 Download FileZilla Client for macOS
  • v1.21.0-rc3 lotus chain prune hot-moving

    lotus span class token operator span version lotus version span class token number 1 21 span span class token number 0 s
  • iPhone卡死 强制重启iPhone 14

    粘贴无法取消 xff0c iPhone卡死 xff0c 无法操作 iPhone 强制重启 无法滑动来关机 按一下 音量键 43 键 xff08 不需要长按 xff09 按一下 音量键 键 xff08 不需要长按 xff09 长按 电源键 大
  • Mainnet endpoint Filecoin Lotus api

    infura ENDPOINTS https span class token punctuation span span class token operator span span class token operator span 2
  • sed 将匹配的整行替换

    sed 将匹配的整行替换 sed 39 span class token operator span pattern span class token operator span c replacement line 39 file pat
  • Spring的Java配置方式

    Spring的发展 Spring1 x 时代 在Spring1 x时代 xff0c 都是通过xml文件配置bean xff0c 随着项目的不断扩大 xff0c 需要将xml配置分放到不同的配置文件中 xff0c 需要频繁的在java类和xm
  • Keychron K3 Pro

    Keychron K3 Pro Keychron K3 Pro A31 连接蓝牙2 更改键盘背光模式和快速关闭开启键盘背光3 调整键盘背光亮度4 调整背光速度5 查看电池电量 Keychron K3 Pro A3 1 连接蓝牙 长按fn 4
  • miner14 export

    lotus chain export span class token operator span recent span class token operator span stateroots span class token oper
  • iTerm2 恢复窗口

    退出应用程序时关闭窗口
  • Homebrew 安装

    Homebrew Homebrew 安装添加 Homebrew 到 PATH在当前终端设置环境变量 运行参考 Homebrew 安装 span class token operator span bin span class token o
  • sed 使用#作为分隔符

    sed 将 bin sh替换为bash需要使用 进行转义 以匹配 字符使用 作为分隔符 将 bin sh替换为bash 需要使用 进行转义 以匹配 字符 sed 39 s span class token operator span spa
  • 华为 USG6625E 防火墙

    服务列表 需要设置目标端口
  • baseDeltaSeconds PROPAGATION_DELAY_SECS 设置传播延迟时间

    设置lotus传播延迟 默认区块传播延迟10秒设置环境变量参考 xff1a 默认区块传播延迟10秒 span class token string 34 baseDeltaSeconds 34 span span class token p
  • Ubuntu 18.04禁止内核更新

    apt span class token operator span mark showhold linux span class token operator span headers span class token operator
  • AMD64的4个架构级别 v1 v2 v3 v4

    查看cpu支持指令 lscpu span class token operator span grep Flags 例如 GOAMD64 61 v3 使用 AVX2 查看cpu是否支持指令 lscpu span class token op
  • Ubuntu 18.04 安装 ffmpeg

    Ubuntu 18 04 安装 ffmpeg wget http span class token punctuation span span class token operator span span class token opera
  • 图形界面无法启动的解决办法

    错误提示信息 xff1a log file 34 var log xorg 0 log 34 Using config file 34 etc X11 Xorg conf 34 gt error bad lenght in compatma
  • SUSE Linux Ubuntu 下安装mysql遇到的问题一。

    用的是阿里云的新服务器 xff0c 用的是Ubuntu不是Contos的在安装mysql的时候遇到了一些问题 在此记录一下 1 创建目录用来下载安装包 命令 xff1a mkdir usr local software 文件目录根据没有固定
  • 是时候拥抱ViewBinding了~

    是时候拥抱ViewBinding了 xff01 xff01 一 前言二 初识ViewBinding三 拥抱ViewBinding3 1 环境要求3 2 开启ViewBinding功能3 3 Activity中ViewBinding的使用3