文章目录

  • Jetpack ViewBinding
    • 概述
    • ViewBinding优点
    • 配置ViewBinding
    • 使用
      • 在Activity中使用
      • 在Fragment中使用
      • 在RecyclerView adapter中使用
      • 在include标签中使用
        • 不使用merge标签
        • 使用merge标签
    • 封装使用
      • 基类封装,不使用反射
      • 基类封装,使用反射
      • 委托实现
    • 源码分析
    • 代码下载

Jetpack ViewBinding

概述

官网文档

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

ViewBinding优点

ViewBinding优点

  • Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。
  • 类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。因此不存强制转换导致的异常风险。

与findViewById区别

  • findViewById编写过于冗余。
  • 类型仍然不安全。

与ButterKnife区别

  • 官宣不维护,推荐使用ViewBinding。
  • 类型仍然不安全。
  • 对组件化项目不友好。

与Kotlin Android Extensions

  • JetBrains废弃该插件。
  • 类型仍然不安全。
  • 性能偏低。

配置ViewBinding

Android Studio3.6以上

android {viewBinding {enabled = true}
}

Android Studio4.0以上

android {buildFeatures {viewBinding = true}
}

如果需要忽略某个布局文件,需要添加tools:viewBindingIgnore="true"属性到布局中

<LinearLayout...tools:viewBindingIgnore="true" >...
</LinearLayout>

使用

当开启ViewBinding后,系统会为该模块中每个XML布局文件生成一个绑定类(转换为驼峰命名并在末尾添加Binding),每个绑定类均包含根视图已交具有id的所有视图的引用。

例如:布局文件名为activity_main.xml,生成绑定类为ActivityMainBinding

在Activity中使用

使用流程

  • 开启视图绑定功能后,系统会为该模块中的XML布局生成一个绑定类,每个绑定类都包含根布局和具有ID布局的引用。
  • 调用绑定类的inflate()方法获取绑定类对象。
  • 调用绑定类对象的getRoot()方法获取根布局传递到setContentView()

XML布局

activity_view_binding_simple.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"><TextViewandroid:id="@+id/tv_name"android:layout_width="wrap_content"android:layout_height="wrap_content" /><TextViewandroid:id="@+id/tv_age"android:layout_width="wrap_content"android:layout_height="wrap_content" /><ImageViewandroid:id="@+id/iv_avatar"android:layout_width="wrap_content"android:layout_height="wrap_content" /><Buttonandroid:id="@+id/btn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="确定" />
</LinearLayout>

Activity类

class ViewBindingSimpleActivity : BaseActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)val viewBinding = ActivityViewBindingSimpleBinding.inflate(layoutInflater)setContentView(viewBinding.root)viewBinding.tvName.text = "hello world"viewBinding.tvAge.text = 18.toString()viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}
}

在Fragment中使用

方式一

  • 调用绑定类的inflate()方法,获取绑定类对象。
  • 再调用getRoot()方法获取根布局。
  • onCreateView()方法返回根布局,使其成为屏幕上的活动视图。
  • 由于Fragment的存在时间比视图长。因此需要在Fragment的onDestroyView()方法中清除对绑定类对象的所有引用。
class MyFragment : BaseFragment() {private var _viewBinding: FragmentMyBinding? = nullprivate val viewBindingget() = _viewBinding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {_viewBinding = FragmentMyBinding.inflate(inflater, container, false)return viewBinding.root}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewBinding.tvName.text = "hello world"viewBinding.tvAge.text = 18.toString()viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}override fun onDestroyView() {super.onDestroyView()_viewBinding = null}
}

方式二

class MyFragment : BaseFragment() {private var _viewBinding: FragmentMyBinding? = nullprivate val viewBindingget() = _viewBinding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {return inflater.inflate(R.layout.fragment_my, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)_viewBinding = FragmentMyBinding.bind(view)viewBinding.tvName.text = "hello world"viewBinding.tvAge.text = 18.toString()viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}override fun onDestroyView() {super.onDestroyView()_viewBinding = null}
}

