ListView 长列表
适用于显示同类的长列表数据类型,对渲染性能有一定的优化效果。ListView
经常和 RefreshControl 搭配使用。
代码演示
Note: you need set
height
/overflow
style.
/* eslint no-dupe-keys: 0, no-mixed-operators: 0 */
import { ListView } from 'antd-mobile';
function MyBody(props) {
return (
<div className="am-list-body my-body">
<span style={{ display: 'none' }}>you can custom body wrap element</span>
{props.children}
</div>
);
}
const data = [
{
img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png',
title: 'Meet hotel',
des: '不是所有的兼职汪都需要风吹日晒',
},
{
img: 'https://zos.alipayobjects.com/rmsportal/XmwCzSeJiqpkuMB.png',
title: 'McDonald\'s invites you',
des: '不是所有的兼职汪都需要风吹日晒',
},
{
img: 'https://zos.alipayobjects.com/rmsportal/hfVtzEhPzTUewPm.png',
title: 'Eat the week',
des: '不是所有的兼职汪都需要风吹日晒',
},
];
let index = data.length - 1;
const NUM_SECTIONS = 5;
const NUM_ROWS_PER_SECTION = 5;
let pageIndex = 0;
class Demo extends React.Component {
constructor(props) {
super(props);
const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID];
const dataSource = new ListView.DataSource({
getRowData,
getSectionHeaderData: getSectionData,
rowHasChanged: (row1, row2) => row1 !== row2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
});
this.dataBlob = {};
this.sectionIDs = [];
this.rowIDs = [];
this.genData = (pIndex = 0) => {
for (let i = 0; i < NUM_SECTIONS; i++) {
const ii = (pIndex * NUM_SECTIONS) + i;
const sectionName = `Section ${ii}`;
this.sectionIDs.push(sectionName);
this.dataBlob[sectionName] = sectionName;
this.rowIDs[ii] = [];
for (let jj = 0; jj < NUM_ROWS_PER_SECTION; jj++) {
const rowName = `S${ii}, R${jj}`;
this.rowIDs[ii].push(rowName);
this.dataBlob[rowName] = rowName;
}
}
// new object ref
this.sectionIDs = [].concat(this.sectionIDs);
this.rowIDs = [].concat(this.rowIDs);
};
this.state = {
dataSource: dataSource.cloneWithRowsAndSections(this.dataBlob, this.sectionIDs, this.rowIDs),
isLoading: true,
};
}
componentDidMount() {
// you can scroll to the specified position
// setTimeout(() => this.refs.lv.refs.listview.scrollTo(0, 120), 800); // also work
// setTimeout(() => this.refs.lv.scrollTo(0, 120), 800); // recommend usage
// simulate initial Ajax
setTimeout(() => {
this.genData();
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(this.dataBlob, this.sectionIDs, this.rowIDs),
isLoading: false,
});
}, 600);
}
// If you use redux, the data maybe at props, you need use `componentWillReceiveProps`
// componentWillReceiveProps(nextProps) {
// if (nextProps.dataSource !== this.props.dataSource) {
// this.setState({
// dataSource: this.state.dataSource.cloneWithRowsAndSections(nextProps.dataSource),
// });
// }
// }
onEndReached = (event) => {
// load new data
// hasMore: from backend data, indicates whether it is the last page, here is false
if (this.state.isLoading && !this.state.hasMore) {
return;
}
console.log('reach end', event);
this.setState({ isLoading: true });
setTimeout(() => {
this.genData(++pageIndex);
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(this.dataBlob, this.sectionIDs, this.rowIDs),
isLoading: false,
});
}, 1000);
}
render() {
const separator = (sectionID, rowID) => (
<div key={`${sectionID}-${rowID}`}
style={{
backgroundColor: '#F5F5F9',
height: 8,
borderTop: '1px solid #ECECED',
borderBottom: '1px solid #ECECED',
}}
/>
);
const row = (rowData, sectionID, rowID) => {
if (index < 0) {
index = data.length - 1;
}
const obj = data[index--];
return (
<div key={rowID} className="row">
<div className="row-title">{obj.title}</div>
<div style={{ display: '-webkit-box', display: 'flex', padding: '0.3rem 0' }}>
<img style={{ height: '1.28rem', marginRight: '0.3rem' }} src={obj.img} alt="icon" />
<div className="row-text">
<div style={{ marginBottom: '0.16rem', fontWeight: 'bold' }}>{obj.des}</div>
<div><span style={{ fontSize: '0.6rem', color: '#FF6E27' }}>35</span>¥</div>
</div>
</div>
</div>
);
};
return (<div style={{ margin: '0 auto', width: '96%' }}>
<ListView ref="lv"
dataSource={this.state.dataSource}
renderHeader={() => <span>header</span>}
renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}>
{this.state.isLoading ? 'Loading...' : 'Loaded'}
</div>)}
renderSectionHeader={sectionData => (
<div>{`Task ${sectionData.split(' ')[1]}`}</div>
)}
renderBodyComponent={() => <MyBody />}
renderRow={row}
renderSeparator={separator}
className="fortest"
style={{
height: document.documentElement.clientHeight * 3 / 4,
overflow: 'auto',
border: '1px solid #ddd',
margin: '0.1rem 0',
}}
pageSize={4}
onScroll={() => { console.log('scroll'); }}
scrollRenderAheadDistance={500}
scrollEventThrottle={200}
onEndReached={this.onEndReached}
onEndReachedThreshold={10}
/>
</div>);
}
}
ReactDOM.render(<Demo />, mountNode);
use html
.row {
padding: 0 0.3rem;
background-color: white;
}
.row-title {
height: 1rem;
line-height: 1rem;
color: #888;
font-size: 0.36rem;
border-bottom: 1px solid #F6F6F6;
}
.row-text {
display: inline-block;
font-size: 0.32rem;
line-height: 1;
}
body
as a scroll container.
/* eslint no-dupe-keys: 0 */
import { ListView } from 'antd-mobile';
const data = [
{
img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png',
title: 'Meet hotel',
des: '不是所有的兼职汪都需要风吹日晒',
},
{
img: 'https://zos.alipayobjects.com/rmsportal/XmwCzSeJiqpkuMB.png',
title: 'McDonald\'s invites you',
des: '不是所有的兼职汪都需要风吹日晒',
},
{
img: 'https://zos.alipayobjects.com/rmsportal/hfVtzEhPzTUewPm.png',
title: 'Eat the week',
des: '不是所有的兼职汪都需要风吹日晒',
},
];
let index = data.length - 1;
const NUM_ROWS = 20;
let pageIndex = 0;
class Demo extends React.Component {
constructor(props) {
super(props);
const dataSource = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
this.genData = (pIndex = 0) => {
const dataBlob = {};
for (let i = 0; i < NUM_ROWS; i++) {
const ii = (pIndex * NUM_ROWS) + i;
dataBlob[`${ii}`] = `row - ${ii}`;
}
return dataBlob;
};
this.state = {
dataSource: dataSource.cloneWithRows({}),
isLoading: true,
};
}
componentDidMount() {
// you can scroll to the specified position
// setTimeout(() => this.refs.lv.refs.listview.scrollTo(0, 200), 800); // also work
// setTimeout(() => this.refs.lv.scrollTo(0, 200), 800); // recommend usage
// simulate initial Ajax
setTimeout(() => {
this.rData = this.genData();
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.rData),
isLoading: false,
});
}, 600);
}
// If you use redux, the data maybe at props, you need use `componentWillReceiveProps`
// componentWillReceiveProps(nextProps) {
// if (nextProps.dataSource !== this.props.dataSource) {
// this.setState({
// dataSource: this.state.dataSource.cloneWithRows(nextProps.dataSource),
// });
// }
// }
onEndReached = (event) => {
// load new data
// hasMore: from backend data, indicates whether it is the last page, here is false
if (this.state.isLoading && !this.state.hasMore) {
return;
}
console.log('reach end', event);
this.setState({ isLoading: true });
setTimeout(() => {
this.rData = { ...this.rData, ...this.genData(++pageIndex) };
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.rData),
isLoading: false,
});
}, 1000);
}
render() {
const separator = (sectionID, rowID) => (
<div key={`${sectionID}-${rowID}`}
style={{
backgroundColor: '#F5F5F9',
height: 8,
borderTop: '1px solid #ECECED',
borderBottom: '1px solid #ECECED',
}}
/>
);
const row = (rowData, sectionID, rowID) => {
if (index < 0) {
index = data.length - 1;
}
const obj = data[index--];
return (
<div key={rowID} className="row">
<div className="row-title">{obj.title}</div>
<div style={{ display: '-webkit-box', display: 'flex', padding: '0.3rem 0' }}>
<img style={{ height: '1.28rem', marginRight: '0.3rem' }} src={obj.img} alt="icon" />
<div className="row-text">
<div style={{ marginBottom: '0.16rem', fontWeight: 'bold' }}>{obj.des}</div>
<div><span style={{ fontSize: '0.6rem', color: '#FF6E27' }}>{rowID}</span>¥</div>
</div>
</div>
</div>
);
};
return (
<ListView ref="lv"
dataSource={this.state.dataSource}
renderHeader={() => <span>header</span>}
renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}>
{this.state.isLoading ? 'Loading...' : 'Loaded'}
</div>)}
renderRow={row}
renderSeparator={separator}
className="am-list"
pageSize={4}
useBodyScroll
onScroll={() => { console.log('scroll'); }}
scrollRenderAheadDistance={500}
scrollEventThrottle={200}
onEndReached={this.onEndReached}
onEndReachedThreshold={10}
/>
);
}
}
ReactDOM.render(<Demo />, mountNode);
sticky block header to the top of the page
.row {
padding: 0 0.3rem;
background-color: white;
}
.row-title {
height: 1rem;
line-height: 1rem;
color: #888;
font-size: 0.36rem;
border-bottom: 1px solid #F6F6F6;
}
.row-text {
display: inline-block;
font-size: 0.32rem;
line-height: 1;
}
/* eslint no-dupe-keys: 0 */
import { ListView } from 'antd-mobile';
const data = [
{
img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png',
title: 'Meet hotel',
des: '不是所有的兼职汪都需要风吹日晒',
},
{
img: 'https://zos.alipayobjects.com/rmsportal/XmwCzSeJiqpkuMB.png',
title: 'McDonald\'s invites you',
des: '不是所有的兼职汪都需要风吹日晒',
},
{
img: 'https://zos.alipayobjects.com/rmsportal/hfVtzEhPzTUewPm.png',
title: 'Eat the week',
des: '不是所有的兼职汪都需要风吹日晒',
},
];
let index = data.length - 1;
const NUM_SECTIONS = 5;
const NUM_ROWS_PER_SECTION = 5;
let pageIndex = 0;
class Demo extends React.Component {
constructor(props) {
super(props);
const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID];
const dataSource = new ListView.DataSource({
getRowData,
getSectionHeaderData: getSectionData,
rowHasChanged: (row1, row2) => row1 !== row2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
});
this.dataBlob = {};
this.sectionIDs = [];
this.rowIDs = [];
this.genData = (pIndex = 0) => {
for (let i = 0; i < NUM_SECTIONS; i++) {
const ii = (pIndex * NUM_SECTIONS) + i;
const sectionName = `Section ${ii}`;
this.sectionIDs.push(sectionName);
this.dataBlob[sectionName] = sectionName;
this.rowIDs[ii] = [];
for (let jj = 0; jj < NUM_ROWS_PER_SECTION; jj++) {
const rowName = `S${ii}, R${jj}`;
this.rowIDs[ii].push(rowName);
this.dataBlob[rowName] = rowName;
}
}
// new object ref
this.sectionIDs = [].concat(this.sectionIDs);
this.rowIDs = [].concat(this.rowIDs);
};
this.state = {
dataSource: dataSource.cloneWithRowsAndSections(this.dataBlob, this.sectionIDs, this.rowIDs),
isLoading: true,
};
}
componentDidMount() {
// you can scroll to the specified position
// setTimeout(() => this.refs.lv.refs.listview.scrollTo(0, 200), 800); // also work
// setTimeout(() => this.refs.lv.scrollTo(0, 200), 800); // recommend usage
// simulate initial Ajax
setTimeout(() => {
this.genData();
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(this.dataBlob, this.sectionIDs, this.rowIDs),
isLoading: false,
});
}, 600);
}
// If you use redux, the data maybe at props, you need use `componentWillReceiveProps`
// componentWillReceiveProps(nextProps) {
// if (nextProps.dataSource !== this.props.dataSource) {
// this.setState({
// dataSource: this.state.dataSource.cloneWithRowsAndSections(nextProps.dataSource),
// });
// }
// }
onEndReached = (event) => {
// load new data
// hasMore: from backend data, indicates whether it is the last page, here is false
if (this.state.isLoading && !this.state.hasMore) {
return;
}
console.log('reach end', event);
this.setState({ isLoading: true });
setTimeout(() => {
this.genData(++pageIndex);
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(this.dataBlob, this.sectionIDs, this.rowIDs),
isLoading: false,
});
}, 1000);
}
render() {
const separator = (sectionID, rowID) => (
<div key={`${sectionID}-${rowID}`}
style={{
backgroundColor: '#F5F5F9',
height: 8,
borderTop: '1px solid #ECECED',
borderBottom: '1px solid #ECECED',
}}
/>
);
const row = (rowData, sectionID, rowID) => {
if (index < 0) {
index = data.length - 1;
}
const obj = data[index--];
return (
<div key={rowID} className="row">
<div className="row-title">{obj.title}</div>
<div style={{ display: '-webkit-box', display: 'flex', padding: '0.3rem 0' }}>
<img style={{ height: '1.28rem', marginRight: '0.3rem' }} src={obj.img} alt="icon" />
<div className="row-text">
<div style={{ marginBottom: '0.16rem', fontWeight: 'bold' }}>{obj.des}</div>
<div><span style={{ fontSize: '0.6rem', color: '#FF6E27' }}>35</span>¥</div>
</div>
</div>
</div>
);
};
return (
<ListView ref="lv"
dataSource={this.state.dataSource}
renderHeader={() => <span>header</span>}
renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}>
{this.state.isLoading ? 'Loading...' : 'Loaded'}
</div>)}
renderSectionHeader={sectionData => (
<div>{`Task ${sectionData.split(' ')[1]}`}</div>
)}
renderRow={row}
renderSeparator={separator}
className="am-list"
pageSize={4}
onScroll={() => { console.log('scroll'); }}
scrollEventThrottle={200}
onEndReached={this.onEndReached}
onEndReachedThreshold={10}
stickyHeader
stickyProps={{
stickyStyle: { zIndex: 999, WebkitTransform: 'none', transform: 'none' },
// topOffset: -43,
// isActive: false,
}}
stickyContainerProps={{
className: 'for-stickyContainer-demo',
}}
/>
);
}
}
ReactDOM.render(<Demo />, mountNode);
Index List
.row {
padding: 0 0.3rem;
background-color: white;
}
.row-title {
height: 1rem;
line-height: 1rem;
color: #888;
font-size: 0.36rem;
border-bottom: 1px solid #F6F6F6;
}
.row-text {
display: inline-block;
font-size: 0.32rem;
line-height: 1;
}
sticky index List
/* eslint no-mixed-operators: 0 */
import { province } from 'antd-mobile-demo-data';
import { ListView, List } from 'antd-mobile';
const { Item } = List;
class Demo extends React.Component {
constructor(props) {
super(props);
const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID];
const dataSource = new ListView.DataSource({
getRowData,
getSectionHeaderData: getSectionData,
rowHasChanged: (row1, row2) => row1 !== row2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
});
const dataBlob = {};
const sectionIDs = [];
const rowIDs = [];
Object.keys(province).forEach((item, index) => {
sectionIDs.push(item);
dataBlob[item] = item;
rowIDs[index] = [];
province[item].forEach((jj) => {
rowIDs[index].push(jj.value);
dataBlob[jj.value] = jj.label;
});
});
this.state = {
dataSource: dataSource.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs),
headerPressCount: 0,
};
}
render() {
return (
<ListView.IndexedList
dataSource={this.state.dataSource}
renderHeader={() => <span>custom header</span>}
renderFooter={() => <span>custom footer</span>}
renderSectionHeader={sectionData => (<div className="ih">{sectionData}</div>)}
renderRow={rowData => (<Item>{rowData}</Item>)}
className="fortest"
style={{
height: document.documentElement.clientHeight * 3 / 4,
overflow: 'auto',
}}
quickSearchBarStyle={{
position: 'absolute',
top: 20,
}}
delayTime={10}
delayActivityIndicator={<div style={{ padding: 25, textAlign: 'center' }}>rendering...</div>}
/>
);
}
}
ReactDOM.render(<Demo />, mountNode);
import { province as provinceData } from 'antd-mobile-demo-data';
import { ListView, List, SearchBar } from 'antd-mobile';
const { Item } = List;
class Demo extends React.Component {
constructor(props) {
super(props);
const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID];
const dataSource = new ListView.DataSource({
getRowData,
getSectionHeaderData: getSectionData,
rowHasChanged: (row1, row2) => row1 !== row2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
});
this.createDs = (ds, province) => {
const dataBlob = {};
const sectionIDs = [];
const rowIDs = [];
Object.keys(province).forEach((item, index) => {
sectionIDs.push(item);
dataBlob[item] = item;
rowIDs[index] = [];
province[item].forEach((jj) => {
rowIDs[index].push(jj.value);
dataBlob[jj.value] = jj.label;
});
});
return ds.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs);
};
this.state = {
inputValue: '',
dataSource: this.createDs(dataSource, provinceData),
headerPressCount: 0,
};
}
onSearch = (val) => {
const pd = { ...provinceData };
Object.keys(pd).forEach((item) => {
pd[item] = pd[item].filter(jj => jj.spell.toLocaleLowerCase().indexOf(val) > -1);
});
this.setState({
inputValue: val,
dataSource: this.createDs(this.state.dataSource, pd),
});
}
render() {
return (<div style={{ paddingTop: '0.88rem', position: 'relative' }}>
<div style={{ position: 'absolute', top: 0, left: 0, right: 0 }}>
<SearchBar
value={this.state.inputValue}
placeholder="Search"
onChange={this.onSearch}
onClear={() => { console.log('onClear'); }}
onCancel={() => { console.log('onCancel'); }}
/>
</div>
<ListView.IndexedList
dataSource={this.state.dataSource}
renderHeader={() => <span>custom header</span>}
renderFooter={() => <span>custom footer</span>}
renderSectionHeader={sectionData => (<div className="ih">{sectionData}</div>)}
renderRow={rowData => (<Item>{rowData}</Item>)}
className="am-list"
stickyHeader
stickyProps={{
stickyStyle: { zIndex: 999 },
}}
quickSearchBarStyle={{
top: 85,
}}
delayTime={10}
delayActivityIndicator={<div style={{ padding: 25, textAlign: 'center' }}>rendering...</div>}
/>
</div>);
}
}
ReactDOM.render(<Demo />, mountNode);
API
适用平台:WEB、React-Native(DEPRECATED
)
注意: React Native ListView 现在已经被标记了DEPRECATED
。因为我们内部是直接从 'react-native' 里导入ListView
,所以我们也将废弃 ListView RN 版本。
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
dataSource | ListView.DataSource (cn) / ListView.DataSource (us) 实例 | ListViewDataSource | - |
initialListSize | 指定在组件刚挂载的时候渲染多少行数据,用这个属性来确保首屏显示合适数量的数据 | number | - |
onEndReached | 当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold 个像素的距离时调用 | (event?) => {} | - |
onEndReachedThreshold | 调用onEndReached 之前的临界值,单位是像素 | number | 1000 |
pageSize | 每次事件循环(每帧)渲染的行数 | number | 1 |
renderHeader / renderFooter | 页头与页脚(如果提供)会在每次渲染过程中都重新渲染。如果它们重绘的性能开销很大,把他们包装到一个StaticContainer或者其它恰当的结构中。页脚在列表的最底部,而页头会在最顶部 | () => renderable | - |
renderRow | 从数据源(data source)中接受一条数据,以及它和它所在section的ID。返回一个可渲染的组件来为这行数据进行渲染。默认情况下参数中的数据就是放进数据源中的数据本身,不过也可以提供一些转换器。如果某一行正在被高亮(通过调用highlightRow函数),ListView会得到相应的通知。 | (rowData, sectionID, rowID, highlightRow) => renderable | - |
renderScrollComponent | 指定一个函数,在其中返回一个可以滚动的组件,ListView将会在该组件内部进行渲染。默认情况下会返回一个包含指定属性的ScrollView。 | (props) => renderable | - |
renderSectionHeader | 如果提供了此函数,会为每个小节(section)渲染一个标题 | (sectionData, sectionID) => renderable | - |
renderSeparator | 如果提供了此属性,一个可渲染的组件会被渲染在每一行下面,除了小节标题的前面的最后一行。在其上方的小节ID和行ID,以及邻近的行是否被高亮会作为参数传递进来。 | (sectionID, rowID, adjacentRowHighlighted) => renderable | - |
scrollRenderAheadDistance | 当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行 | number | 1000 |
contentContainerStyle | 这些样式会应用到一个内层的内容容器上,所有的子视图都会包裹在内容容器内 | Object | - |
horizontal | 当此属性为true的时候,所有的的子视图会在水平方向上排成一行,而不是默认的在垂直方向上排成一列 | bool | false |
onContentSizeChange | 此函数会在 ScrollView 内部可滚动内容的视图发生变化时调用。 | (contentWidth, contentHeight) => {} | - |
onScroll | 在滚动的过程中,每帧最多调用一次此回调函数。调用的频率可以用scrollEventThrottle 属性来控制。 | e => {} | - |
scrollEventThrottle | 控制在滚动过程中,scroll事件被调用的频率 | number | 50 |
refreshControl | 指定 RefreshControl 组件,用于为ScrollView 提供下拉刷新功能。 | element | - |
onLayout | 当组件挂载或者布局变化的时候调用 | ({nativeEvent:{ layout:{ width, height }}}) => {} | - |
—— | |||
renderBodyComponent (web only ) | 自定义 body 的包裹组件 | () => renderable | - |
renderSectionBodyWrapper (web only ) | 渲染自定义的区块包裹组件 | (sectionID) => renderable | - |
useBodyScroll (web only ) | 使用 html 的 body 作为滚动容器 | bool | false |
useZscroller (web only ) | 使用 zscroller 来模拟实现滚动容器 (可用于一些低端 Android 机上),注意:开启后useBodyScroll 和stickyHeader 设置会自动被忽略 | bool | false |
scrollerOptions (web only ) | zscroller options | Object | - |
stickyHeader (web only ) | 固定区块标题到页面顶部 (注意: 设置后会自动开启useBodyScroll ),启用后还可以设置 stickyProps / stickyContainerProps (详见 react-sticky) | bool | false |
方法
getMetrics() - 导出一些用于性能分析的数据。
scrollTo(…args) - 滚动到指定的x, y偏移处(暂不支持过渡动画)。
ListView.IndexedList
适用平台:WEB。 此组件常用于 “通讯录”/“城市列表” 等场景中,支持索引导航功能。你可以使用 ListView 上的几乎所有 APIs,除了
useZscroller
注意:由于索引列表可以点击任一项索引来定位其内容、即内容需要直接滚动到任意位置,这样就难以做到像 ListView 一样能在滚动时自动懒渲染。目前实现上只支持分两步渲染,能借此达到首屏优先显示目的,但如果列表数据量过大时、整体性能仍会有影响。
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
quickSearchBarTop | 快捷导航栏最顶部按钮、常用于回到顶部 | object{value:string, label:string} | { value: '#', label: '#' } |
quickSearchBarStyle | quickSearchBar 的 style | object | - |
onQuickSearch | 快捷导航切换时调用 | (sectionID: any, topId?:any) => void | - |
showQuickSearchIndicator | whether show quick search indicator | bool | false |
delayTime | 延迟渲染时间设置(用于首屏优化,一开始渲染initialListSize 数量的数据,在此时间后、延迟渲染剩余的数据项、即totalRowCount - initialListSize ) | number | 100ms |
delayActivityIndicator | 延迟渲染的 loading 指示器 | react node | - |
提示
ListView 有三种类型的滚动容器:局部 div 容器
- 默认,注意:需要手动给 ListView 设置高度
html 的 body 容器
- 设置
useBodyScroll
或stickyHeader
后生效 (不需要设置高度)
- 设置
使用 zscroller 的模拟滚动容器
- 设置
useZscroller
后生效,然后可以设置scrollerOptions
(需要手动给 ListView 设置高度)
- 设置
this._renderMoreRowsIfNeeded()
,由于此时this.state.curRenderedRowsCount === this.props.dataSource.getRowCount()
即已经渲染的数据与 dataSource 里已有的数据项个数相同,所以 ListView 认为应该再调用 onEndReached 方法。
onEndReached 为什么会不停调用?520#issuecomment-263510596其他问题:#633#573#541