flowlayout add header and footer:js and android implement

This commit is contained in:
pengfei.zhou 2021-10-11 11:14:55 +08:00 committed by osborn
parent 5224be8f90
commit 5ad4f4b981
12 changed files with 208 additions and 32 deletions

View File

@ -39,7 +39,9 @@ import pub.doric.shader.ViewNode;
* @CreateDate: 2019-11-12 * @CreateDate: 2019-11-12
*/ */
class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> { class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> {
private static final int TYPE_LOAD_MORE = -1;
private static final int TYPE_HEADER = -2;
private static final int TYPE_FOOTER = -3;
private final FlowLayoutNode flowLayoutNode; private final FlowLayoutNode flowLayoutNode;
String renderItemFuncId; String renderItemFuncId;
int itemCount = 0; int itemCount = 0;
@ -67,7 +69,17 @@ class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> {
holder.flowLayoutItemNode.setId(jsObject.getProperty("id").asString().value()); holder.flowLayoutItemNode.setId(jsObject.getProperty("id").asString().value());
holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject()); holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject());
} }
if (position >= this.itemCount && !TextUtils.isEmpty(this.flowLayoutNode.onLoadMoreFuncId)) { if ((this.flowLayoutNode.hasHeader() && position == 0)
|| (this.flowLayoutNode.hasFooter() && position == this.getItemCount() - 1)) {
StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
holder.itemView.getLayoutParams().height
);
layoutParams.setFullSpan(true);
holder.itemView.setLayoutParams(layoutParams);
} else if (this.flowLayoutNode.loadMore
&& position == this.itemCount + (this.flowLayoutNode.hasHeader() ? 1 : 0)
&& !TextUtils.isEmpty(this.flowLayoutNode.onLoadMoreFuncId)) {
callLoadMore(); callLoadMore();
StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams( StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@ -80,16 +92,27 @@ class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> {
@Override @Override
public int getItemCount() { public int getItemCount() {
return this.itemCount + (this.flowLayoutNode.loadMore ? 1 : 0); return this.itemCount
+ (this.flowLayoutNode.loadMore ? 1 : 0)
+ (this.flowLayoutNode.hasHeader() ? 1 : 0)
+ (this.flowLayoutNode.hasFooter() ? 1 : 0);
} }
@Override @Override
public int getItemViewType(int position) { public int getItemViewType(int position) {
if (position >= itemCount) { if (this.flowLayoutNode.hasHeader() && position == 0) {
return Integer.MAX_VALUE; return TYPE_HEADER;
}
if (this.flowLayoutNode.hasFooter() && position == this.getItemCount() - 1) {
return TYPE_FOOTER;
}
if (position >= this.itemCount + (this.flowLayoutNode.hasHeader() ? 1 : 0)) {
return TYPE_LOAD_MORE;
} }
JSValue value = getItemModel(position); JSValue value = getItemModel(position);
if (value.isObject()) { if (value != null && value.isObject()) {
if (value.asObject().getProperty("identifier").isString()) { if (value.asObject().getProperty("identifier").isString()) {
return value.asObject().getProperty("identifier").asString().value().hashCode(); return value.asObject().getProperty("identifier").asString().value().hashCode();
} }
@ -97,10 +120,21 @@ class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> {
return super.getItemViewType(position); return super.getItemViewType(position);
} }
private JSValue getItemModel(final int position) { private JSValue getItemModel(int position) {
if (position >= this.itemCount) { if (this.flowLayoutNode.hasHeader() && position == 0) {
return this.flowLayoutNode.getSubModel(this.flowLayoutNode.headerViewId);
}
if (this.flowLayoutNode.hasFooter() && position == this.getItemCount() - 1) {
return this.flowLayoutNode.getSubModel(this.flowLayoutNode.footerViewId);
}
if (position >= this.itemCount + (this.flowLayoutNode.hasHeader() ? 1 : 0)) {
return this.flowLayoutNode.getSubModel(this.flowLayoutNode.loadMoreViewId); return this.flowLayoutNode.getSubModel(this.flowLayoutNode.loadMoreViewId);
} }
if (this.flowLayoutNode.hasHeader()) {
position--;
}
String id = itemValues.get(position); String id = itemValues.get(position);
if (TextUtils.isEmpty(id)) { if (TextUtils.isEmpty(id)) {
AsyncResult<JSDecoder> asyncResult = flowLayoutNode.pureCallJSResponse( AsyncResult<JSDecoder> asyncResult = flowLayoutNode.pureCallJSResponse(

View File

@ -99,6 +99,9 @@ public class FlowLayoutNode extends SuperNode<RecyclerView> implements IDoricScr
private int itemCount = 0; private int itemCount = 0;
private boolean scrollable = true; private boolean scrollable = true;
String headerViewId;
String footerViewId;
public FlowLayoutNode(DoricContext doricContext) { public FlowLayoutNode(DoricContext doricContext) {
super(doricContext); super(doricContext);
this.flowAdapter = new FlowAdapter(this); this.flowAdapter = new FlowAdapter(this);
@ -206,6 +209,12 @@ public class FlowLayoutNode extends SuperNode<RecyclerView> implements IDoricScr
} }
this.onScrollEndFuncId = prop.asString().value(); this.onScrollEndFuncId = prop.asString().value();
break; break;
case "header":
this.headerViewId = prop.asString().value();
break;
case "footer":
this.footerViewId = prop.asString().value();
break;
default: default:
super.blend(view, name, prop); super.blend(view, name, prop);
break; break;
@ -316,4 +325,12 @@ public class FlowLayoutNode extends SuperNode<RecyclerView> implements IDoricScr
public void removeScrollChangeListener(DoricScrollChangeListener listener) { public void removeScrollChangeListener(DoricScrollChangeListener listener) {
listeners.remove(listener); listeners.remove(listener);
} }
boolean hasHeader() {
return !TextUtils.isEmpty(this.headerViewId);
}
boolean hasFooter() {
return !TextUtils.isEmpty(this.footerViewId);
}
} }

View File

@ -1,4 +1,4 @@
import { Group, Panel, flowlayout, layoutConfig, FlowLayoutItem, text, Color, LayoutSpec, Gravity, flowItem } from "doric"; import { Group, Panel, flowlayout, layoutConfig, FlowLayoutItem, text, Color, LayoutSpec, Gravity, flowItem, image } from "doric";
import { colors, label } from "./utils"; import { colors, label } from "./utils";
const imageUrls = [ const imageUrls = [
@ -17,10 +17,18 @@ class FlowDemo extends Panel {
build(rootView: Group) { build(rootView: Group) {
const flowView = flowlayout({ const flowView = flowlayout({
layoutConfig: layoutConfig().most(), layoutConfig: layoutConfig().most(),
itemCount: 100, itemCount: 20,
columnCount: 3, columnCount: 3,
columnSpace: 10, columnSpace: 10,
rowSpace: 10, rowSpace: 10,
header: flowItem(image({
layoutConfig: layoutConfig().mostWidth().fitHeight(),
imageUrl: imageUrls[0]
})),
footer: flowItem(image({
layoutConfig: layoutConfig().mostWidth().fitHeight(),
imageUrl: imageUrls[1]
})),
renderItem: (idx) => { renderItem: (idx) => {
return flowItem( return flowItem(
text({ text({
@ -35,7 +43,7 @@ class FlowDemo extends Panel {
layoutConfig: layoutConfig().configWidth(LayoutSpec.MOST), layoutConfig: layoutConfig().configWidth(LayoutSpec.MOST),
}) })
}, },
loadMore: true, loadMore: false,
onLoadMore: () => { onLoadMore: () => {
setTimeout(() => { setTimeout(() => {
flowView.itemCount += 20 flowView.itemCount += 20

View File

@ -2944,12 +2944,17 @@ var FlowLayout = /** @class */ (function (_super) {
return _this; return _this;
} }
FlowLayout.prototype.allSubviews = function () { FlowLayout.prototype.allSubviews = function () {
var ret = __spreadArray([], __read(this.cachedViews.values()));
if (this.loadMoreView) { if (this.loadMoreView) {
return __spreadArray(__spreadArray([], __read(this.cachedViews.values())), [this.loadMoreView]); ret.push(this.loadMoreView);
} }
else { if (this.header) {
return this.cachedViews.values(); ret.push(this.header);
} }
if (this.footer) {
ret.push(this.footer);
}
return ret;
}; };
FlowLayout.prototype.reset = function () { FlowLayout.prototype.reset = function () {
this.cachedViews.clear(); this.cachedViews.clear();
@ -2972,6 +2977,12 @@ var FlowLayout = /** @class */ (function (_super) {
if (this.loadMoreView) { if (this.loadMoreView) {
this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId; this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId;
} }
if (this.header) {
this.dirtyProps['header'] = this.header.viewId;
}
if (this.footer) {
this.dirtyProps['footer'] = this.footer.viewId;
}
return _super.prototype.toModel.call(this); return _super.prototype.toModel.call(this);
}; };
__decorate$5([ __decorate$5([
@ -3026,6 +3037,14 @@ var FlowLayout = /** @class */ (function (_super) {
Property, Property,
__metadata$5("design:type", Boolean) __metadata$5("design:type", Boolean)
], FlowLayout.prototype, "bounces", void 0); ], FlowLayout.prototype, "bounces", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "header", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "footer", void 0);
return FlowLayout; return FlowLayout;
}(Superview)); }(Superview));
function flowlayout(config) { function flowlayout(config) {

View File

@ -2220,12 +2220,17 @@ class FlowLayout extends Superview {
this.batchCount = 15; this.batchCount = 15;
} }
allSubviews() { allSubviews() {
const ret = [...this.cachedViews.values()];
if (this.loadMoreView) { if (this.loadMoreView) {
return [...this.cachedViews.values(), this.loadMoreView]; ret.push(this.loadMoreView);
} }
else { if (this.header) {
return this.cachedViews.values(); ret.push(this.header);
} }
if (this.footer) {
ret.push(this.footer);
}
return ret;
} }
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
@ -2247,6 +2252,12 @@ class FlowLayout extends Superview {
if (this.loadMoreView) { if (this.loadMoreView) {
this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId; this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId;
} }
if (this.header) {
this.dirtyProps['header'] = this.header.viewId;
}
if (this.footer) {
this.dirtyProps['footer'] = this.footer.viewId;
}
return super.toModel(); return super.toModel();
} }
} }
@ -2302,6 +2313,14 @@ __decorate$5([
Property, Property,
__metadata$5("design:type", Boolean) __metadata$5("design:type", Boolean)
], FlowLayout.prototype, "bounces", void 0); ], FlowLayout.prototype, "bounces", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "header", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "footer", void 0);
function flowlayout(config) { function flowlayout(config) {
const ret = new FlowLayout; const ret = new FlowLayout;
for (let key in config) { for (let key in config) {

View File

@ -3741,12 +3741,17 @@ class FlowLayout extends Superview {
this.batchCount = 15; this.batchCount = 15;
} }
allSubviews() { allSubviews() {
const ret = [...this.cachedViews.values()];
if (this.loadMoreView) { if (this.loadMoreView) {
return [...this.cachedViews.values(), this.loadMoreView]; ret.push(this.loadMoreView);
} }
else { if (this.header) {
return this.cachedViews.values(); ret.push(this.header);
} }
if (this.footer) {
ret.push(this.footer);
}
return ret;
} }
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
@ -3768,6 +3773,12 @@ class FlowLayout extends Superview {
if (this.loadMoreView) { if (this.loadMoreView) {
this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId; this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId;
} }
if (this.header) {
this.dirtyProps['header'] = this.header.viewId;
}
if (this.footer) {
this.dirtyProps['footer'] = this.footer.viewId;
}
return super.toModel(); return super.toModel();
} }
} }
@ -3823,6 +3834,14 @@ __decorate$5([
Property, Property,
__metadata$5("design:type", Boolean) __metadata$5("design:type", Boolean)
], FlowLayout.prototype, "bounces", void 0); ], FlowLayout.prototype, "bounces", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "header", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "footer", void 0);
function flowlayout(config) { function flowlayout(config) {
const ret = new FlowLayout; const ret = new FlowLayout;
for (let key in config) { for (let key in config) {

4
doric-js/index.d.ts vendored
View File

@ -832,7 +832,7 @@ declare module 'doric/lib/src/widget/flowlayout' {
identifier?: string; identifier?: string;
} }
export class FlowLayout extends Superview { export class FlowLayout extends Superview {
allSubviews(): IterableIterator<FlowLayoutItem> | FlowLayoutItem[]; allSubviews(): FlowLayoutItem[];
columnCount: number; columnCount: number;
columnSpace?: number; columnSpace?: number;
rowSpace?: number; rowSpace?: number;
@ -855,6 +855,8 @@ declare module 'doric/lib/src/widget/flowlayout' {
* Take effect only on iOS * Take effect only on iOS
*/ */
bounces?: boolean; bounces?: boolean;
header?: FlowLayoutItem;
footer?: FlowLayoutItem;
reset(): void; reset(): void;
toModel(): NativeViewModel; toModel(): NativeViewModel;
} }

View File

@ -8,7 +8,7 @@ export declare class FlowLayoutItem extends Stack {
} }
export declare class FlowLayout extends Superview { export declare class FlowLayout extends Superview {
private cachedViews; private cachedViews;
allSubviews(): IterableIterator<FlowLayoutItem> | FlowLayoutItem[]; allSubviews(): FlowLayoutItem[];
columnCount: number; columnCount: number;
columnSpace?: number; columnSpace?: number;
rowSpace?: number; rowSpace?: number;
@ -31,6 +31,8 @@ export declare class FlowLayout extends Superview {
* Take effect only on iOS * Take effect only on iOS
*/ */
bounces?: boolean; bounces?: boolean;
header?: FlowLayoutItem;
footer?: FlowLayoutItem;
reset(): void; reset(): void;
private getItem; private getItem;
private renderBunchedItems; private renderBunchedItems;

View File

@ -40,12 +40,17 @@ export class FlowLayout extends Superview {
this.batchCount = 15; this.batchCount = 15;
} }
allSubviews() { allSubviews() {
const ret = [...this.cachedViews.values()];
if (this.loadMoreView) { if (this.loadMoreView) {
return [...this.cachedViews.values(), this.loadMoreView]; ret.push(this.loadMoreView);
} }
else { if (this.header) {
return this.cachedViews.values(); ret.push(this.header);
} }
if (this.footer) {
ret.push(this.footer);
}
return ret;
} }
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
@ -67,6 +72,12 @@ export class FlowLayout extends Superview {
if (this.loadMoreView) { if (this.loadMoreView) {
this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId; this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId;
} }
if (this.header) {
this.dirtyProps['header'] = this.header.viewId;
}
if (this.footer) {
this.dirtyProps['footer'] = this.footer.viewId;
}
return super.toModel(); return super.toModel();
} }
} }
@ -122,6 +133,14 @@ __decorate([
Property, Property,
__metadata("design:type", Boolean) __metadata("design:type", Boolean)
], FlowLayout.prototype, "bounces", void 0); ], FlowLayout.prototype, "bounces", void 0);
__decorate([
Property,
__metadata("design:type", FlowLayoutItem)
], FlowLayout.prototype, "header", void 0);
__decorate([
Property,
__metadata("design:type", FlowLayoutItem)
], FlowLayout.prototype, "footer", void 0);
export function flowlayout(config) { export function flowlayout(config) {
const ret = new FlowLayout; const ret = new FlowLayout;
for (let key in config) { for (let key in config) {

View File

@ -29,11 +29,17 @@ export class FlowLayout extends Superview {
private cachedViews: Map<string, FlowLayoutItem> = new Map private cachedViews: Map<string, FlowLayoutItem> = new Map
allSubviews() { allSubviews() {
const ret = [...this.cachedViews.values()]
if (this.loadMoreView) { if (this.loadMoreView) {
return [...this.cachedViews.values(), this.loadMoreView] ret.push(this.loadMoreView)
} else {
return this.cachedViews.values()
} }
if (this.header) {
ret.push(this.header)
}
if (this.footer) {
ret.push(this.footer)
}
return ret
} }
@Property @Property
@ -77,6 +83,12 @@ export class FlowLayout extends Superview {
@Property @Property
bounces?: boolean bounces?: boolean
@Property
header?: FlowLayoutItem
@Property
footer?: FlowLayoutItem
reset() { reset() {
this.cachedViews.clear() this.cachedViews.clear()
this.itemCount = 0 this.itemCount = 0
@ -98,6 +110,12 @@ export class FlowLayout extends Superview {
if (this.loadMoreView) { if (this.loadMoreView) {
this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId
} }
if (this.header) {
this.dirtyProps['header'] = this.header.viewId
}
if (this.footer) {
this.dirtyProps['footer'] = this.footer.viewId
}
return super.toModel() return super.toModel()
} }
} }

View File

@ -3795,12 +3795,17 @@ class FlowLayout extends Superview {
this.batchCount = 15; this.batchCount = 15;
} }
allSubviews() { allSubviews() {
const ret = [...this.cachedViews.values()];
if (this.loadMoreView) { if (this.loadMoreView) {
return [...this.cachedViews.values(), this.loadMoreView]; ret.push(this.loadMoreView);
} }
else { if (this.header) {
return this.cachedViews.values(); ret.push(this.header);
} }
if (this.footer) {
ret.push(this.footer);
}
return ret;
} }
reset() { reset() {
this.cachedViews.clear(); this.cachedViews.clear();
@ -3822,6 +3827,12 @@ class FlowLayout extends Superview {
if (this.loadMoreView) { if (this.loadMoreView) {
this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId; this.dirtyProps['loadMoreView'] = this.loadMoreView.viewId;
} }
if (this.header) {
this.dirtyProps['header'] = this.header.viewId;
}
if (this.footer) {
this.dirtyProps['footer'] = this.footer.viewId;
}
return super.toModel(); return super.toModel();
} }
} }
@ -3877,6 +3888,14 @@ __decorate$5([
Property, Property,
__metadata$5("design:type", Boolean) __metadata$5("design:type", Boolean)
], FlowLayout.prototype, "bounces", void 0); ], FlowLayout.prototype, "bounces", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "header", void 0);
__decorate$5([
Property,
__metadata$5("design:type", FlowLayoutItem)
], FlowLayout.prototype, "footer", void 0);
function flowlayout(config) { function flowlayout(config) {
const ret = new FlowLayout; const ret = new FlowLayout;
for (let key in config) { for (let key in config) {

File diff suppressed because one or more lines are too long