在RecyclerView adapter中使用

class MyAdapter(context: Context, private val data: ArrayList<String>) :RecyclerView.Adapter<MyAdapter.ViewHolder>() {val layoutInflater: LayoutInflater = LayoutInflater.from(context)class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {val text: TextView = itemView.findViewById(R.id.text)}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {val viewBinding = ItemTextBinding.inflate(layoutInflater, parent, false)return ViewHolder(viewBinding.root)}override fun onBindViewHolder(holder: ViewHolder, position: Int) {holder.text.text = data[position]}override fun getItemCount(): Int {return data.size}
}

在include标签中使用

ViewBinding可以与<include>标签一起使用

不使用merge标签

  • 一定要给<include>标签定义id,使用该id访问布局中的控件。

XML布局

title_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#2196F3"android:minHeight="50dp"android:padding="10dp"><TextViewandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:text="返回" /><TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="标题位置" /><TextViewandroid:id="@+id/confirm"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:text="确定" /></RelativeLayout>

Activity的XML布局

activity_vbinclude.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".IncludeActivity"><includeandroid:id="@+id/titleBar"layout="@layout/titlebar" /></LinearLayout>

在include标签中使用

public class IncludeActivity extends AppCompatActivity {private Context context;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);context = this;ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());binding.titleBar.title.setText("这是一个标题");binding.titleBar.back.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "返回", Toast.LENGTH_SHORT).show();}});binding.titleBar.confirm.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(context, "确定", Toast.LENGTH_SHORT).show();}});}
}

使用merge标签

  • <merge>标签有利于减少布局层次。
  • 需要使用bind()方法绑定根视图。
  • 不能给<include>标签设置id。

布局:detail_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"><ImageViewandroid:id="@+id/ivDetail"android:layout_width="100dp"android:layout_height="100dp"android:scaleType="fitXY" />
</merge>

Activity的XML布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"android:orientation="vertical"tools:context=".IncludeActivity"><include layout="@layout/detail_layout" /></LinearLayout>

在include标签中使用

package com.example.viewbindingdemo;import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import com.example.viewbindingdemo.databinding.ActivityIncludeBinding;
import com.example.viewbindingdemo.databinding.DetailLayoutBinding;public class IncludeActivity extends AppCompatActivity {private Context context;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);context = this;ActivityIncludeBinding binding = ActivityIncludeBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());DetailLayoutBinding detailBinding = DetailLayoutBinding.bind(binding.getRoot());detailBinding.ivDetail.setImageResource(R.mipmap.ic_launcher);}
}

封装使用

基类封装,不使用反射

基类封装

abstract class BindingActivity<VB : ViewBinding> : BaseActivity() {private lateinit var _viewBinding: VBprotected val mViewBinding get() = _viewBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)_viewBinding = getViewBinding()setContentView(_viewBinding.root)}abstract fun getViewBinding(): VB
}abstract class BindingFragment<VB : ViewBinding> : BaseFragment() {private lateinit var _viewBinding: VBprotected val mViewBinding get() = _viewBindingoverride fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {_viewBinding = getViewBinding(inflater, container)return _viewBinding.root}abstract fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB
}

使用

class OneActivity : BindingActivity<ActivityOneBinding>() { override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 18.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}override fun getViewBinding(): ActivityOneBinding {return ActivityOneBinding.inflate(layoutInflater)}
}class OneFragment : BindingFragment<FragmentOneBinding>() {override fun getViewBinding(inflater: LayoutInflater,container: ViewGroup?): FragmentOneBinding {return FragmentOneBinding.inflate(inflater, container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 18.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show()}}
}

基类封装,使用反射

基类封装

