findBiewById
findBiewById 是 Android 开发中在布局中查找 View 元素的 Api。
findBiewById 基本使用
1 2 3 4 5 6 7 8 9 10
| class DemoActivity extends AppCompatActivity { TextView mTextDemo;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_demo); mTextDemo = (TextView) findViewById(R.id.tv_demo); mTextDemo.setText("Demo"); } }
|
因为写起来很繁琐(而且还需要手动强转类型),所以逐渐出现了各种简化或者代替它的方式。
省略强转
从 Android Support Library 26.0.0 Beta 1 开始 findViewById 将不再需要强转了。
findViewById() 方法的所有实例现在会返回 <T extends View> T,而不是 View。
1 2 3 4 5 6 7 8 9 10
| class DemoActivity extends AppCompatActivity { TextView mTextDemo;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_demo); mTextDemo = findViewById(R.id.tv_demo); mTextDemo.setText("Demo"); } }
|
findBiewById 原理
findBiewById 原理实质上是递归遍历查找匹配 Id 的 View。
Activity 中 findViewId 方法会调用获取 Window -> DecoreView -> View 的 findBiewById 方法。
最终会调用到 View 中的 findViewTraversal 方法。方法名看上去是遍历操作,在 View 类中找不到遍历逻辑;
实际上 ViewGroup 覆写了 View 的 findViewTraversal 方法,实现了递归遍历查找匹配 View 的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class View { @Nullable public final <T extends View> T findViewById(@IdRes int id) { if (id == NO_ID) { return null; } return findViewTraversal(id); }
protected <T extends View> T findViewTraversal(@IdRes int id) { if (id == mID) { return (T) this; } return null; } } class ViewGroup {
@Override protected <T extends View> T findViewTraversal(@IdRes int id) { if (id == mID) { return (T) this; }
final View[] where = mChildren; final int len = mChildrenCount;
for (int i = 0; i < len; i++) { View v = where[i];
if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) { v = v.findViewById(id);
if (v != null) { return (T) v; } } }
return null; } }
|
ButterKnife 是 jakewharton 大神开源作品,用于替代 findViewById ,避免繁琐的写法。
ButterKnife 基本使用
1 2 3 4 5 6 7 8 9 10 11
| class DemoActivity extends AppCompatActivity { @BindView(R.id.tv_demo) TextView mTextDemo;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_demo); ButterKnife.bind(this); mTextDemo.setText("Demo"); } }
|
ButterKnife 原理
注解编译时生成绑定类,代替我们完成 FindViewById 的操作。
PS:ButterKnife Github ReadMe 中说明已不推荐使用,推荐下文提到的 Android 官方提供的 ViewBinding
Data Binding 基本使用
布局需要使用 <layout> 标签包裹。
1 2 3 4 5 6 7 8 9
| class DemoActivity extends AppCompatActivity {
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.demo_activity); ActivityDemoBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_demo); binding.tvDemo.setText("Demo"); } }
|
Data Binding 原理
自动查找所有 View 并缓存到 binding 实例中以供访问。性能超过手写的 findViewById,因为它只遍历了一遍 XML 布局,而 findViewById 每次都会去遍历 XML 布局;include 布局中的 view 也能同样能访问,并且保留结构。
Kotlin Android Extensions
直接生成对应的 View 作为属性,不需要 findViewById,不需要定义变量,直接使用。使用时需要注意访问的 View 属于哪个 Layout,因为智能提示的候选项会提供所有布局中的 View 供你选择,然后帮你 import 对应包以便你访问这个 View;假如 import 的多个同一层级的 layout 中具有相同的 id,则这个 id 对应的 View 将无法访问。
Kotlin Android Extensions synthetic 基本使用
1 2 3 4 5 6 7 8 9 10 11
| import kotlinx.android.synthetic.main.activity_demo.*
class DemoActivity : AppCompatActivity() { {
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.demo_activity); tvDemo.setText("Demo"); } }
|
Kotlin Android Extensions synthetic 原理
Kotlin 会自动生成类似 findViewById() 的方法:findCachedViewById(),在这个方法里面创建一个 HashMap 缓存每次查找到的 View,避免每次调用 View 的属性或方法时都会重新调用 findCachedViewById() 进行查找。
PS:在 Kotlin 1.4.20-M2 中,JetBrain s 废弃了 Kotlin Android Extensions 编译插件。推荐使用 ViewBinding。
ViewBinding 基本使用
Android Studio 3.6 Canary 11 及更高版本中可用。
1 2 3 4 5 6
| android { ... viewBinding { enabled = true } }
|
1 2 3 4 5 6 7 8 9 10 11
| class DemoActivity : AppCompatActivity() { {
private lateinit var binding: ActivityDemoBinding
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityDemoBinding.inflate(layoutInflater) setContentView(binding.root) binding.tvDemo.setText("Demo"); } }
|
ViewBinding 原理
ViewBinding 优缺点
优点
Null 安全:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 @Nullable 标记。
类型安全:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。
更快的编译速度:视图绑定不需要处理注释,因此编译时间更短。
易于使用:视图绑定不需要特别标记的 XML 布局文件,因此在应用中采用速度更快。在模块中启用视图绑定后,它会自动应用于该模块的所有布局。
缺点与限制
- 布局和代码之间的不兼容性可能会导致编译版本在编译时(而非运行时)失败。
- 视图绑定不支持布局变量或布局表达式,因此不能用于直接在 XML 布局文件中声明动态界面内容。
- 视图绑定不支持双向数据绑定。
相关文章
使用视图绑定替代 findViewById
你好, View Binding! 再次再见, findViewById!
Kotlin 干掉了 findViewById,但用不好也会有性能问题
Migrating the deprecated Kotlin Android Extensions compiler plugin
【译】迁移被废弃的 Kotlin Android Extensions 插件