flowlayout add header and footer:iOS implement

This commit is contained in:
pengfei.zhou 2021-10-11 15:58:46 +08:00 committed by osborn
parent 0dbab344d9
commit 9429c75834
7 changed files with 108 additions and 23 deletions

View File

@ -70,23 +70,20 @@ class FlowAdapter extends RecyclerView.Adapter<FlowAdapter.DoricViewHolder> {
holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject()); holder.flowLayoutItemNode.blend(jsObject.getProperty("props").asObject());
} }
if ((this.flowLayoutNode.hasHeader() && position == 0) 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( StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
holder.itemView.getLayoutParams().height holder.itemView.getLayoutParams().height
); );
layoutParams.setFullSpan(true); layoutParams.setFullSpan(true);
holder.itemView.setLayoutParams(layoutParams); holder.itemView.setLayoutParams(layoutParams);
} else if (this.flowLayoutNode.loadMore }
if (this.flowLayoutNode.loadMore
&& position == this.itemCount + (this.flowLayoutNode.hasHeader() ? 1 : 0) && position == this.itemCount + (this.flowLayoutNode.hasHeader() ? 1 : 0)
&& !TextUtils.isEmpty(this.flowLayoutNode.onLoadMoreFuncId)) { && !TextUtils.isEmpty(this.flowLayoutNode.onLoadMoreFuncId)) {
callLoadMore(); callLoadMore();
StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
holder.itemView.getLayoutParams().height
);
layoutParams.setFullSpan(true);
holder.itemView.setLayoutParams(layoutParams);
} }
} }

View File

