feat:render subview's change to apply listview's item change

This commit is contained in:
pengfei.zhou 2019-11-14 14:42:31 +08:00
parent 4dcc89497d
commit 1dcdfff97d
8 changed files with 165 additions and 73 deletions

View File

@ -22,12 +22,13 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.github.pengfeizhou.jscore.JSArray;
import com.github.pengfeizhou.jscore.JSDecoder; import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JSObject; import com.github.pengfeizhou.jscore.JSObject;
import com.github.pengfeizhou.jscore.JSValue; import com.github.pengfeizhou.jscore.JSValue;
import java.util.HashMap;
import java.util.Map; import org.json.JSONObject;
import pub.doric.async.AsyncResult; import pub.doric.async.AsyncResult;
import pub.doric.shader.ViewNode; import pub.doric.shader.ViewNode;
@ -39,12 +40,12 @@ import pub.doric.shader.ViewNode;
*/ */
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> { public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
private final ListNode listNode; final ListNode listNode;
String renderItemFuncId; String renderItemFuncId;
private final String renderBunchedItemsFuncId = "renderBunchedItems"; final String renderBunchedItemsFuncId = "renderBunchedItems";
int itemCount = 0; int itemCount = 0;
int batchCount = 15; int batchCount = 15;
private SparseArray<JSObject> itemObjects = new SparseArray<>(); SparseArray<JSValue> itemValues = new SparseArray<>();
public ListAdapter(ListNode listNode) { public ListAdapter(ListNode listNode) {
this.listNode = listNode; this.listNode = listNode;
@ -60,10 +61,13 @@ public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolde
@Override @Override
public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) { public void onBindViewHolder(@NonNull DoricViewHolder holder, int position) {
JSObject jsObject = getItemModel(position); JSValue jsValue = getItemModel(position);
if (jsValue.isObject()) {
JSObject jsObject = jsValue.asObject();
holder.listItemNode.setId(jsObject.getProperty("id").asString().value()); holder.listItemNode.setId(jsObject.getProperty("id").asString().value());
holder.listItemNode.blend(jsObject.getProperty("props").asObject(), holder.itemView.getLayoutParams()); holder.listItemNode.blend(jsObject.getProperty("props").asObject(), holder.itemView.getLayoutParams());
} }
}
@Override @Override
public int getItemCount() { public int getItemCount() {
@ -72,16 +76,18 @@ public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolde
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
JSValue value = getItemModel(position).getProperty("identifier"); JSValue value = getItemModel(position);
if (value.isString()) { if (value.isObject()) {
return value.asString().hashCode(); if (value.asObject().getProperty("identifier").isString()) {
return value.asObject().getProperty("identifier").asString().value().hashCode();
}
} }
return super.getItemViewType(position); return super.getItemViewType(position);
} }
private JSObject getItemModel(int position) { private JSValue getItemModel(int position) {
JSObject itemModel = itemObjects.get(position); JSValue itemModel = itemValues.get(position);
if (itemModel == null) { if (itemModel == null || !itemModel.isObject()) {
AsyncResult<JSDecoder> asyncResult = listNode.callJSResponse( AsyncResult<JSDecoder> asyncResult = listNode.callJSResponse(
renderBunchedItemsFuncId, renderBunchedItemsFuncId,
position, position,
@ -90,11 +96,11 @@ public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolde
JSDecoder jsDecoder = asyncResult.synchronous().get(); JSDecoder jsDecoder = asyncResult.synchronous().get();
JSValue result = jsDecoder.decode(); JSValue result = jsDecoder.decode();
if (result.isArray()) { if (result.isArray()) {
JSValue[] values = result.asArray().toArray(); JSArray jsArray = result.asArray();
for (int i = 0; i < values.length; i++) { for (int i = 0; i < jsArray.size(); i++) {
itemObjects.put(i + position, values[i].asObject()); itemValues.put(i + position, jsArray.get(i));
} }
return values[0].asObject(); return itemValues.get(position);
} }
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@ -104,20 +110,53 @@ public class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolde
} }
public void blendSubNode(JSObject subProperties) { void blendSubNode(JSObject subProperties) {
String subNodeId = subProperties.getProperty("id").asString().value(); String subNodeId = subProperties.getProperty("id").asString().value();
for (int i = 0; i < itemObjects.size(); i++) { for (int i = 0; i < itemValues.size(); i++) {
JSObject jsObject = itemObjects.valueAt(i); JSValue jsValue = itemValues.valueAt(i);
if (jsValue.isObject()) {
JSObject jsObject = jsValue.asObject();
if (subNodeId.equals(jsObject.getProperty("id").asString().value())) { if (subNodeId.equals(jsObject.getProperty("id").asString().value())) {
for (String key : subProperties.propertySet()) { mixin(subProperties, jsObject);
jsObject.setProperty(key, subProperties.getProperty(key)); int position = itemValues.keyAt(i);
}
int position = itemObjects.keyAt(i);
notifyItemChanged(position); notifyItemChanged(position);
break; break;
} }
} }
} }
}
private void mixin(JSObject src, JSObject target) {
JSValue srcProps = src.getProperty("props");
JSValue targetProps = target.getProperty("props");
if (srcProps.isObject()) {
if (targetProps.isObject()) {
for (String key : srcProps.asObject().propertySet()) {
JSValue jsValue = srcProps.asObject().getProperty(key);
if ("children".equals(key) && jsValue.isArray()) {
JSValue targetChildren = targetProps.asObject().getProperty("children");
if (targetChildren.isArray() && targetChildren.asArray().size() == jsValue.asArray().size()) {
for (int i = 0; i < jsValue.asArray().size(); i++) {
JSValue childSrc = jsValue.asArray().get(i);
JSValue childTarget = targetChildren.asArray().get(i);
if (childSrc.isObject()) {
if (childTarget.isObject()) {
mixin(childSrc.asObject(), childTarget.asObject());
} else {
targetChildren.asArray().put(i, childSrc);
}
}
}
}
continue;
}
targetProps.asObject().setProperty(key, jsValue);
}
} else {
target.setProperty("props", srcProps);
}
}
}
static class DoricViewHolder extends RecyclerView.ViewHolder { static class DoricViewHolder extends RecyclerView.ViewHolder {

View File

@ -76,6 +76,8 @@ public class ListNode extends SuperNode<RecyclerView> {
break; break;
case "renderItem": case "renderItem":
this.listAdapter.renderItemFuncId = prop.asString().value(); this.listAdapter.renderItemFuncId = prop.asString().value();
// If reset renderItem,should reset native cache.
this.listAdapter.itemValues.clear();
break; break;
case "batchCount": case "batchCount":
this.listAdapter.batchCount = 15; this.listAdapter.batchCount = 15;

View File

@ -1,4 +1,4 @@
import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, ListItem, NativeCall, listItem, log } from "doric"; import { Group, Panel, List, text, gravity, Color, Stack, LayoutSpec, list, NativeCall, listItem, log, vlayout } from "doric";
const colors = [ const colors = [
"#f0932b", "#f0932b",
"#eb4d4b", "#eb4d4b",
@ -10,14 +10,22 @@ const colors = [
@Entry @Entry
class ListPanel extends Panel { class ListPanel extends Panel {
build(rootView: Group): void { build(rootView: Group): void {
const list = new List rootView.addChild(vlayout([
list.layoutConfig = { text({
text: "ListDemo",
layoutConfig: {
widthSpec: LayoutSpec.AT_MOST, widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.AT_MOST, heightSpec: LayoutSpec.EXACTLY,
} },
rootView.addChild(list) textSize: 30,
list.itemCount = 1000 textColor: Color.parse("#535c68"),
list.renderItem = (idx) => { bgColor: Color.parse("#dff9fb"),
textAlignment: gravity().center(),
height: 50,
}),
list({
itemCount: 1000,
renderItem: (idx: number) => {
return listItem(text({ return listItem(text({
layoutConfig: { layoutConfig: {
widthSpec: LayoutSpec.AT_MOST, widthSpec: LayoutSpec.AT_MOST,
@ -44,9 +52,20 @@ class ListPanel extends Panel {
it.onClick = () => { it.onClick = () => {
log(`Click item at ${idx}`) log(`Click item at ${idx}`)
it.bgColor = Color.parse('#000000') it.bgColor = Color.parse('#000000')
log(`changed,listview is dirty:${list.isDirty()}`) log(`bgcolor is ${Color.parse('#000000').toModel()}`)
} }
}) })
},
layoutConfig: {
widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.AT_MOST,
},
}),
]).also(it => {
it.layoutConfig = {
widthSpec: LayoutSpec.AT_MOST,
heightSpec: LayoutSpec.AT_MOST,
} }
}))
} }
} }

View File

@ -13,9 +13,10 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { View, } from './view' import { View, LayoutSpec } from './view'
import { Stack, HLayout, VLayout } from './layout' import { Stack, HLayout, VLayout } from './layout'
import { IText, IImage, Text, Image } from './widgets' import { IText, IImage, Text, Image } from './widgets'
import { IList, List } from './listview'
export function text(config: IText) { export function text(config: IText) {
const ret = new Text const ret = new Text
@ -35,6 +36,10 @@ export function image(config: IImage) {
export function stack(views: View[]) { export function stack(views: View[]) {
const ret = new Stack const ret = new Stack
ret.layoutConfig = {
widthSpec: LayoutSpec.WRAP_CONTENT,
heightSpec: LayoutSpec.WRAP_CONTENT,
}
for (let v of views) { for (let v of views) {
ret.addChild(v) ret.addChild(v)
} }
@ -43,6 +48,10 @@ export function stack(views: View[]) {
export function hlayout(views: View[]) { export function hlayout(views: View[]) {
const ret = new HLayout const ret = new HLayout
ret.layoutConfig = {
widthSpec: LayoutSpec.WRAP_CONTENT,
heightSpec: LayoutSpec.WRAP_CONTENT,
}
for (let v of views) { for (let v of views) {
ret.addChild(v) ret.addChild(v)
} }
@ -51,8 +60,20 @@ export function hlayout(views: View[]) {
export function vlayout(views: View[]) { export function vlayout(views: View[]) {
const ret = new VLayout const ret = new VLayout
ret.layoutConfig = {
widthSpec: LayoutSpec.WRAP_CONTENT,
heightSpec: LayoutSpec.WRAP_CONTENT,
}
for (let v of views) { for (let v of views) {
ret.addChild(v) ret.addChild(v)
} }
return ret return ret
} }
export function list(config: IList) {
const ret = new List
for (let key in config) {
Reflect.set(ret, key, Reflect.get(config, key, config), ret)
}
return ret
}

View File

@ -14,11 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import { View, Property, LayoutSpec, Superview } from "./view"; import { View, Property, LayoutSpec, Superview, IView } from "./view";
import { Model } from "../util/types";
import { O_TRUNC } from "constants";
import { Stack } from "./layout"; import { Stack } from "./layout";
import { loge } from "../util/log";
export function listItem(item: View) { export function listItem(item: View) {
return (new ListItem).also((it) => { return (new ListItem).also((it) => {
it.layoutConfig = { it.layoutConfig = {
@ -37,7 +35,13 @@ export class ListItem extends Stack {
identifier?: string identifier?: string
} }
export class List extends Superview { export interface IList extends IView {
renderItem: (index: number) => ListItem
itemCount: number
batchCount?: number
}
export class List extends Superview implements IList {
private cachedViews: Map<string, ListItem> = new Map private cachedViews: Map<string, ListItem> = new Map
allSubviews() { allSubviews() {

View File

@ -15,7 +15,7 @@
*/ */
import './../runtime/global' import './../runtime/global'
import { View, Group } from "./view"; import { View, Group } from "./view";
import { loge, log } from '../util/log'; import { loge } from '../util/log';
import { Model } from '../util/types'; import { Model } from '../util/types';
import { Root } from './layout'; import { Root } from './layout';
@ -113,13 +113,13 @@ export abstract class Panel {
} }
private hookBeforeNativeCall() { private hookBeforeNativeCall() {
this.__root__.clean()
} }
private hookAfterNativeCall() { private hookAfterNativeCall() {
if (this.__root__.isDirty()) { if (this.__root__.isDirty()) {
const model = this.__root__.toModel() const model = this.__root__.toModel()
this.nativeRender(model) this.nativeRender(model)
this.__root__.clean()
} }
} }

View File

@ -278,6 +278,13 @@ export abstract class Superview extends View {
return false return false
} }
clean() {
for (let v of this.allSubviews()) {
v.clean()
}
super.clean()
}
toModel() { toModel() {
const subviews = [] const subviews = []
for (let v of this.allSubviews()) { for (let v of this.allSubviews()) {
@ -315,7 +322,7 @@ export abstract class Group extends Superview {
return e.toModel() return e.toModel()
} else { } else {
//Dont need return model //Dont need return model
return {} return undefined
} }
}) })
return this.nativeViewModel return this.nativeViewModel

View File

@ -35,7 +35,7 @@ export function obj2Model(obj: Model): Model {
} }
} }
type _M = string | number | boolean | Modeling | { [index: string]: Model | undefined } type _M = string | number | boolean | Modeling | { [index: string]: Model } | undefined
export type Model = _M | Array<_M> export type Model = _M | Array<_M>
export type Binder<T> = (v: T) => void export type Binder<T> = (v: T) => void