- 尽可能避免使用自定义 View
- 使用 Presenter 代替自定义 View 封装数据绑定逻辑
- 使用注册回调代替重写 View 方法
- View 的职责仅有展示数据与响应交互,将其他所有逻辑移出自定义 View
尽可能避免使用自定义 View
Android View 系统是个技术债务极重的系统:API 30 的 View.java 有 30409 行!这一债务如此沉重,以至于 Google 最终决定另起一套视图系统(Jetpack Compose)而非在现有系统基础上继续修补。
当你继承 View 类时,你也继承了这一沉重的技术债务。
- 对 View 进行单元测试非常困难,因为其严重依赖 Android 运行时环境。
- 如果你的自定义 View 中有业务相关的初始化逻辑,在 Layout Inspector 中预览视图可能会失败,因为其不能脱离真实 Android 设备正确模拟视图创建过程。这很影响开发效率。
- 对于重建 Activity/Fragment 的场景,系统会重新创建视图层级,这可能会绕过你自定义的初始化逻辑。
如果你只是想要一个编写业务逻辑的地方,请使用 Presenter 设计模式。如果你需要重写 View 方法以实现业务逻辑,请考虑使用监听器能否实现相同功能。
使用 Presenter 代替自定义 View 封装数据绑定逻辑
经常有需求需要封装一组视图,并对外提供数据绑定的方法。相对于使用自定义视图,更推荐使用 Presenter。
1 | public class MyTextView extends TextView { |
1 | public final class MyTextPresenter extends TextView { |
这样做有以下好处:
- 封装了 View 的接口方法,避免外界随意调用篡改其内容;
- 将视图布局本身与数据绑定接口解耦,便于以后修改布局;
使用注册回调代替重写 View 方法
有时自定义 View 的目的是为了重写特定的 View 以实现业务逻辑,但大部分这样的场景都可以通过注册 Listener 实现,以下是一些例子。
可重写的方法 | 相应的监听器或接口 |
---|---|
onClick | OnClickListener |
onLongClick | OnLongClickListener |
onFocusChange | OnFocusChangeListener |
onKey | OnKeyListener |
onTouchEvent | OnTouchListener |
onGenericMotion | OnGenericMotionListener |
onSystemUiVisibilityChange | OnSystemUiVisibilityChangeListener |
View 的职责仅有展示数据与响应交互,将其他所有逻辑移出自定义 View
这样符合单一职责原则,也便于业务逻辑复用。
另外,业务逻辑的生命周期不一定与 View 一致,将两者分离可以减少潜在的 bug 风险。