feat:Fix List call onLoadMore multi times

This commit is contained in:
pengfeizhou 2021-02-02 19:47:15 +08:00 committed by osborn
parent 438d7233ff
commit 112e45af06
5 changed files with 250 additions and 108 deletions

View File

@ -45,6 +45,7 @@ class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
} }
private int itemCount = 0; private int itemCount = 0;
private int loadAnchor = 0;
@NonNull @NonNull
@Override @Override
@ -62,8 +63,8 @@ class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
holder.listItemNode.setId(jsObject.getProperty("id").asString().value()); holder.listItemNode.setId(jsObject.getProperty("id").asString().value());
holder.listItemNode.blend(jsObject.getProperty("props").asObject()); holder.listItemNode.blend(jsObject.getProperty("props").asObject());
} }
if (position >= this.listNode.itemCount) { if (position >= this.listNode.itemCount && !TextUtils.isEmpty(this.listNode.onLoadMoreFuncId)) {
this.listNode.callJSResponse(this.listNode.onLoadMoreFuncId); callLoadMore();
} }
} }
@ -142,6 +143,13 @@ class ListAdapter extends RecyclerView.Adapter<ListAdapter.DoricViewHolder> {
} }
} }
private void callLoadMore() {
if (loadAnchor != itemCount) {
loadAnchor = itemCount;
this.listNode.callJSResponse(this.listNode.onLoadMoreFuncId);
}
}
static class DoricViewHolder extends RecyclerView.ViewHolder { static class DoricViewHolder extends RecyclerView.ViewHolder {
ListItemNode listItemNode; ListItemNode listItemNode;

View File

@ -16,7 +16,6 @@
package pub.doric.shader.list; package pub.doric.shader.list;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.View; import android.view.View;
@ -63,7 +62,6 @@ public class ListNode extends SuperNode<RecyclerView> implements IDoricScrollabl
private String onScrollFuncId; private String onScrollFuncId;
private String onScrollEndFuncId; private String onScrollEndFuncId;
private final DoricJSDispatcher jsDispatcher = new DoricJSDispatcher(); private final DoricJSDispatcher jsDispatcher = new DoricJSDispatcher();
public ListNode(DoricContext doricContext) { public ListNode(DoricContext doricContext) {
super(doricContext); super(doricContext);
this.listAdapter = new ListAdapter(this); this.listAdapter = new ListAdapter(this);

View File

@ -1,10 +1,37 @@
import { Group, Panel, List, text, gravity, Color, LayoutSpec, list, listItem, log, vlayout, Gravity, hlayout, Text, refreshable, Refreshable, ListItem, layoutConfig } from "doric"; import { Group, Panel, List, text, gravity, Color, LayoutSpec, list, listItem, log, vlayout, Gravity, hlayout, Text, refreshable, Refreshable, ListItem, layoutConfig, ViewHolder, ViewModel, VMPanel, loge } from "doric";
import { rotatedArrow, colors } from "./utils";
@Entry interface ItemModel {
class ListPanel extends Panel { text: string
build(rootView: Group): void { }
let refreshView: Refreshable
let offset = Math.ceil(Math.random() * colors.length) interface ListModel {
end: boolean
offset: number
data: ItemModel[]
}
async function loadData(offset: number): Promise<{
isEnd: boolean,
data: ItemModel[]
}> {
return new Promise<{
isEnd: boolean,
data: ItemModel[]
}>(resolve => {
setTimeout(() => {
resolve({
isEnd: offset > 100,
data: new Array(5).fill(offset).map((e, idx) => {
return { text: `Item: ${e + idx}` }
})
})
}, 1000)
})
}
class ListVH extends ViewHolder {
list!: List
build(root: Group) {
vlayout( vlayout(
[ [
text({ text({
@ -19,106 +46,83 @@ class ListPanel extends Panel {
textAlignment: gravity().center(), textAlignment: gravity().center(),
height: 50, height: 50,
}), }),
refreshView = refreshable({ this.list = list({
onRefresh: () => { itemCount: 0,
refreshView.setRefreshing(context, false).then(() => { layoutConfig: {
(refreshView.content as List).also(it => { widthSpec: LayoutSpec.MOST,
it.reset() heightSpec: LayoutSpec.JUST,
offset = Math.ceil(Math.random() * colors.length) weight: 1
it.itemCount = 40
it.loadMore = true
it.onLoadMore = () => {
setTimeout(() => {
it.itemCount += 10
}, 1000)
}
it.loadMoreView = listItem(text({
text: "Loading",
layoutConfig: layoutConfig().most().configHeight(LayoutSpec.JUST).configAlignment(Gravity.Center),
height: 50,
}))
it.renderItem = (idx: number) => {
let counter!: Text
return listItem(
hlayout([
text({
layoutConfig: {
widthSpec: LayoutSpec.FIT,
heightSpec: LayoutSpec.JUST,
alignment: gravity().center(),
},
text: `Cell At Line ${idx}`,
textAlignment: gravity().center(),
textColor: Color.parse("#ffffff"),
textSize: 20,
height: 50,
}),
text({
textColor: Color.parse("#ffffff"),
textSize: 20,
text: "",
}).also(it => {
counter = it
it.layoutConfig = {
widthSpec: LayoutSpec.FIT,
heightSpec: LayoutSpec.FIT,
margin: {
left: 10,
}
}
})
]).also(it => {
it.layoutConfig = {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.FIT,
margin: {
bottom: 2,
}
}
it.gravity = gravity().center()
it.backgroundColor = colors[(idx + offset) % colors.length]
let clicked = 0
it.onClick = () => {
counter.text = `Item Clicked ${++clicked}`
}
})
).also(it => {
it.layoutConfig = {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.FIT,
}
it.onClick = () => {
log(`Click item at ${idx}`)
it.height += 10
it.nativeChannel(context, "getWidth")().then(
resolve => {
log(`resolve,${resolve}`)
},
reject => {
log(`reject,${reject}`)
})
}
})
}
})
})
}, },
header: rotatedArrow(), })
content: list({
itemCount: 0,
renderItem: () => new ListItem,
layoutConfig: {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.MOST,
},
}),
}),
], ],
{ {
layoutConfig: layoutConfig().most(), layoutConfig: layoutConfig().most(),
backgroundColor: Color.WHITE backgroundColor: Color.WHITE
}).in(rootView) }).in(root)
refreshView.backgroundColor = Color.YELLOW }
}
class ListVM extends ViewModel<ListModel, ListVH> {
onAttached(state: ListModel, vh: ListVH) {
vh.list.apply({
renderItem: (index) => {
const data = state.data[index]
return listItem(text({
text: data.text,
textSize: 20,
layoutConfig: {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.JUST,
},
height: 50
}), {
layoutConfig: {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.FIT,
}
})
},
onLoadMore: async () => {
loge(`LoadMore,offset:${state.offset}`)
const ret = await loadData(state.offset)
this.updateState(state => {
state.end = ret.isEnd
state.data = state.data.concat(ret.data)
state.offset = state.data.length
})
}
})
loadData(state.offset).then(ret => {
this.updateState(state => {
state.end = ret.isEnd
state.data = state.data.concat(ret.data)
state.offset = state.data.length
})
})
}
onBind(state: ListModel, vh: ListVH) {
vh.list.apply({
itemCount: state.data.length,
loadMore: !state.end
})
loge(`onBind,itemCount:${vh.list.itemCount}`)
}
}
@Entry
class ListPanel extends VMPanel<ListModel, ListVH> {
getViewModelClass() {
return ListVM
}
getViewHolderClass() {
return ListVH
}
getState() {
return {
end: true,
data: [],
offset: 0
}
} }
} }

View File

@ -0,0 +1,124 @@
import { Group, Panel, List, text, gravity, Color, LayoutSpec, list, listItem, log, vlayout, Gravity, hlayout, Text, refreshable, Refreshable, ListItem, layoutConfig } from "doric";
import { rotatedArrow, colors } from "./utils";
@Entry
class ListPanel extends Panel {
build(rootView: Group): void {
let refreshView: Refreshable
let offset = Math.ceil(Math.random() * colors.length)
vlayout(
[
text({
text: "ListDemo",
layoutConfig: {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.JUST,
},
textSize: 30,
textColor: Color.parse("#535c68"),
backgroundColor: Color.parse("#dff9fb"),
textAlignment: gravity().center(),
height: 50,
}),
refreshView = refreshable({
onRefresh: () => {
refreshView.setRefreshing(context, false).then(() => {
(refreshView.content as List).also(it => {
it.reset()
offset = Math.ceil(Math.random() * colors.length)
it.itemCount = 40
it.loadMore = true
it.onLoadMore = () => {
setTimeout(() => {
it.itemCount += 10
}, 1000)
}
it.loadMoreView = listItem(text({
text: "Loading",
layoutConfig: layoutConfig().most().configHeight(LayoutSpec.JUST).configAlignment(Gravity.Center),
height: 50,
}))
it.renderItem = (idx: number) => {
let counter!: Text
return listItem(
hlayout([
text({
layoutConfig: {
widthSpec: LayoutSpec.FIT,
heightSpec: LayoutSpec.JUST,
alignment: gravity().center(),
},
text: `Cell At Line ${idx}`,
textAlignment: gravity().center(),
textColor: Color.parse("#ffffff"),
textSize: 20,
height: 50,
}),
text({
textColor: Color.parse("#ffffff"),
textSize: 20,
text: "",
}).also(it => {
counter = it
it.layoutConfig = {
widthSpec: LayoutSpec.FIT,
heightSpec: LayoutSpec.FIT,
margin: {
left: 10,
}
}
})
]).also(it => {
it.layoutConfig = {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.FIT,
margin: {
bottom: 2,
}
}
it.gravity = gravity().center()
it.backgroundColor = colors[(idx + offset) % colors.length]
let clicked = 0
it.onClick = () => {
counter.text = `Item Clicked ${++clicked}`
}
})
).also(it => {
it.layoutConfig = {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.FIT,
}
it.onClick = () => {
log(`Click item at ${idx}`)
it.height += 10
it.nativeChannel(context, "getWidth")().then(
resolve => {
log(`resolve,${resolve}`)
},
reject => {
log(`reject,${reject}`)
})
}
})
}
})
})
},
header: rotatedArrow(),
content: list({
itemCount: 0,
renderItem: () => new ListItem,
layoutConfig: {
widthSpec: LayoutSpec.MOST,
heightSpec: LayoutSpec.MOST,
},
}),
}),
],
{
layoutConfig: layoutConfig().most(),
backgroundColor: Color.WHITE
}).in(rootView)
refreshView.backgroundColor = Color.YELLOW
}
}

View File

@ -47,6 +47,7 @@ @interface DoricListNode () <UITableViewDataSource, UITableViewDelegate>
@property(nonatomic, copy) NSString *renderItemFuncId; @property(nonatomic, copy) NSString *renderItemFuncId;
@property(nonatomic, copy) NSString *loadMoreViewId; @property(nonatomic, copy) NSString *loadMoreViewId;
@property(nonatomic, assign) BOOL loadMore; @property(nonatomic, assign) BOOL loadMore;
@property(nonatomic, assign) NSUInteger loadAnchor;
@property(nonatomic, strong) NSMutableSet <DoricDidScrollBlock> *didScrollBlocks; @property(nonatomic, strong) NSMutableSet <DoricDidScrollBlock> *didScrollBlocks;
@property(nonatomic, copy) NSString *onScrollFuncId; @property(nonatomic, copy) NSString *onScrollFuncId;
@property(nonatomic, copy) NSString *onScrollEndFuncId; @property(nonatomic, copy) NSString *onScrollEndFuncId;
@ -124,6 +125,13 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger
return self.itemCount + (self.loadMore ? 1 : 0); return self.itemCount + (self.loadMore ? 1 : 0);
} }
- (void)callLoadMore {
if (self.itemCount != self.loadAnchor) {
self.loadAnchor = self.itemCount;
[self callJSResponse:self.onLoadMoreFuncId, nil];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger position = (NSUInteger) indexPath.row; NSUInteger position = (NSUInteger) indexPath.row;
NSDictionary *model = [self itemModelAt:position]; NSDictionary *model = [self itemModelAt:position];
@ -131,7 +139,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
NSString *reuseId = props[@"identifier"]; NSString *reuseId = props[@"identifier"];
if (position > 0 && position >= self.itemCount && self.onLoadMoreFuncId) { if (position > 0 && position >= self.itemCount && self.onLoadMoreFuncId) {
reuseId = @"doricLoadMoreCell"; reuseId = @"doricLoadMoreCell";
[self callJSResponse:self.onLoadMoreFuncId, nil]; [self callLoadMore];
} }
DoricTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId ?: @"doriccell"]; DoricTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseId ?: @"doriccell"];
if (!cell) { if (!cell) {