abstract class BindingActivity<VB : ViewBinding> : BaseActivity() {private lateinit var _viewBinding: VBprotected val mViewBinding get() = _viewBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mContext = thisval type = javaClass.genericSuperclassif (type is ParameterizedType) {val clz = type.actualTypeArguments[0] as Class<*>val method = clz.getMethod("inflate", LayoutInflater::class.java)_viewBinding = method.invoke(null, layoutInflater) as VBsetContentView(_viewBinding.root)}}
}abstract class BindingFragment<VB : ViewBinding> : BaseFragment() {private var _viewBinding: VB? = nullprotected val mViewBinding get() = _viewBinding!!override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {val type = javaClass.genericSuperclassif (type is ParameterizedType) {val clz = type.actualTypeArguments[0] as Class<*>val method = clz.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)_viewBinding = method.invoke(null, inflater, container, false) as VB}return mViewBinding.root}override fun onDestroyView() {super.onDestroyView()_viewBinding = null}
}

使用

class TwoActivity : BindingActivity<ActivityTwoBinding>() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 28.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello2", Toast.LENGTH_SHORT).show()}}
}class TwoFragment : BindingFragment<FragmentTwoBinding>() {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)mViewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)mViewBinding.tvName.text = "小白"mViewBinding.tvAge.text = 28.toString()mViewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello2", Toast.LENGTH_SHORT).show()}}
}

委托实现

封装

//Activity ViewBindinginline fun <reified VB : ViewBinding> ComponentActivity.viewBindings(noinline factory: (LayoutInflater) -> VB,setContentView: Boolean = true
) = ActivityViewBindingDelegate1(factory, setContentView)inline fun <reified VB : ViewBinding> ComponentActivity.viewBindings(setContentView: Boolean = true) =ActivityViewBindingDelegate2(VB::class.java, setContentView)class ActivityViewBindingDelegate1<VB : ViewBinding>(private val factory: (LayoutInflater) -> VB,private val setContentView: Boolean,
) : ReadOnlyProperty<ComponentActivity, VB> {private var viewBinding: VB? = nulloverride fun getValue(thisRef: ComponentActivity, property: KProperty<*>): VB {viewBinding?.let { return it }viewBinding = factory(thisRef.layoutInflater).also { viewBinding ->if (setContentView) thisRef.setContentView(viewBinding.root)}return viewBinding!!}
}class ActivityViewBindingDelegate2<VB : ViewBinding>(private val clazz: Class<VB>,private val setContentView: Boolean,
) : ReadOnlyProperty<ComponentActivity, VB> {private var viewBinding: VB? = nulloverride fun getValue(thisRef: ComponentActivity, property: KProperty<*>): VB {viewBinding?.let { return it }val inflateMethod = clazz.getMethod("inflate", LayoutInflater::class.java)viewBinding =(inflateMethod.invoke(null, thisRef.layoutInflater) as VB).also { viewBinding ->if (setContentView) thisRef.setContentView(viewBinding.root)}return viewBinding!!}
}
//Fragment ViewBindinginline fun <reified VB : ViewBinding> Fragment.viewBindings(noinline factory: (View) -> VB) =FragmentViewBindingDelegate1(factory)inline fun <reified VB : ViewBinding> Fragment.viewBindings() =FragmentViewBindingDelegate2(VB::class.java)class FragmentViewBindingDelegate1<VB : ViewBinding>(private val factory: (View) -> VB,
) : ReadOnlyProperty<Fragment, VB> {private var viewBinding: VB? = nulloverride fun getValue(thisRef: Fragment, property: KProperty<*>): VB {viewBinding?.let { return it }val lifecycle = thisRef.viewLifecycleOwner.lifecycleviewBinding = factory(thisRef.requireView())if (lifecycle.currentState == Lifecycle.State.DESTROYED) {Log.w("TAG","Access to viewBinding after Lifecycle is destroyed or hasn't created yet. The instance of viewBinding will be not cached.")} else {thisRef.viewLifecycleOwnerLiveData.observe(thisRef) { viewLifecycleOwner ->viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {override fun onDestroy(owner: LifecycleOwner) {viewBinding = null}})}}return viewBinding!!}
}class FragmentViewBindingDelegate2<VB : ViewBinding>(clazz: Class<VB>,
) : ReadOnlyProperty<Fragment, VB> {private var viewBinding: VB? = nullprivate val bindMethod = clazz.getMethod("bind", View::class.java)override fun getValue(thisRef: Fragment, property: KProperty<*>): VB {viewBinding?.let { return it }val lifecycle = thisRef.viewLifecycleOwner.lifecycleviewBinding = bindMethod.invoke(null, thisRef.requireView()) as VBif (lifecycle.currentState == Lifecycle.State.DESTROYED) {Log.w("TAG","Access to viewBinding after Lifecycle is destroyed or hasn't created yet. The instance of viewBinding will be not cached.")} else {thisRef.viewLifecycleOwnerLiveData.observe(thisRef) { viewLifecycleOwner ->viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {override fun onDestroy(owner: LifecycleOwner) {viewBinding = null}})}}return viewBinding!!}
}