@ -43,7 +43,7 @@ class FlowDemo extends Panel {
layoutConfig: layoutConfig().configWidth(LayoutSpec.MOST), layoutConfig: layoutConfig().configWidth(LayoutSpec.MOST),
}) })
}, },
loadMore: false, loadMore: true,
onLoadMore: () => { onLoadMore: () => {
setTimeout(() => { setTimeout(() => {
flowView.itemCount += 20 flowView.itemCount += 20

View File

@ -28,6 +28,8 @@ - (CGFloat)doricFlowLayoutItemHeightAtIndexPath:(NSIndexPath *)indexPath;
- (CGFloat)doricFlowLayoutItemWidthAtIndexPath:(NSIndexPath *)indexPath; - (CGFloat)doricFlowLayoutItemWidthAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)doricFlowLayoutItemFullSpan:(NSIndexPath *)indexPath;
- (CGFloat)doricFlowLayoutColumnSpace; - (CGFloat)doricFlowLayoutColumnSpace;
- (CGFloat)doricFlowLayoutRowSpace; - (CGFloat)doricFlowLayoutRowSpace;
@ -92,7 +94,7 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSInde
NSNumber *minYOfColumn = @(0); NSNumber *minYOfColumn = @(0);
NSArray<NSNumber *> *keys = self.columnHeightInfo.allKeys; NSArray<NSNumber *> *keys = self.columnHeightInfo.allKeys;
NSArray<NSNumber *> *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(NSNumber *obj1, NSNumber *obj2) { NSArray<NSNumber *> *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) { for (NSNumber *key in sortedKeys) {
@ -100,17 +102,15 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSInde
minYOfColumn = key; minYOfColumn = key;
} }
} }
CGFloat width = [self.delegate doricFlowLayoutItemWidthAtIndexPath:indexPath]; CGFloat width = [self.delegate doricFlowLayoutItemWidthAtIndexPath:indexPath];
CGFloat height = [self.delegate doricFlowLayoutItemHeightAtIndexPath:indexPath]; CGFloat height = [self.delegate doricFlowLayoutItemHeightAtIndexPath:indexPath];
CGFloat x = (width + self.columnSpace) * [minYOfColumn integerValue]; CGFloat x = 0;
CGFloat y = [self.columnHeightInfo[minYOfColumn] floatValue]; CGFloat y = [self.columnHeightInfo[minYOfColumn] floatValue];
if (y > 0) { if (y > 0) {
y += self.rowSpace; y += self.rowSpace;
} }
if (width == self.collectionView.width) { if ([self.delegate doricFlowLayoutItemFullSpan:indexPath]) {
x = 0;
NSNumber *maxYColumn = @(0); NSNumber *maxYColumn = @(0);
for (NSNumber *key in sortedKeys) { for (NSNumber *key in sortedKeys) {
if ([self.columnHeightInfo[key] floatValue] > [self.columnHeightInfo[maxYColumn] floatValue]) { if ([self.columnHeightInfo[key] floatValue] > [self.columnHeightInfo[maxYColumn] floatValue]) {
@ -119,8 +119,12 @@ - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSInde
} }
CGFloat maxY = [self.columnHeightInfo[maxYColumn] floatValue]; CGFloat maxY = [self.columnHeightInfo[maxYColumn] floatValue];
y = maxY + self.rowSpace; y = maxY + self.rowSpace;
self.columnHeightInfo[maxYColumn] = @(y + height); for (NSNumber *key in self.columnHeightInfo.allKeys) {
self.columnHeightInfo[key] = @(y + height);
}
} else { } else {
CGFloat columnWidth = (self.collectionView.width - (self.columnCount - 1) * self.columnSpace) / self.columnCount;
x = (columnWidth + self.columnSpace) * [minYOfColumn integerValue];
self.columnHeightInfo[minYOfColumn] = @(y + height); self.columnHeightInfo[minYOfColumn] = @(y + height);
} }
UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; UICollectionViewLayoutAttributes *attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
@ -163,6 +167,8 @@ @interface DoricFlowLayoutNode () <UICollectionViewDataSource, UICollectionViewD
@property(nonatomic, copy) NSString *onLoadMoreFuncId; @property(nonatomic, copy) NSString *onLoadMoreFuncId;
@property(nonatomic, copy) NSString *loadMoreViewId; @property(nonatomic, copy) NSString *loadMoreViewId;
@property(nonatomic, copy) NSString *headerViewId;
@property(nonatomic, copy) NSString *footerViewId;
@property(nonatomic, assign) BOOL loadMore; @property(nonatomic, assign) BOOL loadMore;
@property(nonatomic, strong) NSMutableSet <DoricDidScrollBlock> *didScrollBlocks; @property(nonatomic, strong) NSMutableSet <DoricDidScrollBlock> *didScrollBlocks;
@property(nonatomic, copy) NSString *onScrollFuncId; @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 { - (void)blendView:(UICollectionView *)view forPropName:(NSString *)name propValue:(id)prop {
if ([@"scrollable" isEqualToString:name]) { if ([@"scrollable" isEqualToString:name]) {
self.view.scrollEnabled = [prop boolValue]; self.view.scrollEnabled = [prop boolValue];
@ -240,14 +254,35 @@ - (void)blendView:(UICollectionView *)view forPropName:(NSString *)name propValu
self.onScrollFuncId = prop; self.onScrollFuncId = prop;
} else if ([@"onScrollEnd" isEqualToString:name]) { } else if ([@"onScrollEnd" isEqualToString:name]) {
self.onScrollEndFuncId = prop; self.onScrollEndFuncId = prop;
} else if ([@"header" isEqualToString:name]) {
self.headerViewId = prop;
} else if ([@"footer" isEqualToString:name]) {
self.footerViewId = prop;
} else { } else {
[super blendView:view forPropName:name propValue:prop]; [super blendView:view forPropName:name propValue:prop];
} }
} }
- (NSDictionary *)itemModelAt:(NSUInteger)position { - (NSDictionary *)itemModelAt:(NSUInteger)position {
if (position >= self.itemCount) { 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]; return [self subModelOf:self.loadMoreViewId];
} else {
return nil;
}
}
if (self.hasHeader) {
position--;
} }
NSString *viewId = self.itemViewIds[@(position)]; NSString *viewId = self.itemViewIds[@(position)];
if (viewId && viewId.length > 0) { if (viewId && viewId.length > 0) {
@ -331,7 +366,7 @@ - (void)callLoadMore {
} }
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - (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 { - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
@ -339,7 +374,18 @@ - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collection
NSDictionary *model = [self itemModelAt:position]; NSDictionary *model = [self itemModelAt:position];
NSDictionary *props = model[@"props"]; NSDictionary *props = model[@"props"];
NSString *identifier = props[@"identifier"] ?: @"doricCell"; 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"; identifier = @"doricLoadMoreCell";
[self callLoadMore]; [self callLoadMore];
} }
@ -355,7 +401,16 @@ - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collection
DoricFlowLayoutItemNode *node = cell.viewNode; DoricFlowLayoutItemNode *node = cell.viewNode;
node.viewId = model[@"id"]; node.viewId = model[@"id"];
[node blend:props]; [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; node.view.width = collectionView.width;
} else { } else {
node.view.width = (collectionView.width - (self.columnCount - 1) * self.columnSpace) / self.columnCount; node.view.width = (collectionView.width - (self.columnCount - 1) * self.columnSpace) / self.columnCount;
@ -399,6 +454,19 @@ - (NSInteger)doricFlowLayoutColumnCount {
return self.columnCount; 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 { - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
for (DoricDidScrollBlock block in self.didScrollBlocks) { for (DoricDidScrollBlock block in self.didScrollBlocks) {
block(scrollView); block(scrollView);

View File

@ -168,7 +168,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
&& position == self.itemCount && position == self.itemCount
+ (self.loadMore ? 1 : 0) + (self.loadMore ? 1 : 0)
+ (self.hasHeader ? 1 : 0) + (self.hasHeader ? 1 : 0)
+ (self.hasFooter ? 1 : 0)) { + (self.hasFooter ? 1 : 0)
- 1) {
reuseId = @"doricFooterCell"; reuseId = @"doricFooterCell";
} else if (self.loadMore } else if (self.loadMore
&& position == self.itemCount + (self.hasHeader ? 1 : 0) && position == self.itemCount + (self.hasHeader ? 1 : 0)
@ -269,8 +270,12 @@ - (NSDictionary *)itemModelAt:(NSUInteger)position {
- 1) { - 1) {
return [self subModelOf:self.footerViewId]; return [self subModelOf:self.footerViewId];
} }
if (position >= self.itemCount + (self.hasHeader ? 1 : 0)) { if (self.loadMore && position >= self.itemCount + (self.hasHeader ? 1 : 0)) {
if (self.loadMoreViewId && self.loadMoreViewId.length > 0) {
return [self subModelOf:self.loadMoreViewId]; return [self subModelOf:self.loadMoreViewId];
} else {
return nil;
}
} }
if (self.hasHeader) { if (self.hasHeader) {
position--; position--;

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

@ -143,6 +143,11 @@ declare module 'doric/lib/src/ui/panel' {
onShow(): void; onShow(): void;
onHidden(): void; onHidden(): void;
onEnvChanged(): 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; abstract build(rootView: Group): void;
addHeadView(type: string, v: View): void; addHeadView(type: string, v: View): void;
allHeadViews(): IterableIterator<Map<string, View>>; allHeadViews(): IterableIterator<Map<string, View>>;

View File

@ -10,6 +10,11 @@ export declare abstract class Panel {
onShow(): void; onShow(): void;
onHidden(): void; onHidden(): void;
onEnvChanged(): 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; abstract build(rootView: Group): void;
private __data__?; private __data__?;
private __root__; private __root__;

View File

@ -43,6 +43,11 @@ export abstract class Panel {
this.__root__.children.length = 0 this.__root__.children.length = 0
this.build(this.__root__) 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 abstract build(rootView: Group): void
private __data__?: object private __data__?: object