ViewBinding和DataBinding的理解和区别

2023-05-16

之前一直把ViewBinding当成了DataBinding,直到最近的学习中才发现他们不是一个东西。于是写下这篇笔记帮助理解和区分他们俩。

一、ViewBinding

1.什么是ViewBinding

先来看看官方是怎么说的。

通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。

在大多数情况下,视图绑定会替代 findViewById

来源:视图绑定 | Android 开发者 | Android Developers (google.cn)

在刚接触Android的时候,获取布局里的一个组件是通过findViewById去获取的。比如获取一个Button,那么写法就是

val btn: Button = findViewById(R.id.btn)

于是当组件很多的时候,就需要大量的用findViewById来获取,这是很繁琐的。然后在学习郭霖老师的《第一行代码时》,郭神书里提到了kotlin-android-extensions这个插件。该插件能够帮我们省去findViewById,在用Kotlin写的时候可以直接通过视图组件的Id来获取。

比如视图里有一个id为btn的Button组件,那么在Acitivy中就会有一个btn变量。这个插件帮我们简化了上面的步骤。

但是这个插件很快就被Google废弃了,在AndroidStudio中引入会出现警告。

 

取而代之的则是ViewBinding。

而Viewbinding就是会给每一个xml布局生成一个对应的binding类。比如activity_main.xml布局,就会生成一个ActivityMainBinding类。

2.怎么使用ViewBinding

想要使用ViewBinding,首先需要在build.gradle里加入配置。

android {
    ...
    buildFeatures {
        viewBinding true
    }
}

然后创建一个MainActivity,并写下布局文件activity_main.xml

<?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">
​
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
​
    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="textView"
        android:textColor="@color/black"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

然后在Activity里的使用

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }
}

ViewBinding帮我们生成了一个ActivityMainBinding类,我们通过ActivityMainBinding.inflate()来加载布局。

然后我们就可以通过binding来操作布局了。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.btn.setOnClickListener {
            Toast.makeText(this, "click", Toast.LENGTH_SHORT).show()
        }
        binding.tv.text = "setText"
    }
}

这样就帮我们省去了FindViewById的步骤,这也是ViewBinding最大的功能。

二、DataBinding

1.什么是DataBinding

还是先看看官方的说法

数据绑定库是一种支持库,借助该库,您可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源。

来源:数据绑定库 | Android 开发者 | Android Developers (google.cn)

这里官方的说话就有点看不懂了。那我就尝试来解释一下。

比如我们要修改一个TextView的text值,之前都是在代码里获取到TextView组件,然后通过textView.text去赋值。比如

val str = "setText"
binding.tv.text = str

或者当我们要获取一个TextView的文本值时,也需要通过textView.text来获取

val value = binding.tv.text.toString()

这样的操作就是程序化,要在逻辑代码里去赋值或取值。如果我们可以在xml布局文件里就声明该组件的值和哪个变量绑定,就能方便很多。

利用DataBinding就能做到这一点,比如

<?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"/>
       </LinearLayout>
    </layout>
    

这样声明式的方式就将数据绑定了。具体的使用方法会在下面给出。

2.怎么使用DataBinding

2.1 基础用法

想要使用DataBinding,同样需要现在build.gradle里加入配置

android {
    ...
    buildFeatures {
        dataBinding true
    }
}

然后我们先创建一个User的实体类,用于绑定数据

class User(var firstName: String, var lastName: String) {}

然后写布局activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
       <data>
           <variable name="user" type="com.example.User"/>
       </data>
       <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"/>
       </LinearLayout>
    </layout>

注意这里的格式, 最外层是一个Layout标签,里面包含了data标签和LinearLayout标签,这是Databinding的表示方式,LinearLayout其实就是这里的根布局。

如何形成这样的文件呢?在原本的xml文件里,将光标移动到根布局的位置,然后按alt+enter就会出现选项转换到DataBinding的格式。

 

然后再来分析布局文件的代码。

        <data>
           <variable name="user" type="com.example.User"/>
       </data>

data标签里表示的是数据源,name是名称,type是类型。这里的type就是我们前面写的User实体类。

    <LinearLayout
           android:orientation="vertical"
           android:layout_width="match_parent"
           android:layout_height="match_parent">
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.firstName}"/>
           <TextView android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:text="@{user.lastName}"/>
       </LinearLayout>

