Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(android): refactoring WaterfallView #3809

Merged
merged 6 commits into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading