VM表单是一种高仿Vue的封装,从Vue的角度来说,比如我们登录页面的vue里面定义了一个params对象,用来收集登录需要用到的参数。
在vue的内部,只要用户在输入框中输入了值就会实时的被vue放到params中。我们这里和vue如出一辙。
首先所有的表单类属于我们需要特别要关注的对象,所以但他们是来自不同的类,我们需要把这些来自不同的类统一管理(因为他们有很多共性),我们抽取一个VMProtocol协议,用来标识这是一个表单类:
表单类:要么收集数据(vid),要么触发事件(clickEvent)。
protocol VMProtocol {
var baseActivity: BaseActivity? //这个组件所属的activity,
var vid: String = "" //这个一般是输入框用的
var clickEvent: String = "" //这个一般是按钮用的
}
严格来说上面的BaseActivity类型不准确,因为安卓还有tab页面不是activity,之前我们已经用协议统一了,所以这个类型最好是BasePageInterface.
现在可以开始布局了。输入框按不同的页面填充vid,按钮就填写clickEvent。当页面布局好之后,页面是不是在运行的时候触发onLoad呢。当一会发生点击事件的时候,按钮需要调用到所属页面的js方法。但目前按钮怎么知道当前页面在哪呢,这需要我们写代码来得到。
另外,我们可以不用这么麻烦,点击按钮的时候,手写点击事件,再通过当前页面调用js。这样确实没问题,不过当页面按钮或输入框多的时候你会发现这是一个重复内枯燥的体力活,完全没必要做这个重复的工作。而且应该简洁封装,达到布局完页面功能就好的目的。
再观察下vue的语法,输入框仅仅是绑定了key,按钮仅仅是写了一个事件名,所有功能就对了,就应该这么简洁简单。
我们在BaseActivity的onLoad里面,此时所有的控件均已经加载完成了。我们从BaseActivity根视图中 递归 查询所有的VMProtocol。
特别注意:应该是递归查询,因为子视图的嵌套可能有多层。
细节:
(1)当一个view的子views数组的length=0,说明没有子视图,可以不用递归。
(2)当一个view是按钮,switch,列表等(这里是ios思维)复杂控件也可以不用递归,严格说应该是可能有子view的才递归。
(3)当这个view本身就是VMProtocol,其实也可以不用递归了。因为已经找到了。
从结果上,找到了VMProtocol之后,我们只需要做一件事:
vm.baseActivity = this
或者
vm.basePageInterface = this
比如按钮:未来,当这个按钮发生点击事件,这个button就可以拿到baseActivity,继而就可以调用js这个方法混编了。
一、封装VMTextField, 输入框(多行文本域同理)
class VMTextField: TextField, VMProtocol { //遵守VMProtocol协议
var basePageInterface: BasePageInterface? //这个组件所属的activity,
fun init(){ //控件初始化
self.addEventLister("inputEvent",{ //自己就在内部监听自己的实时输入事件
this.basePageInterface.js("vmodel", {vid: this.vid, value: this.value})
})
}
}
这个回调特别注意下:
(1)控件拿到所在页面basePageInterface
(2)拿到了页面,就能调用js方法混编了,混编的时候需要指明这是表单的action=vmodel, vid为指定要求的vid值,value就是输入框现在的值。
(3)vid一般是服务器字段,比如登录页面的用户名vid=name,密码vid=pwd。
二、封装VMButton:
这个很简单了,和上面一样,只是监听点击事件,并在点击后调用
this.basePageInterface.js(this.clickEvent, {data: this.data})
其中clickEvent为点击事件名,data为区分相同事件的参数(我的页面有体现)。
我们现在有了VMButton,VMTextField, VMTextview,可以实现很多页面的功能了。