然后在TextView里的text属性,通过@{}的格式来获取data标签里声明的变量。

最后回到Activity中来绑定数据。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.user = User("Test", "User")
    }
}

首先是定义DataBinding时和ViewBinding有所不同,DataBinding是通过DataBindingUtil.setContentView来绑定布局的。

最后一行就是执行了数据绑定。

运行后结果如下:

 

这就是databinding最基础的用法。

2.2 绑定点击事件

databinding不止能绑定数据,还能绑定一些事件,比如点击事件。

我们先创建一个Handlers,里面添加一个onClick()方法,用于点击事件回调。

class Handlers {
​
    fun myOnClick(view: View) {
        Log.d("Handlers", "onClick()")
    }
}

然后修改刚才的布局文件,添加一个Button组件,然后在data标签里声明刚才写的Handlers。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="handler" type="com.example.example.Handlers" />
        <variable name="user" type="com.example.example.User"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        ...
        
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{handler::onClick}"
            android:text="Button"/>
​
    </LinearLayout>
​
​
</layout>
 

注意Button的onClick属性。依然是通过@{}来调用变量,并且对于方法的调用是"::"。

需要注意的是,自定义的这个方法的签名必须和监听器对象方法中的签名完全一样。比如点击事件是View.OnClickListner的onClick(view: View),那么自定义的方法参数也必须保持一致:myOnClick(view: View),否则会报错。

然后运行试一下,点击按钮,查看日志。

 

成功绑定。

2.3 使用可观察的数据对象

前面的基础使用,我们只是知道了DataBinding如何声明式而非程序式的赋值。但是这样的情况下,如果我们要修改视图里的值,我们依然需要通过在代码里赋值的方式才能修改。

数据绑定,我们更希望的是让组件属性的值直接绑定到一个变量中,当变量发生改变时组件属性的值相应发生改变,而不需要我们再去进行赋值。

那么要实现这种当一个值发生改变,另一个值相应发生改变的效果,我们很容易想到观察者模式。那我们就可以将变量变成一个可观察的数据对象。

如果去实现Observable接口的话,对于一些简单的类型来说比较麻烦。所以基础类型的变量可以用以下的类来声明

  • ObservableBoolean

  • ObservableByte

  • ObservableChar

  • ObservableShort

  • ObservableInt

  • ObservableLong

  • ObservableFloat

  • ObservableDouble

  • ObservableParcelable

接下来就来实践测试一下。

我们先修改一个User实体类

class User {
    val firstName = ObservableField<String>()
    val lastName = ObservableField<String>()
    val age = ObservableInt()
}

注意这里声明变量我们使用了val,是因为要使用Observable要尽量避免开箱或封箱操作,在Java里声明也应该是pubc final属性

private static class User {
        public final ObservableField<String> firstName = new ObservableField<>();
        public final ObservableField<String> lastName = new ObservableField<>();
        public final ObservableInt age = new ObservableInt();
    }

这里有人可能会有疑问,用val定义了我怎么修改变量的值?其实Observable的实现类里都提供了get()和set()函数来修改具体的值。

接着在布局里新增一个TextView来展示age变量。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="handler" type="com.example.example.Handlers" />
        <variable name="user" type="com.example.example.User"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"/>
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"/>
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{String.valueOf(user.age)}"/>
        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{handler::onClick}"
            android:text="Change"/>
    </LinearLayout>
</layout>
​


  

然后修改一下Handlers的点击事件逻辑,点击按钮修改user的变量值。

class Handlers(private val user: User) {
​
    fun onClick(view: View) {
        user.firstName.set("Luka")
        user.lastName.set("Dončić")
        user.age.set(23)
        Log.d("Handlers", "onClick()")
    }
}

最后在MainActivity里绑定

package com.example.example
​
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.databinding.DataBindingUtil
import com.example.example.databinding.ActivityMainBinding
​
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)
        val user = User()
        user.firstName.set("Stephen")
        user.lastName.set("Curry")
        user.age.set(34)
        binding.user = user
        binding.handler = Handlers(user)
    }
}

最终的效果如下

 

这样我们就不用再去给组件属性做赋值操作,只需要修改绑定的变量的值即可。

三、区别

看到这里,相信你对ViewBinding和DataBinding都有了一定的了解。接下来就总结一下他们的区别。

  1. 目的不同。ViewBinding的出现仅仅是为了帮开发人员省去写findViewById的步骤;而DataBinding是用于绑定数据的,能够把视图的数据和代码变量绑定起来,并且实现自动更新。这个特性使得DataBinding能和MVVM框架进行很好的配合。

  2. 初始化方式不同。ViewBinding通过生成的Binding类的inflate方法来加载布局,然后还需要用Activity的setContentView()方法来绑定。而Databinding则是通过DataBindingUtil.setContentView()来绑定的。

  3. 包含关系。DataBinding也有ViewBinding的功能,也可以省去findViewById()方法。

四、总结

本文从使用方面理解了什么是ViewBinding和DataBinding,并对他们做了区别分析,加深对他们俩的理解。

本文主要参考资料为Android 开发者 | Android Developers (google.cn)

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

ViewBinding和DataBinding的理解和区别 的相关文章

  • VM 与 Device/Credential Guard 不兼容

    目前来说除了网上清一色的禁用Hyper V方式 xff0c VM官网给出了另外的解决方式 xff0c 升级Windows版本和VM 版本 xff0c 官网解决方式链接 xff1a https kb vmware com s article
  • 安装ROS Melodic 时出现“The following packages have unmet dependencies: ros-melodic-desktop-full :...“

    问题 如题所示 xff0c 安装时发现安装ros melodic desktop full时显示一堆软件无法安装 xff0c 一个一个安装显然是不现实的 xff0c 找了网上方法都比较不理想 xff0c 后来感觉可能会是更新源的问题 解决方
  • 数据百问系列之六:数据仓库中的主题域是如何划分的?

    本次讨论的主题是 xff1a 数据仓库中的主题域是如何划分的 xff1f 问题 xff1a 你在工作中和交流中 xff0c 有主题域划分的例子可以分享吗 xff1f 分析 xff1a 本话题是一个发散性的话题 xff0c 并没有限制太多的内
  • Linux系统下安装Java环境(史上最简单没有之一)

    Linux系统下安装Java环境 xff08 史上最简单没有之一 xff09 主要有两个方法 xff1a 一个是直接用yun安装jdk xff08 不推荐 xff0c 环境变量不好配置 xff09 yum span class token
  • int数据类型的取值范围

    int取值范围 总所周知int类型在计算机中占4个字节 4 8 61 32位 xff0c 因此int类型数据的取值范围为 xff1a 231 231 1 即 xff1a 2147483648 2147483647 xff0c 那么这是如何得
  • (转载)计算机为什么要用补码?

    我记得大一的时候 xff0c 计算机基础 第一章内容就会讲到计算机编码 期末考试的时候 xff0c 老师会有这样的考题 xff1a 求出一个数的补码 我能清楚的记得步骤是 xff1a 先判断数的正负 正数的补码是其原码本身 xff1b 而负
  • C语言技巧--结构体指针的强制转换

    关于强制类型转换 总所周知 xff0c 在编程语言中的强制类型转换可以将已经既定的数据类型强制转换成所需要的目的类型 其实强制类型转换的过程中并不会改变数据内存中存贮的具体二进制的值 xff0c 而是仅使编译器对此内存块内的数据的解释方法做
  • IPv6网络编程注意[from IPv4]随笔

    C语言IPv6网络编程注意 from IPv4 随笔 在选择address family时 xff08 如socket inet ntop 第一个参数 xff0c sockaddr in6 sin6 family xff09 使用AF IN
  • VMware虚拟机NAT无法联网的几大原因

    1 宿主机电脑网络链接 适配器中 xff0c VMnet8网卡可能未启用 xff0c 请禁用后再启用 2 右键我的虚拟机 gt 设置 gt 网络适配器 xff0c 选择NAT或者自定义 gt VMnet8 3 VMware gt 编辑 gt
  • SELinux学习总结 (Ubuntu)

    SELinux学习总结 目录 一 介绍1 简介2 作用3 架构 https blog csdn net MyArrow article details 10063581 二 英文缩写三 SELinux元素介绍1 主体 Subject 2 客
  • Linux PAM 可插入验证模块

    这两篇足够了 xff0c 记录一下 xff1a pam模块的配置使用 Linux下PAM模块学习总结
  • 微信公众号文章跳转网页携带参数

    微信公众号文章跳转网页时 xff0c 请求头中携带了微信文章链接 xff0c 我们把需要传递的参数放在微信文章链接中就可以了 2020年12月10日11 35 23更新 微信方面做出限制 xff0c 已经获取不到Header xff1a R
  • 深入Linux C/C++ Timer定时器的实现核心原理

    时间与定时 定时器的实现原理Linux上的定时函数获取当前系统时间定时器的设计最小堆实现时间轮实现 要不要用Timerfd xff1f 每个超时事件独享一个timerfd所有超时事件共享一个timerfd 总结 我曾以为像定时器这样基础的功
  • 深入linux下文件系统磁盘Disk,分区Partition,挂载Mount

    目录 Linux 分区简介挂载点目录简介实战分区挂载临时挂载永久挂载 xff1a 开机自动挂载添加硬盘 amp 分区 amp 挂载loop device 回环设备loop mount绑定式挂载 bind mount 重新挂载 remount
  • 高效工具-局域网服务器访问公网

    文章目录 任务需求方法1 xff1a 使用CCproxy代理简单介绍下载安装配置逻辑本机配置客户机配置 成功测试 方法2 修改MAC地址查询本机MAC地址修改内网服务器MAC地址打开rc local service服务添加Install段创
  • 百万C++程序员的启蒙书,畅销20余年,这部经典终于出配套习题解答了!

    在编程的世界里 xff0c 很多语言来了又走 xff0c 而C 43 43 却屹立了30年 xff0c 并在21世纪仍保持强劲势头 去年 xff0c C 43 43 之父Bjarne Stroustrup公布了C 43 43 20添加的新特
  • 【无标题】

    1 MLT开源框架 MLT是为视频编辑而设计的LGPL多媒体框架 本文档为MLT的最小配置 构建和安装提供了快速参考 有关用法详细信息 xff0c 请参阅docs 目录 有关开发细节和贡献指南 xff0c 请参见网站 2 配置 通过运行以下
  • IDEA新建project步骤,项目组成和解释

    完成之后如图所示 xff1a 新建完成后会有2个文件 xff0c 一个是新建的文件夹其下包括 idea和src源码文件 xff0c 另一个是external libraries xff0c 其中存储的是一个可能用到的jar包 xff0c 如
  • ssm单元测试(junit)简单使用

    测试 软件测试 单元测试 xff1a 对你每个函数单元进行测试 xff0c 保证每个模块正常工作集成测试 xff1a 对已经测试过的功能模块进行组装后的测试系统测试 xff1a 检验软件产品能否与系统的其他部分协调工作验收测试 xff1a
  • ubuntu18图形界面设置开机自启动踩坑

    解决ubuntu18开机自启动问题 xff08 2 xff09 自己写一个shell脚本 将写好的脚本 xff08 sh文件 xff09 放到目录 etc profile d 下 xff0c 系统启动后就会自动执行该目录下的所有shell脚

