From 43fd9fedd181b0414980362aa8add6a5f06f68c5 Mon Sep 17 00:00:00 2001 From: "pengfei.zhou" Date: Wed, 24 Jul 2019 16:38:34 +0800 Subject: [PATCH] implent mvvm --- .../penfeizhou/doricdemo/MainActivity.java | 5 +- .../penfeizhou/doric/shader/LinearNode.java | 5 ++ js-framework/demo.ts | 87 +++++++++---------- js-framework/index.ts | 1 + js-framework/src/ui/view.ts | 4 + js-framework/src/vm/mvvm.ts | 65 ++++++++++++++ js-framework/src/vm/viewmodel.ts | 11 --- 7 files changed, 119 insertions(+), 59 deletions(-) create mode 100644 js-framework/src/vm/mvvm.ts delete mode 100644 js-framework/src/vm/viewmodel.ts diff --git a/Android/app/src/main/java/com/github/penfeizhou/doricdemo/MainActivity.java b/Android/app/src/main/java/com/github/penfeizhou/doricdemo/MainActivity.java index 31ce23db..cfa240c0 100644 --- a/Android/app/src/main/java/com/github/penfeizhou/doricdemo/MainActivity.java +++ b/Android/app/src/main/java/com/github/penfeizhou/doricdemo/MainActivity.java @@ -2,6 +2,7 @@ package com.github.penfeizhou.doricdemo; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; +import android.view.ViewGroup; import android.widget.FrameLayout; import com.github.penfeizhou.doric.DoricContext; @@ -17,8 +18,8 @@ public class MainActivity extends AppCompatActivity { setContentView(R.layout.activity_main); DoricContext doricContext = DoricContext.create(this, DoricUtils.readAssetFile("demo.js"), "demo"); doricContext.callEntity("__init__", new JSONBuilder() - .put("width", DoricUtils.px2dp(DoricUtils.getScreenWidth())) - .put("height", DoricUtils.px2dp(DoricUtils.getScreenHeight()))); + .put("width", ViewGroup.LayoutParams.MATCH_PARENT) + .put("height", ViewGroup.LayoutParams.MATCH_PARENT)); doricContext.callEntity("log"); doricContext.getRootNode().setRootView((FrameLayout) findViewById(R.id.root)); } diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/LinearNode.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/LinearNode.java index e3aedc5a..2cb0d857 100644 --- a/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/LinearNode.java +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/shader/LinearNode.java @@ -27,6 +27,11 @@ public class LinearNode extends GroupNode { } } + @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + @Override public LinearLayout build(JSObject jsObject) { return new LinearLayout(getContext()); diff --git a/js-framework/demo.ts b/js-framework/demo.ts index e7ed47e9..021b8da5 100644 --- a/js-framework/demo.ts +++ b/js-framework/demo.ts @@ -1,53 +1,48 @@ -import { Gravity, Mutable, NativeCall, Text, Color, VLayout, Panel, log, logw, loge, Group, Stack, } from "./index" -import { WRAP_CONTENT } from "./src/ui/view"; +import { VMPanel, View, ViewModel, WRAP_CONTENT, Gravity, Mutable, NativeCall, Text, Color, VLayout, Panel, log, logw, loge, Group, Stack, } from "./index" +import { CENTER } from "./src/util/gravity"; + +interface CountModel { + count: number +} + +class CounterVM extends ViewModel { + 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 -export class MyPage extends Panel { +class MyPage extends VMPanel{ - build(rootView: Group): void { - const state = Mutable.of(1) - 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) + createVM(): ViewModel { + return new CounterVM({ count: 0 }) } @NativeCall diff --git a/js-framework/index.ts b/js-framework/index.ts index d469f866..0854f492 100644 --- a/js-framework/index.ts +++ b/js-framework/index.ts @@ -4,4 +4,5 @@ export * from "./src/util/color" export * from './src/util/log' export * from './src/util/types' export * from './src/util/gravity' +export * from './src/vm/mvvm' export * from './src/runtime/global' \ No newline at end of file diff --git a/js-framework/src/ui/view.ts b/js-framework/src/ui/view.ts index bd4bf4e5..af7ffc48 100644 --- a/js-framework/src/ui/view.ts +++ b/js-framework/src/ui/view.ts @@ -252,6 +252,10 @@ export abstract class Group extends View { onChildPropertyChanged(child: View) { this.getDirtyChildrenModel()[this.children.indexOf(child)] = child.nativeViewModel + this.getDirtyChildrenModel().length = this.children.length + if (this.parent) { + this.parent.onChildPropertyChanged(this) + } } isDirty() { diff --git a/js-framework/src/vm/mvvm.ts b/js-framework/src/vm/mvvm.ts new file mode 100644 index 00000000..3fff9d90 --- /dev/null +++ b/js-framework/src/vm/mvvm.ts @@ -0,0 +1,65 @@ +import { View, Group } from "../ui/view"; +import { Panel } from "../ui/panel"; + + +function listen(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 extends Panel { + + private vm: ViewModel = this.createVM() + + abstract createVM(): ViewModel + + + getModel() { + return this.vm.getModel() + } + + getVM() { + return this.vm + } + + build(root: Group): void { + this.vm.build(root, this.vm.getModel()) + } +} + +export abstract class ViewModel { + 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) + } +} \ No newline at end of file diff --git a/js-framework/src/vm/viewmodel.ts b/js-framework/src/vm/viewmodel.ts deleted file mode 100644 index 74b28ed2..00000000 --- a/js-framework/src/vm/viewmodel.ts +++ /dev/null @@ -1,11 +0,0 @@ -export class ViewModel { - data: T - constructor(obj: T) { - this.data = new Proxy(obj, { - get: () => { - - } - }) - } - -} \ No newline at end of file