使用

class ThreeActivity : BaseActivity() {//方式一private val viewBinding: ActivityThreeBinding by viewBindings(ActivityThreeBinding::inflate)//方式二//    private val viewBinding: ActivityThreeBinding by viewBindings()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.tvName.text = "小白"viewBinding.tvAge.text = 38.toString()viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello3", Toast.LENGTH_SHORT).show()}}
}class ThreeFragment : BaseFragment(R.layout.fragment_three) {//方式一
//    private val viewBinding: FragmentThreeBinding by viewBindings(FragmentThreeBinding::bind)//方式二private val viewBinding: FragmentThreeBinding by viewBindings()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewBinding.ivAvatar.setImageResource(R.mipmap.ic_launcher_round)viewBinding.tvName.text = "小白33"viewBinding.tvAge.text = 338.toString()viewBinding.btn.setOnClickListener {Toast.makeText(mContext, "hello33", Toast.LENGTH_SHORT).show()}}
}

源码分析

代码下载

Jetpack ViewBinding相关推荐

  1. Jetpack——LiveData与ViewBinding

    这篇文章主要实现一下上篇文章中Jetpack-LiveData关于数据粘性的问题与ViewBinding两大部分. 一.LiveData去除数据粘性 上篇文章简单分析了 LiveData的使用和源码执 ...

  2. 【JetPack】视图绑定 ( ViewBinding ) 各种应用 ( 视图绑定两种方式 | Activity 布局 | 对话框布局 | 自定义组件布局 | RecyclerView 列表布局 )

    文章目录 I . 视图绑定 ( ViewBinding ) 界面的两种方式 II . Activity 界面中 应用 视图绑定 ( ViewBinding ) III . Dialog 对话框界面中 ...

  3. 【JetPack】为现有 Android 项目配置视图绑定 ( ViewBinding ) 模块 ( 视图绑定不影响传统布局操作 | 视图绑定类关联 Activity | 视图绑定类本质 )

    文章目录 I . 为现有项目配置 视图绑定 ( ViewBinding ) 应用 II . 视图绑定 ( ViewBinding ) 定制 III . 视图绑定 ( ViewBinding ) 对于正 ...

  4. 【JetPack】ViewBinding 视图绑定组件 ( 启用模块 | 视图绑定定制 | 绑定类名称生成规则 | 绑定类字段生成规则 | 绑定类获取根视图 | 绑定类获取布局组件 )

    文章目录 I . 视图绑定组件简介 II . 视图绑定 ViewBinding 使用前提 ( Android Studio 3.6 ) III . 视图绑定组件启用 IV . 定制视图绑定 ( 启用视 ...

  5. Android MVVM框架搭建(十)Hilt、ViewBinding、Activity Result API

    Android MVVM框架搭建(十)Hilt.ViewBinding.Activity Result API 前言 正文 一.依赖 二.Hilt使用 1. Hilt 应用类 2. ViewModel ...

  6. Jetpack Compose入门详解(实时更新)

    Jetpack Compose入门详解 前排提醒 前言(Compose是什么) 1.实战准备 一.优势与缺点 二.前四课 三.标准布局组件 1.Column 2.Row 3.Box 四.xml和com ...

  7. 优雅地封装和使用 ViewBinding

    /   今日科技快讯   / 近日,有网友在社交平台展示使用筋膜枪抢茅台的操作.对此天猫超市官方作出回应,表示此方法不可靠,并存在身体受伤的可能,希望广大网友理性购物. /   作者简介   / 明天 ...

  8. 大型Android项目架构:基于组件化+模块化+Kotlin+协程+Flow+Retrofit+Jetpack+MVVM架构实现WanAndroid客户端

    前言:苟有恒,何必三更眠五更起:最无益,莫过一日曝十日寒. 前言 之前一直想写个 WanAndroid 项目来巩固自己对 Kotlin+Jetpack+协程 等知识的学习,但是一直没有时间.这里重新行 ...

  9. Android ABC 取其精华去其糟粕、JetPack好用的组件推荐

    JetPack主流组件对比 jetpack组件名 推荐指数 槽点指数 解析 LiveData ★★★★★ 配合ViewModel和数据可以实现界面的动态更新,内部使用version版本控制和观察者模式 ...

  10. 【背上Jetpack之DataBinding】数据驱动魔法师 何时迎来翻身日?

    系列文章 [背上Jetpack]Jetpack 主要组件的依赖及传递关系 [背上Jetpack]AdroidX下使用Activity和Fragment的变化 [背上Jetpack之Fragment]你 ...

最新文章

  1. Android 悬浮窗口
  2. python 3下对stm32串口数据做解析
  3. ASP.NET - 截取固定长度字符串显示在页面,多余部分显示为省略号
  4. AI and logistics Patent
  5. ICCV 2019 最佳论文和最佳学生论文下载
  6. SQL2005 数据库数据同步
  7. WebShop WebSocket server 和WebSocket客户端的一对多关系维护
  8. lua transliterate实现(lua程序设计10.6练习10.3题)
  9. OpenResty(nginx)限流配置实现
  10. 【100题】第三十二 数组、规划
  11. 2021大同高考成绩查询,大同高考分数查询(查询方法+入口)
  12. KMP模式匹配 三(弦)
  13. 邱锡鹏:为什么相比于CV,NLP领域的发展要缓慢?
  14. C#属性默认值设置(model实体类)
  15. java计算机毕业设计足球队管理系统源码+数据库+系统+lw文档+mybatis+运行部署
  16. 关于数组中的大括号{}和数组的遍历
  17. jsp+css实现图片自动轮换
  18. 腾讯马化腾:公司拥有大量探索和开发元宇宙的技术和能力
  19. Unity小地图的放大缩小
  20. pubwin2009服务端 修改系统时间方法

热门文章

  1. 怎么样有效防电脑辐射
  2. 软件测试行业发展现状及前景
  3. 5 Linux系统编程之网络编程--学习笔记
  4. 数据结构与算法复习第一弹(快速排序)
  5. CCF中学生计算机程序设计入门篇练习2.4.2(NOI 1002 三角形) pascal
  6. 数学速算法_计算总是出算?小学数学常用的25种快速口算窍门,学好算数必备...
  7. 虚拟地址与虚拟内存的理解
  8. 零基础学习UI设计,有哪些软件推荐
  9. tk.mybatis.mapper.MapperException: 无法获取 com.zhao.mapper.BIllTypeMapper.selectCountByExample 方法的泛型信息
  10. 关于给青轴润轴消除弹簧音[误]