随机推荐

  • idea本地编译报错 程序包org.slf4j不存在;程序包javax.servlet.http不存在;Error:(13, 2) java: 找不到符号;

    一 问题详细 最近换了一台新电脑 再打开之前的旧项目是报下面的错误 但是项目可以正常打包编译 但是就不能本地运行 idea中通过maven已经导入了包 xff0c idea中也能定位到包的位置 xff0c 本地maven仓库也有对应的jar
  • 构建Spring项目的一些配置文件

    pom xml基础配置 lt dependencies gt lt https mvnrepository com artifact log4j log4j gt lt dependency gt lt groupId gt log4j l
  • Api Generator Plus & Copy as curl 自动上传YApi接口

    Api Generator Plus 插件能够一键自动上传YApi接口 xff1b 一键生成 curl 命令 fetch方法 axios方法 快速开始 1 打开插件管理 xff0c 搜索api generator plus xff0c 安装
  • centos6升级到7.2-----2023年

    CentOS 6 升级到 CentOS 7 准确的说 xff0c 是从 CentOS 6 5 先升级到 CentOS 7 2 只有 6 5 以上版本才能这么升级 xff0c 而且最高只能升级到 7 2 现在的问题是官网已经不再维护 Cent
  • 怎么在IDEA中配置power shell

    第一步 xff1a 接下来 xff0c 在步骤3那里找到黄色方框里面的powershell exe文件 在点击OK之后 xff0c 你就能看到以下 说明 xff0c 已经配置成功了
  • 【数据结构】——二叉树的创建详解

    之前有篇文章概要的叙述了一下关于二叉树的创建 xff0c 参考博文二叉树的c语言创建但是很不全面 xff0c 这篇文章就让我们来好好探讨一下关于二叉树的创建吧 首先 xff0c 我们要做一些前期准备 xff0c 有关于本课二叉树的结点信息和
  • Fortify--安装与使用

    Fortify是Micro Focus旗下AST xff08 应用程序安全测试 xff09 产品 xff0c 其产品组合包括 xff1a Fortify Static Code Analyzer提供静态代码分析器 xff08 SAST xf
  • [web安全]burpsuite抓取微信公众号、小程序数据包

    最近在接触微信公众号和微信小程序的渗透 xff0c 用过模拟器 xff0c 也直接在手机上设置代理 xff0c 都遇到各种问题 xff0c 用起来很不舒心 记录遇到的一些问题 xff0c 帮助和我一样踩过坑的 xff0c 亲测好用 IE浏览
  • 安全-开源项目安全检查规范

    有些公司为了提高在IT圈的技术知名度 xff0c 增加行业影响力 xff0c 一些技术会定期将非核心业务源代码以公司级名义上传到源码开源平台 xff0c 如GitHub 特此输出相关的安全检查规范 第一条 开源的代码项目可以为通用的解决方案
  • 安全-系统上线安全检查规范

    现在各个公司都开始重视安全 xff0c 不仅仅是因为国家开始重视安全 xff0c 而是安全漏洞一旦暴露 xff0c 被有心之人发现进行破坏 xff0c 损失将无法估量 xff1b 比如 xff1a 前端时间拼多多优惠券事件 安全测试是一项比
  • 应用安全测试技术DAST、SAST、IAST对比分析-持续更新

    应用安全测试技术DAST SAST IAST对比分析 持续更新 版权来源 xff1a 安全牛首发文章 xff0c 本文仅补充完善 一 全球面临软件安全危机 我们即将处于一个软件定义一切的时代 xff0c 这是 一个最好的时代 xff0c 也
  • Python中函数和方法的区别

    简单总结 xff1a 与类和实例无绑定关系的function都属于函数 xff08 function xff09 xff1b 与类和实例有绑定关系的function都属于方法 xff08 method xff09 首先摒弃错误认知 并不是类
  • 九宫格,二十五宫格,甚至八十一宫格 技巧

    九宫格 二十五宫格 甚至八十一宫格 只要是奇数的平方宫格者能做到横格相加 坚格相加 斜格相加得数相同 而偶数的宫格只有十六宫格有些规律 下面是三宫格 五宫格 七宫格 九宫格图 填写技巧 第一行中间填1 xff0c 右上没有的 xff0c 就
  • python 编写输出到csv

    def test write self fields 61 fields append orderCode with open r 39 test001 csv 39 39 a 39 newline 61 34 34 as f writer
  • Vagrant 共享目录出现 mount:unknown filesystem type ‘vboxsf‘

    环境 xff1a Windows 10 VirtualBox 7 0 6 Vagrant 2 3 4 错误如下 xff1a 61 61 gt default Attempting graceful shutdown of VM 61 61
  • 利用python进行企业微信机器人自动发送消息

    def test 004 robot self headers 61 34 Content Type 34 34 text plain 34 s 61 34 卖品 xff0c 打印码 xff1a 验证码 34 format str prin
  • js修改页面hidden属性

    想获取这个value的值 xff0c 但是看其是个input标签 xff0c 他的type属性是hidden 也就是只能定位 xff0c 不能对其操作 xff0c 想要通过元素的 get attribute 34 value 34 是不可能
  • mybatis的多表查询(一对一,一对多,多对多)

    mybatis多表查询 表之间的关系有几种 xff1a 一对多 多对一 一对一 多对多 举例 xff1a 用户和订单就是一对多 订单和用户就是多对一 一个用户可以下多个订单 多个订单属于同一个用户 人和身份证号就是一对一 一个人只能有一个身
  • 8款纯CSS3搜索框

    效果如下 xff1a 代码实现如下 xff1a span class token doctype lt DOCTYPE html gt span span class token tag span class token tag span
  • ViewBinding和DataBinding的理解和区别

    之前一直把ViewBinding当成了DataBinding xff0c 直到最近的学习中才发现他们不是一个东西 于是写下这篇笔记帮助理解和区分他们俩 一 ViewBinding 1 什么是ViewBinding 先来看看官方是怎么说的 通