feat:render subview's change to apply listview's item change
This commit is contained in:
parent
4dcc89497d
commit
1dcdfff97d
@ -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,9 +61,12 @@ 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);
|
||||||
holder.listItemNode.setId(jsObject.getProperty("id").asString().value());
|
if (jsValue.isObject()) {
|
||||||
holder.listItemNode.blend(jsObject.getProperty("props").asObject(), holder.itemView.getLayoutParams());
|
JSObject jsObject = jsValue.asObject();
|
||||||
|
holder.listItemNode.setId(jsObject.getProperty("id").asString().value());
|
||||||
|
holder.listItemNode.blend(jsObject.getProperty("props").asObject(), holder.itemView.getLayoutParams());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -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,17 +110,50 @@ 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 (subNodeId.equals(jsObject.getProperty("id").asString().value())) {
|
if (jsValue.isObject()) {
|
||||||
for (String key : subProperties.propertySet()) {
|
JSObject jsObject = jsValue.asObject();
|
||||||
jsObject.setProperty(key, subProperties.getProperty(key));
|
if (subNodeId.equals(jsObject.getProperty("id").asString().value())) {
|
||||||
|
mixin(subProperties, jsObject);
|
||||||
|
int position = itemValues.keyAt(i);
|
||||||
|
notifyItemChanged(position);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
int position = itemObjects.keyAt(i);
|
}
|
||||||
notifyItemChanged(position);
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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,43 +10,62 @@ 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({
|
||||||
widthSpec: LayoutSpec.AT_MOST,
|
text: "ListDemo",
|
||||||
heightSpec: LayoutSpec.AT_MOST,
|
|
||||||
}
|
|
||||||
rootView.addChild(list)
|
|
||||||
list.itemCount = 1000
|
|
||||||
list.renderItem = (idx) => {
|
|
||||||
return listItem(text({
|
|
||||||
layoutConfig: {
|
layoutConfig: {
|
||||||
widthSpec: LayoutSpec.AT_MOST,
|
|
||||||
heightSpec: LayoutSpec.WRAP_CONTENT,
|
|
||||||
margin: {
|
|
||||||
left: 10,
|
|
||||||
right: 10,
|
|
||||||
top: 10,
|
|
||||||
bottom: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
text: `Cell At Line ${idx}`,
|
|
||||||
textAlignment: gravity().center(),
|
|
||||||
textColor: Color.parse("#ffffff"),
|
|
||||||
textSize: 20,
|
|
||||||
})).also(it => {
|
|
||||||
it.gravity = gravity().center()
|
|
||||||
it.bgColor = Color.parse(colors[idx % colors.length])
|
|
||||||
it.layoutConfig = {
|
|
||||||
widthSpec: LayoutSpec.AT_MOST,
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
heightSpec: LayoutSpec.EXACTLY,
|
heightSpec: LayoutSpec.EXACTLY,
|
||||||
}
|
},
|
||||||
it.height = 50
|
textSize: 30,
|
||||||
it.onClick = () => {
|
textColor: Color.parse("#535c68"),
|
||||||
log(`Click item at ${idx}`)
|
bgColor: Color.parse("#dff9fb"),
|
||||||
it.bgColor = Color.parse('#000000')
|
textAlignment: gravity().center(),
|
||||||
log(`changed,listview is dirty:${list.isDirty()}`)
|
height: 50,
|
||||||
}
|
}),
|
||||||
})
|
list({
|
||||||
}
|
itemCount: 1000,
|
||||||
|
renderItem: (idx: number) => {
|
||||||
|
return listItem(text({
|
||||||
|
layoutConfig: {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.WRAP_CONTENT,
|
||||||
|
margin: {
|
||||||
|
left: 10,
|
||||||
|
right: 10,
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
text: `Cell At Line ${idx}`,
|
||||||
|
textAlignment: gravity().center(),
|
||||||
|
textColor: Color.parse("#ffffff"),
|
||||||
|
textSize: 20,
|
||||||
|
})).also(it => {
|
||||||
|
it.gravity = gravity().center()
|
||||||
|
it.bgColor = Color.parse(colors[idx % colors.length])
|
||||||
|
it.layoutConfig = {
|
||||||
|
widthSpec: LayoutSpec.AT_MOST,
|
||||||
|
heightSpec: LayoutSpec.EXACTLY,
|
||||||
|
}
|
||||||
|
it.height = 50
|
||||||
|
it.onClick = () => {
|
||||||
|
log(`Click item at ${idx}`)
|
||||||
|
it.bgColor = Color.parse('#000000')
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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
|
||||||
|
}
|
@ -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() {
|
||||||
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user