diff --git a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java index 0f9871b2..51bb56b3 100644 --- a/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java +++ b/doric-android/doric/src/main/java/pub/doric/shader/flowlayout/FlowAdapter.java @@ -70,23 +70,20 @@ class FlowAdapter extends RecyclerView.Adapter { holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject()); } if ((this.flowLayoutNode.hasHeader() && position == 0) - || (this.flowLayoutNode.hasFooter() && position == this.getItemCount() - 1)) { + || (this.flowLayoutNode.hasFooter() && position == this.getItemCount() - 1) + || this.flowLayoutNode.loadMore + && position == this.itemCount + (this.flowLayoutNode.hasHeader() ? 1 : 0)) { 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 + } + if (this.flowLayoutNode.loadMore && position == this.itemCount + (this.flowLayoutNode.hasHeader() ? 1 : 0) && !TextUtils.isEmpty(this.flowLayoutNode.onLoadMoreFuncId)) { callLoadMore(); - StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - holder.itemView.getLayoutParams().height - ); - layoutParams.setFullSpan(true); - holder.itemView.setLayoutParams(layoutParams); } } diff --git a/doric-demo/src/FlowLayoutDemo.ts b/doric-demo/src/FlowLayoutDemo.ts index b3948b14..f8d3b343 100644 --- a/doric-demo/src/FlowLayoutDemo.ts +++ b/doric-demo/src/FlowLayoutDemo.ts @@ -43,7 +43,7 @@ class FlowDemo extends Panel { layoutConfig: layoutConfig().configWidth(LayoutSpec.MOST), }) }, - loadMore: false, + loadMore: true, onLoadMore: () => { setTimeout(() => { flowView.itemCount += 20 diff --git a/doric-iOS/Pod/Classes/Shader/DoricFlowLayoutNode.m b/doric-iOS/Pod/Classes/Shader/DoricFlowLayoutNode.m index 939cb4fa..7f57a7fe 100644 --- a/doric-iOS/Pod/Classes/Shader/DoricFlowLayoutNode.m +++ b/doric-iOS/Pod/Classes/Shader/DoricFlowLayoutNode.m @@ -28,6 +28,8 @@ - (CGFloat)doricFlowLayoutItemHeightAtIndexPath:(NSIndexPath *)indexPath; - (CGFloat)doricFlowLayoutItemWidthAtIndexPath:(NSIndexPath *)indexPath; +- (BOOL)doricFlowLayoutItemFullSpan:(NSIndexPath *)indexPath; + - (CGFloat)doricFlowLayoutColumnSpace; - (CGFloat)doricFlowLayoutRowSpace; @@ -92,7 +94,7 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSInde NSNumber *minYOfColumn = @(0); NSArray *keys = self.columnHeightInfo.allKeys; NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) { - return [obj1 intValue] <= [obj2 intValue] ? -1 : 1; + return ([obj1 intValue] <= [obj2 intValue] ? NSOrderedAscending : NSOrderedDescending); }]; for (NSNumber *key in sortedKeys) { @@ -100,17 +102,15 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSInde minYOfColumn = key; } } - CGFloat width = [self.delegate doricFlowLayoutItemWidthAtIndexPath:indexPath]; CGFloat height = [self.delegate doricFlowLayoutItemHeightAtIndexPath:indexPath]; - CGFloat x = (width + self.columnSpace) * [minYOfColumn integerValue]; + CGFloat x = 0; CGFloat y = [self.columnHeightInfo[minYOfColumn] floatValue]; if (y > 0) { y += self.rowSpace; } - if (width == self.collectionView.width) { - x = 0; + if ([self.delegate doricFlowLayoutItemFullSpan:indexPath]) { NSNumber *maxYColumn = @(0); for (NSNumber *key in sortedKeys) { if ([self.columnHeightInfo[key] floatValue] > [self.columnHeightInfo[maxYColumn] floatValue]) { @@ -119,8 +119,12 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSInde } CGFloat maxY = [self.columnHeightInfo[maxYColumn] floatValue]; y = maxY + self.rowSpace; - self.columnHeightInfo[maxYColumn] = @(y + height); + for (NSNumber *key in self.columnHeightInfo.allKeys) { + self.columnHeightInfo[key] = @(y + height); + } } else { + CGFloat columnWidth = (self.collectionView.width - (self.columnCount - 1) * self.columnSpace) / self.columnCount; + x = (columnWidth + self.columnSpace) * [minYOfColumn integerValue]; self.columnHeightInfo[minYOfColumn] = @(y + height); } UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; @@ -163,6 +167,8 @@ @interface DoricFlowLayoutNode () *didScrollBlocks; @property(nonatomic, copy) NSString *onScrollFuncId; @@ -201,6 +207,14 @@ - (UICollectionView *)build { }]; } +- (BOOL)hasHeader { + return self.headerViewId && self.headerViewId.length > 0; +} + +- (BOOL)hasFooter { + return self.footerViewId && self.footerViewId.length > 0; +} + - (void)blendView:(UICollectionView *)view forPropName:(NSString *)name propValue:(id)prop { if ([@"scrollable" isEqualToString:name]) { self.view.scrollEnabled = [prop boolValue]; @@ -240,14 +254,35 @@ - (void)blendView:(UICollectionView *)view forPropName:(NSString *)name propValu self.onScrollFuncId = prop; } else if ([@"onScrollEnd" isEqualToString:name]) { self.onScrollEndFuncId = prop; + } else if ([@"header" isEqualToString:name]) { + self.headerViewId = prop; + } else if ([@"footer" isEqualToString:name]) { + self.footerViewId = prop; } else { [super blendView:view forPropName:name propValue:prop]; } } - (NSDictionary *)itemModelAt:(NSUInteger)position { - if (position >= self.itemCount) { - return [self subModelOf:self.loadMoreViewId]; + if (self.hasHeader && position == 0) { + return [self subModelOf:self.headerViewId]; + } + if (self.hasFooter && position == self.itemCount + + (self.loadMore ? 1 : 0) + + (self.hasHeader ? 1 : 0) + + (self.hasFooter ? 1 : 0) + - 1) { + return [self subModelOf:self.footerViewId]; + } + if (self.loadMore && position >= self.itemCount + (self.hasHeader ? 1 : 0)) { + if (self.loadMoreViewId && self.loadMoreViewId.length > 0) { + return [self subModelOf:self.loadMoreViewId]; + } else { + return nil; + } + } + if (self.hasHeader) { + position--; } NSString *viewId = self.itemViewIds[@(position)]; if (viewId && viewId.length > 0) { @@ -331,7 +366,7 @@ - (void)callLoadMore { } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return self.itemCount + (self.loadMore ? 1 : 0); + return self.itemCount + (self.loadMore ? 1 : 0) + (self.hasHeader ? 1 : 0) + (self.hasFooter ? 1 : 0); } - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { @@ -339,7 +374,18 @@ - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collection NSDictionary *model = [self itemModelAt:position]; NSDictionary *props = model[@"props"]; NSString *identifier = props[@"identifier"] ?: @"doricCell"; - if (position >= self.itemCount && self.onLoadMoreFuncId) { + if (self.hasHeader && position == 0) { + identifier = @"doricHeaderCell"; + } else if (self.hasFooter + && position == self.itemCount + + (self.loadMore ? 1 : 0) + + (self.hasHeader ? 1 : 0) + + (self.hasFooter ? 1 : 0) + - 1) { + identifier = @"doricFooterCell"; + } else if (self.loadMore + && position == self.itemCount + (self.hasHeader ? 1 : 0) + && self.onLoadMoreFuncId) { identifier = @"doricLoadMoreCell"; [self callLoadMore]; } @@ -355,7 +401,16 @@ - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collection DoricFlowLayoutItemNode *node = cell.viewNode; node.viewId = model[@"id"]; [node blend:props]; - if (position >= self.itemCount) { + BOOL fillWidth = (self.hasHeader && position == 0) + || (self.hasFooter + && position == self.itemCount + + (self.loadMore ? 1 : 0) + + (self.hasHeader ? 1 : 0) + + (self.hasFooter ? 1 : 0) + - 1) + || (self.loadMore + && position == self.itemCount + (self.hasHeader ? 1 : 0)); + if (fillWidth) { node.view.width = collectionView.width; } else { node.view.width = (collectionView.width - (self.columnCount - 1) * self.columnSpace) / self.columnCount; @@ -399,6 +454,19 @@ - (NSInteger)doricFlowLayoutColumnCount { return self.columnCount; } +- (BOOL)doricFlowLayoutItemFullSpan:(NSIndexPath *)indexPath { + NSUInteger position = (NSUInteger) indexPath.row; + return (self.hasHeader && position == 0) + || (self.hasFooter + && position == self.itemCount + + (self.loadMore ? 1 : 0) + + (self.hasHeader ? 1 : 0) + + (self.hasFooter ? 1 : 0) + - 1) + || (self.loadMore + && position == self.itemCount + (self.hasHeader ? 1 : 0)); +} + - (void)scrollViewDidScroll:(UIScrollView *)scrollView { for (DoricDidScrollBlock block in self.didScrollBlocks) { block(scrollView); diff --git a/doric-iOS/Pod/Classes/Shader/DoricListNode.m b/doric-iOS/Pod/Classes/Shader/DoricListNode.m index 35b55d78..c5d15fc6 100644 --- a/doric-iOS/Pod/Classes/Shader/DoricListNode.m +++ b/doric-iOS/Pod/Classes/Shader/DoricListNode.m @@ -168,7 +168,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N && position == self.itemCount + (self.loadMore ? 1 : 0) + (self.hasHeader ? 1 : 0) - + (self.hasFooter ? 1 : 0)) { + + (self.hasFooter ? 1 : 0) + - 1) { reuseId = @"doricFooterCell"; } else if (self.loadMore && position == self.itemCount + (self.hasHeader ? 1 : 0) @@ -269,8 +270,12 @@ - (NSDictionary *)itemModelAt:(NSUInteger)position { - 1) { return [self subModelOf:self.footerViewId]; } - if (position >= self.itemCount + (self.hasHeader ? 1 : 0)) { - return [self subModelOf:self.loadMoreViewId]; + if (self.loadMore && position >= self.itemCount + (self.hasHeader ? 1 : 0)) { + if (self.loadMoreViewId && self.loadMoreViewId.length > 0) { + return [self subModelOf:self.loadMoreViewId]; + } else { + return nil; + } } if (self.hasHeader) { position--; diff --git a/doric-js/index.d.ts b/doric-js/index.d.ts index 781e4206..a62c04a7 100644 --- a/doric-js/index.d.ts +++ b/doric-js/index.d.ts @@ -143,6 +143,11 @@ declare module 'doric/lib/src/ui/panel' { onShow(): void; onHidden(): void; onEnvChanged(): void; + /** + * Build view of the current Panel + * This could be called any times at any time when necessary. + * @param rootView root view of this panel + */ abstract build(rootView: Group): void; addHeadView(type: string, v: View): void; allHeadViews(): IterableIterator>; diff --git a/doric-js/lib/src/ui/panel.d.ts b/doric-js/lib/src/ui/panel.d.ts index f746a70f..19466c1e 100644 --- a/doric-js/lib/src/ui/panel.d.ts +++ b/doric-js/lib/src/ui/panel.d.ts @@ -10,6 +10,11 @@ export declare abstract class Panel { onShow(): void; onHidden(): void; onEnvChanged(): void; + /** + * Build view of the current Panel + * This could be called any times at any time when necessary. + * @param rootView root view of this panel + */ abstract build(rootView: Group): void; private __data__?; private __root__; diff --git a/doric-js/src/ui/panel.ts b/doric-js/src/ui/panel.ts index ee0faa1e..4c11bc29 100644 --- a/doric-js/src/ui/panel.ts +++ b/doric-js/src/ui/panel.ts @@ -43,6 +43,11 @@ export abstract class Panel { this.__root__.children.length = 0 this.build(this.__root__) } + /** + * Build view of the current Panel + * This could be called any times at any time when necessary. + * @param rootView root view of this panel + */ abstract build(rootView: Group): void private __data__?: object