implent mvvm
This commit is contained in:
parent
f33c320b0c
commit
43fd9fedd1
@ -2,6 +2,7 @@ package com.github.penfeizhou.doricdemo;
|
|||||||
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import com.github.penfeizhou.doric.DoricContext;
|
import com.github.penfeizhou.doric.DoricContext;
|
||||||
@ -17,8 +18,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
DoricContext doricContext = DoricContext.create(this, DoricUtils.readAssetFile("demo.js"), "demo");
|
DoricContext doricContext = DoricContext.create(this, DoricUtils.readAssetFile("demo.js"), "demo");
|
||||||
doricContext.callEntity("__init__", new JSONBuilder()
|
doricContext.callEntity("__init__", new JSONBuilder()
|
||||||
.put("width", DoricUtils.px2dp(DoricUtils.getScreenWidth()))
|
.put("width", ViewGroup.LayoutParams.MATCH_PARENT)
|
||||||
.put("height", DoricUtils.px2dp(DoricUtils.getScreenHeight())));
|
.put("height", ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
doricContext.callEntity("log");
|
doricContext.callEntity("log");
|
||||||
doricContext.getRootNode().setRootView((FrameLayout) findViewById(R.id.root));
|
doricContext.getRootNode().setRootView((FrameLayout) findViewById(R.id.root));
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,11 @@ public class LinearNode extends GroupNode<LinearLayout> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||||
|
return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LinearLayout build(JSObject jsObject) {
|
public LinearLayout build(JSObject jsObject) {
|
||||||
return new LinearLayout(getContext());
|
return new LinearLayout(getContext());
|
||||||
|
@ -1,53 +1,48 @@
|
|||||||
import { Gravity, Mutable, NativeCall, Text, Color, VLayout, Panel, log, logw, loge, Group, Stack, } from "./index"
|
import { VMPanel, View, ViewModel, WRAP_CONTENT, Gravity, Mutable, NativeCall, Text, Color, VLayout, Panel, log, logw, loge, Group, Stack, } from "./index"
|
||||||
import { WRAP_CONTENT } from "./src/ui/view";
|
import { CENTER } from "./src/util/gravity";
|
||||||
|
|
||||||
|
interface CountModel {
|
||||||
|
count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
class CounterVM extends ViewModel<CountModel> {
|
||||||
|
build(root: Group, model: CountModel) {
|
||||||
|
const vlayout = new VLayout
|
||||||
|
const number = new Text
|
||||||
|
number.textSize = 40
|
||||||
|
number.layoutConfig = {
|
||||||
|
alignment: new Gravity().center()
|
||||||
|
}
|
||||||
|
const counter = new Text
|
||||||
|
counter.text = "点击计数"
|
||||||
|
counter.textSize = 20
|
||||||
|
|
||||||
|
vlayout.space = 20
|
||||||
|
vlayout.layoutConfig = {
|
||||||
|
alignment: new Gravity().center()
|
||||||
|
}
|
||||||
|
vlayout.addChild(number)
|
||||||
|
vlayout.addChild(counter)
|
||||||
|
root.addChild(vlayout)
|
||||||
|
|
||||||
|
this.bind((data) => {
|
||||||
|
number.text = data.count.toString()
|
||||||
|
loge(`data changed:${data.count},${vlayout.isDirty()}`)
|
||||||
|
})
|
||||||
|
counter.onClick = () => {
|
||||||
|
model.count++
|
||||||
|
loge('onclick', model.count)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Entry
|
@Entry
|
||||||
export class MyPage extends Panel {
|
class MyPage extends VMPanel<CountModel>{
|
||||||
|
|
||||||
build(rootView: Group): void {
|
createVM(): ViewModel<CountModel> {
|
||||||
const state = Mutable.of(1)
|
return new CounterVM({ count: 0 })
|
||||||
const numberView = new Text
|
|
||||||
numberView.width = WRAP_CONTENT
|
|
||||||
numberView.height = WRAP_CONTENT
|
|
||||||
numberView.top = 50
|
|
||||||
state.bind((v) => {
|
|
||||||
numberView.text = v.toString()
|
|
||||||
})
|
|
||||||
numberView.textSize = 40
|
|
||||||
numberView.layoutConfig = {
|
|
||||||
alignment: new Gravity().centerX()
|
|
||||||
}
|
|
||||||
rootView.addChild(numberView)
|
|
||||||
const click = new Text
|
|
||||||
click.textSize = 20
|
|
||||||
click.text = '点击计数'
|
|
||||||
click.onClick = () => {
|
|
||||||
state.set(state.get() + 1)
|
|
||||||
}
|
|
||||||
click.top = 200
|
|
||||||
|
|
||||||
click.layoutConfig = {
|
|
||||||
alignment: new Gravity().centerX()
|
|
||||||
}
|
|
||||||
|
|
||||||
rootView.addChild(click)
|
|
||||||
|
|
||||||
const vlayout = new VLayout
|
|
||||||
vlayout.width = this.getRootView().width
|
|
||||||
vlayout.height = 500
|
|
||||||
vlayout.bgColor = Color.parse('#ff00ff')
|
|
||||||
vlayout.top = 50
|
|
||||||
vlayout.centerX = this.getRootView().width / 2
|
|
||||||
vlayout.space = 0
|
|
||||||
vlayout.gravity = (new Gravity()).bottom()
|
|
||||||
vlayout.onClick = () => {
|
|
||||||
const stack = new Stack
|
|
||||||
stack.width = stack.height = 50
|
|
||||||
stack.bgColor = Color.safeParse('#00ff00')
|
|
||||||
vlayout.addChild(stack)
|
|
||||||
}
|
|
||||||
rootView.addChild(vlayout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NativeCall
|
@NativeCall
|
||||||
|
@ -4,4 +4,5 @@ export * from "./src/util/color"
|
|||||||
export * from './src/util/log'
|
export * from './src/util/log'
|
||||||
export * from './src/util/types'
|
export * from './src/util/types'
|
||||||
export * from './src/util/gravity'
|
export * from './src/util/gravity'
|
||||||
|
export * from './src/vm/mvvm'
|
||||||
export * from './src/runtime/global'
|
export * from './src/runtime/global'
|
@ -252,6 +252,10 @@ export abstract class Group extends View {
|
|||||||
|
|
||||||
onChildPropertyChanged(child: View) {
|
onChildPropertyChanged(child: View) {
|
||||||
this.getDirtyChildrenModel()[this.children.indexOf(child)] = child.nativeViewModel
|
this.getDirtyChildrenModel()[this.children.indexOf(child)] = child.nativeViewModel
|
||||||
|
this.getDirtyChildrenModel().length = this.children.length
|
||||||
|
if (this.parent) {
|
||||||
|
this.parent.onChildPropertyChanged(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isDirty() {
|
isDirty() {
|
||||||
|
65
js-framework/src/vm/mvvm.ts
Normal file
65
js-framework/src/vm/mvvm.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { View, Group } from "../ui/view";
|
||||||
|
import { Panel } from "../ui/panel";
|
||||||
|
|
||||||
|
|
||||||
|
function listen<T extends Object>(obj: T, listener: Function): T {
|
||||||
|
return new Proxy(obj, {
|
||||||
|
get: (target, prop, receiver) => {
|
||||||
|
const ret = Reflect.get(target, prop, receiver)
|
||||||
|
if (ret instanceof Object) {
|
||||||
|
return listen(ret, listener)
|
||||||
|
} else {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
set: (target, prop, value, receiver) => {
|
||||||
|
const ret = Reflect.set(target, prop, value, receiver)
|
||||||
|
Reflect.apply(listener, undefined, [])
|
||||||
|
return ret
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class VMPanel<M extends Object> extends Panel {
|
||||||
|
|
||||||
|
private vm: ViewModel<M> = this.createVM()
|
||||||
|
|
||||||
|
abstract createVM(): ViewModel<M>
|
||||||
|
|
||||||
|
|
||||||
|
getModel() {
|
||||||
|
return this.vm.getModel()
|
||||||
|
}
|
||||||
|
|
||||||
|
getVM() {
|
||||||
|
return this.vm
|
||||||
|
}
|
||||||
|
|
||||||
|
build(root: Group): void {
|
||||||
|
this.vm.build(root, this.vm.getModel())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class ViewModel<M extends Object> {
|
||||||
|
private model: M
|
||||||
|
private listeners: Function[] = []
|
||||||
|
constructor(obj: M) {
|
||||||
|
this.model = listen(obj, () => {
|
||||||
|
this.listeners.forEach(e => {
|
||||||
|
Reflect.apply(e, this.model, [this.model])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract build(root: Group, model: M): void
|
||||||
|
|
||||||
|
getModel() {
|
||||||
|
return this.model
|
||||||
|
}
|
||||||
|
|
||||||
|
bind(f: (data: M) => void) {
|
||||||
|
Reflect.apply(f, this.model, [this.model])
|
||||||
|
this.listeners.push(f)
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +0,0 @@
|
|||||||
export class ViewModel<T extends Object> {
|
|
||||||
data: T
|
|
||||||
constructor(obj: T) {
|
|
||||||
this.data = new Proxy(obj, {
|
|
||||||
get: () => {
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Reference in New Issue
Block a user