入门Android
应用开发,第二步当然是要处理一些基本事件啦。📱
准备
对于Android
的一些基本事件的处理,一般的做法都是使用findViewById
获取xml
布局上的控件,然后转化为对应的控件类。有了控件对象,我们就可以通过各种方法获取控件中的内容或者响应控件中的不同的事件。
当然,有着各种各样的框架可以替代findViewById
,以更方便更优雅的方式写代码,比如ButterKnife
。
但是这种做法把所有的逻辑、数据和视图的处理都放在了Activity
里面,如果项目变大了,这种写法就会变得十分不可维护,因此衍生了一系列的 MVC、MVP、MVVM 的架构。
通过这个项目,来尝试一下Google
官方的推荐DataBinding
DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的耦合性,使代码逻辑更加清晰。MVVM 相对于 MVP,其实就是将 Presenter 层替换成了 ViewModel 层。DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常
官方宣称这个框架的性能比findViewById
还强。
DataBinding
并不需要什么依赖库,并且最低可以支持到Android 1.3
,可谓是十分强大了,只需要改动一下
文件位置:app\build.gradle
1 | android { |
把数据绑定选项开启就可以了
Model
首先,我们要先为这个页面建立一个Model
1 | public class SearchModel extends BaseObservable { |
在这个Model
里面,有两个变量,一个是搜索框内的文本searchKey
,另一个是搜索的类型。
然后还需要为他加上一般的getXX
和setXX
的基本函数,以便外部可以访问并且绑定这两个变量。
Handler
有了数据绑定,当然还需要一个事件绑定,这里用一个Handler
类来实现
1 | public class SearchHandler { |
在这个类里面实现了两个事件,分别对应Button
的点击事件和RadioGroup
的选择事件。
XML
DataBinding
强大的地方就在于可以通过修改XML
文件,指定控件绑定的事件或者变量。
首先,将原来的布局外面套上一层Layout
然后把上面写好的Model
和Handler
引入这个布局当中。
1 |
|
然后就可以将这些变量或者事件绑定到指定的控件上面了.
下面是其中一个控件绑定的例子
1 | <RadioGroup |
其中数据通过@={class.data}
对数据进行双向绑定,其中的=
是双向绑定。
然后事件通过@{class::function}
绑定事件,也可以通过 Lambda 表达式@{()=>{class.function()}}
进行绑定。
Activity
绑定到一系列东西之后,自然要把这些东西都联系起来,这里就需要在Activity
里面来处理了。
1 | public class SearchActivity extends AppCompatActivity { |
使用DataBindingUtil
将layout
和Activity
绑定在一起,然后为他设置好Model
和Handler
。
这样就把他们联系起来了。
获取信息
那在事件处理中,我们如何获取绑定好的数据呢?
这个很简单,由于我们的Handler
里面已经在初始化的时候绑定了指定的binding
类,我们只需要从里面get
就可以了
1 | SearchModel searchData = mSearchBinding.getSearchModel(); |
然后就可以很简单地获取到绑定好的数据了。比起findViewById
要方便得多,尤其是当项目庞大的时候。
Toast 与 AlertDialog
这次的实验中主要使用到就是这两个类,用于显示Toast
和对话框。
他们的用法也比较简单。
Toast
甚至只需要一行代码就可以实现了
1 | Toast.makeText(mContext, "搜索内容不能为空", Toast.LENGTH_LONG).show(); |
mContext
为当前活动的上下文,在handler
初始化的时候就已经绑定了
Toast.LENGTH_LONG
为Toast
显示停留的时间。
而AlertDialog
就需要借助AlertDialog.Builder
来建立
下面是一个简单例子:
1 | AlertDialog.Builder builder = new AlertDialog.Builder(mContext); |
通过各种set
方法为对话框设置标题、内容、按钮以及其绑定的回调事件。
最后通过show()
使其显示出来。
奇怪的东西
虽然使用来是比较方便,一路上也没有遇到什么问题,但是却发现了一个奇怪的现象。
奇怪的 ID
如果布局文件中Button
的id
的字典序大于RadioGroup
的id
,那么上面绑定在RadioGroup
的onChechChange
事件onSelectType
就会被莫名其妙地在界面加载的时候触发两次。也就是说,在应用打开的时候,会有两次Toast
弹出,显示图片被选中
。
然而把他们的 ID 的字典序调换过来就没有了这个现象了,这已经是科学不能解释的事情了,大概涉及到玄学领域了。
通过查看DataBindIng
自动生成的类ActivitySearchBindingImpl
,发现了这样一个现象,就是这两个事件的绑定的顺序是和他们的id
的字典序是有关系的。
奇怪的绑定
然后我尝试用回调 Lambda 的方式去掉直接绑定的方式绑定button
的onClick
事件,然后一开始的触发两次居然再次消失了。这大概和绑定的顺序已经方法有关,但是都是不可预测的玄学东西。
Model 与 Handler
通过在互联网的搜索,发现了stackoverflow| Android Data binding issue Binding adapter call twice上一个一个类似的问题。
1 | private OnRebindCallback<ActivityMainBinding> delayRebindCallback = |
然后我在绑定Model
之后立刻binding.executePendingBindings()
,然后再绑定handler
事件,然后那个现象就消失了。然后我就可以猜测,这个现象是因为UI
的多线程导致的。
因为model
的绑定会导致onCheckChange
事件触发, 如果handler
的绑定在model
之前,那么就会触发那个事件,而绑定的顺序和方式可能也会导致不同的绑定顺序。
触发两次
那为什么会触发两次呢?
Android-为什么 RadioGroup.onCheckedChanged() 会调用多次?
这个应该就和这个事件的内部实现有关了,因为一开始的初始值是-1
,然后代码中对其赋值。然后就发生了一系列的化学反应,导致触发了两次。
总结
通过简单的事件处理,来探索了Google
最新的框架,虽然遇到一些奇奇怪怪的东西,但是总体上体验还是不错的,比起传统的方式,这种架构的确是更加优美了。