Skip to content

Commit

Permalink
feat(android): refactoring WaterfallView (Tencent#3809)
Browse files Browse the repository at this point in the history
* feat(android): refactoring WaterfallView

* fix(vue3): fix vue3 refreshing && vue2 style

* fix(android): fix PullRefreshContainer size for recycler view

* fix(android): add copyright header for ItemLayoutParams

---------

Co-authored-by: maxli <[email protected]>
Co-authored-by: zealotchen <[email protected]>
  • Loading branch information
3 people authored Apr 8, 2024
1 parent 2110705 commit 8dad9f2
Show file tree
Hide file tree
Showing 94 changed files with 1,833 additions and 19,611 deletions.
6 changes: 2 additions & 4 deletions docs/api/hippy-react/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -516,15 +516,13 @@ import icon from './qb_icon_new.png';
| interItemSpacing | item 间的垂直间距 | `number` | `Android、iOS、Voltron` |
| contentInset | 内容缩进 ,默认值 `{ top:0, left:0, bottom:0, right:0 }` | `Object` | `Android、iOS、Voltron` |
| renderItem | 这里的入参是当前 item 的 index,在这里可以凭借 index 获取到瀑布流一个具体单元格的数据,从而决定如何渲染这个单元格。 | `(index: number) => React.ReactElement` | `Android、iOS、Voltron` |
| renderBanner | 如何渲染 Banner。 | `() => React.ReactElement` | `iOS、Voltron`
| renderBanner | 如何渲染 Banner。 | `() => React.ReactElement` | `Android、iOS、Voltron`
| getItemStyle | 设置`WaterfallItem`容器的样式。 | `(index: number) => styleObject` | `Android、iOS、Voltron` |
| getItemType | 指定一个函数,在其中返回对应条目的类型(返回Number类型的自然数,默认是0),List 将对同类型条目进行复用,所以合理的类型拆分,可以很好地提升list 性能。 | `(index: number) => number` | `Android、iOS、Voltron` |
| getItemKey | 指定一个函数,在其中返回对应条目的 Key 值,详见 [React 官文](//reactjs.org/docs/lists-and-keys.html) | `(index: number) => any` | `Android、iOS、Voltron` |
| preloadItemNumber | 滑动到瀑布流底部前提前预加载的 item 数量 | `number` | `Android、iOS、Voltron` |
| onEndReached | 当所有的数据都已经渲染过,并且列表被滚动到最后一条时,将触发 `onEndReached` 回调。 | `Function` | `Android、iOS、Voltron` |
| containPullHeader | 是否包含`PullHeader`组件,默认 `false``Android` 暂不支持,可暂时用 `RefreshWrapper` 组件替代 | `boolean` | `iOS、Voltron` |
| renderPullHeader | 如何渲染 `PullHeader`,此时 `containPullHeader` 默认设置成 `true` | `() => React.ReactElement` | `iOS、Voltron` |
| containPullFooter | 是否包含`PullFooter`组件,默认 `false` | `boolean` | `Android、iOS、Voltron` |
| renderPullHeader | 如何渲染 `PullHeader`,此时 `containPullHeader` 默认设置成 `true` | `() => React.ReactElement` | `Android、iOS、Voltron` |
| renderPullFooter | 如何渲染 `PullFooter`,此时 `containPullFooter` 默认设置成 `true` | `() => React.ReactElement` | `Android、iOS、Voltron` |
| onScroll | 当触发 `WaterFall` 的滑动事件时回调。`startEdgePos`表示距离 List 顶部边缘滚动偏移量;`endEdgePos`表示距离 List 底部边缘滚动偏移量;`firstVisibleRowIndex`表示当前可见区域内第一个元素的索引;`lastVisibleRowIndex`表示当前可见区域内最后一个元素的索引;`visibleRowFrames`表示当前可见区域内所有 item 的信息(x,y,width,height) | `nativeEvent: { startEdgePos: number, endEdgePos: number, firstVisibleRowIndex: number, lastVisibleRowIndex: number, visibleRowFrames: Object[] }` | `Android、iOS、Voltron`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
StyleSheet,
Text,
Dimensions,
RefreshWrapper,
} from '@hippy/react';

import mockDataTemp from '../../shared/UIStyles/mock';
Expand Down Expand Up @@ -55,8 +54,9 @@ export default class ListExample extends React.Component {
super(props);
this.state = {
dataSource: [],
pullingText: '继续下拉触发刷新',
loadingState: '正在加载...',
headerRefreshText: '继续下拉触发刷新',
footerRefreshText: '正在加载...',
horizontal: undefined,
};
this.numberOfColumns = 2;
this.columnSpacing = 6;
Expand All @@ -69,8 +69,14 @@ export default class ListExample extends React.Component {
this.onRefresh = this.onRefresh.bind(this);
this.getRefresh = this.getRefresh.bind(this);
this.renderPullFooter = this.renderPullFooter.bind(this);
this.renderPullHeader = this.renderPullHeader.bind(this);
this.onHeaderReleased = this.onHeaderReleased.bind(this);
this.onHeaderPulling = this.onHeaderPulling.bind(this);
this.onFooterPulling = this.onFooterPulling.bind(this);
this.renderBanner = this.renderBanner.bind(this);
this.getItemStyle = this.getItemStyle.bind(this);
this.getHeaderStyle = this.getHeaderStyle.bind(this);
this.onScroll = this.onScroll.bind(this);
}

async componentDidMount() {
Expand All @@ -80,7 +86,9 @@ export default class ListExample extends React.Component {

/**
* 页面加载更多时触发
* 这里触发加载更多还可以使用 PullFooter 组件,主要看是否需要一个内容加载区。
*
* 这里触发加载更多还可以使用 PullFooter 组件。
*
* onEndReached 更适合用来无限滚动的场景。
*/
async onEndReached() {
Expand All @@ -91,29 +99,101 @@ export default class ListExample extends React.Component {
}
this.loadMoreDataFlag = true;
this.setState({
loadingState: '加载更多...',
footerRefreshText: '加载更多...',
});
let newData = [];
try {
newData = await this.mockFetchData();
} catch (err) {}
if (newData.length === 0) {
this.setState({
loadingState: '没有更多数据',
footerRefreshText: '没有更多数据',
});
}
const newDataSource = [...dataSource, ...newData];
this.setState({ dataSource: newDataSource });
this.loadMoreDataFlag = false;
this.listView.collapsePullFooter();
}

/**
* 下拉超过内容高度,松手后触发
*/
async onHeaderReleased() {
if (this.fetchingDataFlag) {
return;
}
this.fetchingDataFlag = true;
console.log('onHeaderReleased');
this.setState({
headerRefreshText: '刷新数据中,请稍等',
});
let dataSource = [];
try {
dataSource = await this.mockFetchData();
} catch (err) {}
this.fetchingDataFlag = false;
this.setState({
dataSource,
headerRefreshText: '2秒后收起',
}, () => {
this.listView.collapsePullHeader({ time: 2000 });
});
}

/**
* 下拉过程中触发
*
* 事件会通过 contentOffset 参数返回拖拽高度,我们已经知道了内容高度,
* 简单对比一下就可以显示不同的状态。
*
* 这里简单处理,其实可以做到更复杂的动态效果。
*/
onHeaderPulling(evt) {
if (this.fetchingDataFlag) {
return;
}
console.log('onHeaderPulling', evt.contentOffset);
if (evt.contentOffset > styles.pullContent.height) {
this.setState({
headerRefreshText: '松手,即可触发刷新',
});
} else {
this.setState({
headerRefreshText: '继续下拉,触发刷新',
});
}
}

onFooterPulling(evt) {
console.log('onFooterPulling', evt);
}

/**
* 渲染 pullFooter 组件
*/
renderPullFooter() {
if (this.state.dataSource.length === 0) return null;
return (<View style={styles.pullFooter}>
const { horizontal } = this.state;
return !horizontal ? <View style={styles.pullFooter}>
<Text style={{
color: 'white',
}}>{this.state.loadingState}</Text>
</View>);
}}
>{this.state.footerRefreshText}</Text>
</View> : <View style={{
width: 40,
height: 300,
backgroundColor: '#4c9afa',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text style={{
color: 'white',
lineHeight: 25,
width: 40,
paddingHorizontal: 15,
}}
>{this.state.footerRefreshText}</Text>
</View>;
}

async onRefresh() {
Expand Down Expand Up @@ -155,6 +235,10 @@ export default class ListExample extends React.Component {
this.listView.scrollToIndex({ index, animation: true });
}

onScroll(obj) {

}

// render banner(it is not supported on Android yet)
renderBanner() {
if (this.state.dataSource.length === 0) return null;
Expand Down Expand Up @@ -215,53 +299,80 @@ export default class ListExample extends React.Component {
}

getWaterfallContentInset() {
return { top: 0, left: 5, bottom: 0, right: 5 };
return { top: 0, left: 0, bottom: 0, right: 0 };
}

getItemStyle() {
const { numberOfColumns, columnSpacing } = this;
const screenWidth = Dimensions.get('screen').width;
const screenWidth = Dimensions.get('screen').width - 32;
const contentInset = this.getWaterfallContentInset();
const width = screenWidth - contentInset.left - contentInset.right;
return {
width: (width - ((numberOfColumns - 1) * columnSpacing)) / numberOfColumns,
};
}

getHeaderStyle() {
const { horizontal } = this.state;
return !horizontal ? {} : {
width: 50,
};
}

/**
* 渲染 pullHeader 组件
*/
renderPullHeader() {
const { headerRefreshText, horizontal } = this.state;
return (
!horizontal ? <View style={styles.pullContainer}>
<Text style={styles.pullContent}>{headerRefreshText}</Text>
</View> : <View style={{
width: 40,
height: 300,
backgroundColor: '#4c9afa',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text style={{
lineHeight: 25,
color: 'white',
width: 40,
paddingHorizontal: 15,
}}>{headerRefreshText}</Text>
</View>
);
}

render() {
const { dataSource } = this.state;
const { numberOfColumns, columnSpacing, interItemSpacing } = this;
const contentInset = this.getWaterfallContentInset();
return (
<RefreshWrapper
ref={(ref) => {
this.refresh = ref;
}}
style={{ flex: 1 }}
onRefresh={this.onRefresh}
bounceTime={100}
getRefresh={this.getRefresh}
>
<WaterfallView
ref={(ref) => {
this.listView = ref;
}}
renderBanner={this.renderBanner}
numberOfColumns={numberOfColumns}
columnSpacing={columnSpacing}
interItemSpacing={interItemSpacing}
numberOfItems={dataSource.length}
preloadItemNumber={4}
style={{ flex: 1 }}
renderItem={this.renderItem}
onScroll={this.onScroll}
renderBanner={this.renderBanner}
renderPullHeader={this.renderPullHeader}
onEndReached={this.onEndReached}
onFooterReleased={this.onEndReached}
onHeaderReleased={this.onHeaderReleased}
onHeaderPulling={this.onHeaderPulling}
renderItem={this.renderItem}
getItemType={this.getItemType}
getItemKey={this.getItemKey}
contentInset={contentInset}
getItemStyle={this.getItemStyle}
containPullFooter={true}
renderPullFooter={this.renderPullFooter}
getHeaderStyle={this.getHeaderStyle}
contentInset={contentInset}
/>
</RefreshWrapper>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,56 +258,56 @@ export default {
border-style: solid;
}
[specital-attr='pull-header-footer'] >>> .article-title {
[specital-attr='pull-header-footer'] .article-title {
font-size: 17px;
line-height: 24px;
color: #242424;
}
[specital-attr='pull-header-footer'] >>> .normal-text {
[specital-attr='pull-header-footer'] .normal-text {
font-size: 11px;
color: #aaa;
align-self: center;
}
[specital-attr='pull-header-footer'] >>> .image {
[specital-attr='pull-header-footer'] .image {
flex: 1;
height: 160px;
resize-mode: cover;
}
[specital-attr='pull-header-footer'] >>> .style-one-image-container {
[specital-attr='pull-header-footer'] .style-one-image-container {
flex-direction: row;
justify-content: center;
margin-top: 8px;
flex: 1;
}
[specital-attr='pull-header-footer'] >>> .style-one-image {
[specital-attr='pull-header-footer'] .style-one-image {
height: 120px;
}
[specital-attr='pull-header-footer'] >>> .style-two {
[specital-attr='pull-header-footer'] .style-two {
flex-direction: row;
justify-content: space-between;
}
[specital-attr='pull-header-footer'] >>> .style-two-left-container {
[specital-attr='pull-header-footer'] .style-two-left-container {
flex: 1;
flex-direction: column;
justify-content: center;
margin-right: 8px;
}
[specital-attr='pull-header-footer'] >>> .style-two-image-container {
[specital-attr='pull-header-footer'] .style-two-image-container {
flex: 1;
}
[specital-attr='pull-header-footer'] >>> .style-two-image {
[specital-attr='pull-header-footer'] .style-two-image {
height: 140px;
}
[specital-attr='pull-header-footer'] >>> .style-five-image-container {
[specital-attr='pull-header-footer'] .style-five-image-container {
flex-direction: row;
justify-content: center;
margin-top: 8px;
Expand Down
Loading

0 comments on commit 8dad9f2

Please sign in to comment.