ListView 长列表

适用于显示同类的长列表数据类型,对渲染性能有一定的优化效果。

代码演示

自定义容器

Note: you need to set height/overflow style.

  1. /* eslint no-dupe-keys: 0, no-mixed-operators: 0 */
  2. import { ListView } from 'antd-mobile';
  3. function MyBody(props) {
  4. return (
  5. <div className="am-list-body my-body">
  6. <span style={{ display: 'none' }}>you can custom body wrap element</span>
  7. {props.children}
  8. </div>
  9. );
  10. }
  11. const data = [
  12. {
  13. img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png',
  14. title: 'Meet hotel',
  15. des: '不是所有的兼职汪都需要风吹日晒',
  16. },
  17. {
  18. img: 'https://zos.alipayobjects.com/rmsportal/XmwCzSeJiqpkuMB.png',
  19. title: 'McDonald\'s invites you',
  20. des: '不是所有的兼职汪都需要风吹日晒',
  21. },
  22. {
  23. img: 'https://zos.alipayobjects.com/rmsportal/hfVtzEhPzTUewPm.png',
  24. title: 'Eat the week',
  25. des: '不是所有的兼职汪都需要风吹日晒',
  26. },
  27. ];
  28. const NUM_SECTIONS = 5;
  29. const NUM_ROWS_PER_SECTION = 5;
  30. let pageIndex = 0;
  31. const dataBlobs = {};
  32. let sectionIDs = [];
  33. let rowIDs = [];
  34. function genData(pIndex = 0) {
  35. for (let i = 0; i < NUM_SECTIONS; i++) {
  36. const ii = (pIndex * NUM_SECTIONS) + i;
  37. const sectionName = `Section ${ii}`;
  38. sectionIDs.push(sectionName);
  39. dataBlobs[sectionName] = sectionName;
  40. rowIDs[ii] = [];
  41. for (let jj = 0; jj < NUM_ROWS_PER_SECTION; jj++) {
  42. const rowName = `S${ii}, R${jj}`;
  43. rowIDs[ii].push(rowName);
  44. dataBlobs[rowName] = rowName;
  45. }
  46. }
  47. sectionIDs = [...sectionIDs];
  48. rowIDs = [...rowIDs];
  49. }
  50. class Demo extends React.Component {
  51. constructor(props) {
  52. super(props);
  53. const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
  54. const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID];
  55. const dataSource = new ListView.DataSource({
  56. getRowData,
  57. getSectionHeaderData: getSectionData,
  58. rowHasChanged: (row1, row2) => row1 !== row2,
  59. sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
  60. });
  61. this.state = {
  62. dataSource,
  63. isLoading: true,
  64. height: document.documentElement.clientHeight * 3 / 4,
  65. };
  66. }
  67. componentDidMount() {
  68. // you can scroll to the specified position
  69. // setTimeout(() => this.lv.scrollTo(0, 120), 800);
  70. const hei = document.documentElement.clientHeight - ReactDOM.findDOMNode(this.lv).parentNode.offsetTop;
  71. // simulate initial Ajax
  72. setTimeout(() => {
  73. genData();
  74. this.setState({
  75. dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlobs, sectionIDs, rowIDs),
  76. isLoading: false,
  77. height: hei,
  78. });
  79. }, 600);
  80. }
  81. // If you use redux, the data maybe at props, you need use `componentWillReceiveProps`
  82. // componentWillReceiveProps(nextProps) {
  83. // if (nextProps.dataSource !== this.props.dataSource) {
  84. // this.setState({
  85. // dataSource: this.state.dataSource.cloneWithRowsAndSections(nextProps.dataSource),
  86. // });
  87. // }
  88. // }
  89. onEndReached = (event) => {
  90. // load new data
  91. // hasMore: from backend data, indicates whether it is the last page, here is false
  92. if (this.state.isLoading && !this.state.hasMore) {
  93. return;
  94. }
  95. console.log('reach end', event);
  96. this.setState({ isLoading: true });
  97. setTimeout(() => {
  98. genData(++pageIndex);
  99. this.setState({
  100. dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlobs, sectionIDs, rowIDs),
  101. isLoading: false,
  102. });
  103. }, 1000);
  104. }
  105. render() {
  106. const separator = (sectionID, rowID) => (
  107. <div
  108. key={`${sectionID}-${rowID}`}
  109. style={{
  110. backgroundColor: '#F5F5F9',
  111. height: 8,
  112. borderTop: '1px solid #ECECED',
  113. borderBottom: '1px solid #ECECED',
  114. }}
  115. />
  116. );
  117. let index = data.length - 1;
  118. const row = (rowData, sectionID, rowID) => {
  119. if (index < 0) {
  120. index = data.length - 1;
  121. }
  122. const obj = data[index--];
  123. return (
  124. <div key={rowID} style={{ padding: '0 15px' }}>
  125. <div
  126. style={{
  127. lineHeight: '50px',
  128. color: '#888',
  129. fontSize: 18,
  130. borderBottom: '1px solid #F6F6F6',
  131. }}
  132. >{obj.title}</div>
  133. <div style={{ display: '-webkit-box', display: 'flex', padding: '15px 0' }}>
  134. <img style={{ height: '64px', marginRight: '15px' }} src={obj.img} alt="" />
  135. <div style={{ lineHeight: 1 }}>
  136. <div style={{ marginBottom: '8px', fontWeight: 'bold' }}>{obj.des}</div>
  137. <div><span style={{ fontSize: '30px', color: '#FF6E27' }}>35</span>¥ {rowID}</div>
  138. </div>
  139. </div>
  140. </div>
  141. );
  142. };
  143. return (
  144. <ListView
  145. ref={el => this.lv = el}
  146. dataSource={this.state.dataSource}
  147. renderHeader={() => <span>header</span>}
  148. renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}>
  149. {this.state.isLoading ? 'Loading...' : 'Loaded'}
  150. </div>)}
  151. renderSectionHeader={sectionData => (
  152. <div>{`Task ${sectionData.split(' ')[1]}`}</div>
  153. )}
  154. renderBodyComponent={() => <MyBody />}
  155. renderRow={row}
  156. renderSeparator={separator}
  157. style={{
  158. height: this.state.height,
  159. overflow: 'auto',
  160. }}
  161. pageSize={4}
  162. onScroll={() => { console.log('scroll'); }}
  163. scrollRenderAheadDistance={500}
  164. onEndReached={this.onEndReached}
  165. onEndReachedThreshold={10}
  166. />
  167. );
  168. }
  169. }
  170. ReactDOM.render(<Demo />, mountNode);

body 容器

use html body as a scroll container.

  1. /* eslint no-dupe-keys: 0 */
  2. import { ListView } from 'antd-mobile';
  3. const data = [
  4. {
  5. img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png',
  6. title: 'Meet hotel',
  7. des: '不是所有的兼职汪都需要风吹日晒',
  8. },
  9. {
  10. img: 'https://zos.alipayobjects.com/rmsportal/XmwCzSeJiqpkuMB.png',
  11. title: 'McDonald\'s invites you',
  12. des: '不是所有的兼职汪都需要风吹日晒',
  13. },
  14. {
  15. img: 'https://zos.alipayobjects.com/rmsportal/hfVtzEhPzTUewPm.png',
  16. title: 'Eat the week',
  17. des: '不是所有的兼职汪都需要风吹日晒',
  18. },
  19. ];
  20. const NUM_ROWS = 20;
  21. let pageIndex = 0;
  22. function genData(pIndex = 0) {
  23. const dataBlob = {};
  24. for (let i = 0; i < NUM_ROWS; i++) {
  25. const ii = (pIndex * NUM_ROWS) + i;
  26. dataBlob[`${ii}`] = `row - ${ii}`;
  27. }
  28. return dataBlob;
  29. }
  30. class Demo extends React.Component {
  31. constructor(props) {
  32. super(props);
  33. const dataSource = new ListView.DataSource({
  34. rowHasChanged: (row1, row2) => row1 !== row2,
  35. });
  36. this.state = {
  37. dataSource,
  38. isLoading: true,
  39. };
  40. }
  41. componentDidMount() {
  42. // you can scroll to the specified position
  43. // setTimeout(() => this.lv.scrollTo(0, 120), 800);
  44. // simulate initial Ajax
  45. setTimeout(() => {
  46. this.rData = genData();
  47. this.setState({
  48. dataSource: this.state.dataSource.cloneWithRows(this.rData),
  49. isLoading: false,
  50. });
  51. }, 600);
  52. }
  53. // If you use redux, the data maybe at props, you need use `componentWillReceiveProps`
  54. // componentWillReceiveProps(nextProps) {
  55. // if (nextProps.dataSource !== this.props.dataSource) {
  56. // this.setState({
  57. // dataSource: this.state.dataSource.cloneWithRows(nextProps.dataSource),
  58. // });
  59. // }
  60. // }
  61. onEndReached = (event) => {
  62. // load new data
  63. // hasMore: from backend data, indicates whether it is the last page, here is false
  64. if (this.state.isLoading && !this.state.hasMore) {
  65. return;
  66. }
  67. console.log('reach end', event);
  68. this.setState({ isLoading: true });
  69. setTimeout(() => {
  70. this.rData = { ...this.rData, ...genData(++pageIndex) };
  71. this.setState({
  72. dataSource: this.state.dataSource.cloneWithRows(this.rData),
  73. isLoading: false,
  74. });
  75. }, 1000);
  76. }
  77. render() {
  78. const separator = (sectionID, rowID) => (
  79. <div
  80. key={`${sectionID}-${rowID}`}
  81. style={{
  82. backgroundColor: '#F5F5F9',
  83. height: 8,
  84. borderTop: '1px solid #ECECED',
  85. borderBottom: '1px solid #ECECED',
  86. }}
  87. />
  88. );
  89. let index = data.length - 1;
  90. const row = (rowData, sectionID, rowID) => {
  91. if (index < 0) {
  92. index = data.length - 1;
  93. }
  94. const obj = data[index--];
  95. return (
  96. <div key={rowID} style={{ padding: '0 15px' }}>
  97. <div
  98. style={{
  99. lineHeight: '50px',
  100. color: '#888',
  101. fontSize: 18,
  102. borderBottom: '1px solid #F6F6F6',
  103. }}
  104. >{obj.title}</div>
  105. <div style={{ display: '-webkit-box', display: 'flex', padding: '15px 0' }}>
  106. <img style={{ height: '64px', marginRight: '15px' }} src={obj.img} alt="" />
  107. <div style={{ lineHeight: 1 }}>
  108. <div style={{ marginBottom: '8px', fontWeight: 'bold' }}>{obj.des}</div>
  109. <div><span style={{ fontSize: '30px', color: '#FF6E27' }}>{rowID}</span>¥</div>
  110. </div>
  111. </div>
  112. </div>
  113. );
  114. };
  115. return (
  116. <ListView
  117. ref={el => this.lv = el}
  118. dataSource={this.state.dataSource}
  119. renderHeader={() => <span>header</span>}
  120. renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}>
  121. {this.state.isLoading ? 'Loading...' : 'Loaded'}
  122. </div>)}
  123. renderRow={row}
  124. renderSeparator={separator}
  125. className="am-list"
  126. pageSize={4}
  127. useBodyScroll
  128. onScroll={() => { console.log('scroll'); }}
  129. scrollRenderAheadDistance={500}
  130. onEndReached={this.onEndReached}
  131. onEndReachedThreshold={10}
  132. />
  133. );
  134. }
  135. }
  136. ReactDOM.render(<Demo />, mountNode);

标题吸顶(body 容器)

sticky block header to the top of the page

  1. /* eslint no-dupe-keys: 0 */
  2. import { ListView } from 'antd-mobile';
  3. import { StickyContainer, Sticky } from 'react-sticky';
  4. const data = [
  5. {
  6. img: 'https://zos.alipayobjects.com/rmsportal/dKbkpPXKfvZzWCM.png',
  7. title: 'Meet hotel',
  8. des: '不是所有的兼职汪都需要风吹日晒',
  9. },
  10. {
  11. img: 'https://zos.alipayobjects.com/rmsportal/XmwCzSeJiqpkuMB.png',
  12. title: 'McDonald\'s invites you',
  13. des: '不是所有的兼职汪都需要风吹日晒',
  14. },
  15. {
  16. img: 'https://zos.alipayobjects.com/rmsportal/hfVtzEhPzTUewPm.png',
  17. title: 'Eat the week',
  18. des: '不是所有的兼职汪都需要风吹日晒',
  19. },
  20. ];
  21. const NUM_SECTIONS = 5;
  22. const NUM_ROWS_PER_SECTION = 5;
  23. let pageIndex = 0;
  24. const dataBlobs = {};
  25. let sectionIDs = [];
  26. let rowIDs = [];
  27. function genData(pIndex = 0) {
  28. for (let i = 0; i < NUM_SECTIONS; i++) {
  29. const ii = (pIndex * NUM_SECTIONS) + i;
  30. const sectionName = `Section ${ii}`;
  31. sectionIDs.push(sectionName);
  32. dataBlobs[sectionName] = sectionName;
  33. rowIDs[ii] = [];
  34. for (let jj = 0; jj < NUM_ROWS_PER_SECTION; jj++) {
  35. const rowName = `S${ii}, R${jj}`;
  36. rowIDs[ii].push(rowName);
  37. dataBlobs[rowName] = rowName;
  38. }
  39. }
  40. sectionIDs = [...sectionIDs];
  41. rowIDs = [...rowIDs];
  42. }
  43. class Demo extends React.Component {
  44. constructor(props) {
  45. super(props);
  46. const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
  47. const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID];
  48. const dataSource = new ListView.DataSource({
  49. getRowData,
  50. getSectionHeaderData: getSectionData,
  51. rowHasChanged: (row1, row2) => row1 !== row2,
  52. sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
  53. });
  54. this.state = {
  55. dataSource,
  56. isLoading: true,
  57. };
  58. }
  59. componentDidMount() {
  60. // you can scroll to the specified position
  61. // setTimeout(() => this.lv.scrollTo(0, 120), 800);
  62. // simulate initial Ajax
  63. setTimeout(() => {
  64. genData();
  65. this.setState({
  66. dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlobs, sectionIDs, rowIDs),
  67. isLoading: false,
  68. });
  69. }, 600);
  70. }
  71. // If you use redux, the data maybe at props, you need use `componentWillReceiveProps`
  72. // componentWillReceiveProps(nextProps) {
  73. // if (nextProps.dataSource !== this.props.dataSource) {
  74. // this.setState({
  75. // dataSource: this.state.dataSource.cloneWithRowsAndSections(nextProps.dataSource),
  76. // });
  77. // }
  78. // }
  79. onEndReached = (event) => {
  80. // load new data
  81. // hasMore: from backend data, indicates whether it is the last page, here is false
  82. if (this.state.isLoading && !this.state.hasMore) {
  83. return;
  84. }
  85. console.log('reach end', event);
  86. this.setState({ isLoading: true });
  87. setTimeout(() => {
  88. genData(++pageIndex);
  89. this.setState({
  90. dataSource: this.state.dataSource.cloneWithRowsAndSections(dataBlobs, sectionIDs, rowIDs),
  91. isLoading: false,
  92. });
  93. }, 1000);
  94. }
  95. render() {
  96. const separator = (sectionID, rowID) => (
  97. <div
  98. key={`${sectionID}-${rowID}`}
  99. style={{
  100. backgroundColor: '#F5F5F9',
  101. height: 8,
  102. borderTop: '1px solid #ECECED',
  103. borderBottom: '1px solid #ECECED',
  104. }}
  105. />
  106. );
  107. let index = data.length - 1;
  108. const row = (rowData, sectionID, rowID) => {
  109. if (index < 0) {
  110. index = data.length - 1;
  111. }
  112. const obj = data[index--];
  113. return (
  114. <div key={rowID} style={{ padding: '0 15px' }}>
  115. <div
  116. style={{
  117. lineHeight: '50px',
  118. color: '#888',
  119. fontSize: 18,
  120. borderBottom: '1px solid #F6F6F6',
  121. }}
  122. >{obj.title}</div>
  123. <div style={{ display: '-webkit-box', display: 'flex', padding: '15px 0' }}>
  124. <img style={{ height: '64px', marginRight: '15px' }} src={obj.img} alt="" />
  125. <div style={{ lineHeight: 1 }}>
  126. <div style={{ marginBottom: '8px', fontWeight: 'bold' }}>{obj.des}</div>
  127. <div><span style={{ fontSize: '30px', color: '#FF6E27' }}>35</span>¥ {rowID}</div>
  128. </div>
  129. </div>
  130. </div>
  131. );
  132. };
  133. return (
  134. <ListView
  135. ref={el => this.lv = el}
  136. dataSource={this.state.dataSource}
  137. className="am-list sticky-list"
  138. useBodyScroll
  139. renderSectionWrapper={sectionID => (
  140. <StickyContainer
  141. key={`s_${sectionID}_c`}
  142. className="sticky-container"
  143. style={{ zIndex: 4 }}
  144. />
  145. )}
  146. renderSectionHeader={sectionData => (
  147. <Sticky>
  148. {({
  149. style,
  150. }) => (
  151. <div
  152. className="sticky"
  153. style={{
  154. ...style,
  155. zIndex: 3,
  156. backgroundColor: parseInt(sectionData.replace('Section ', ''), 10) % 2 ?
  157. '#5890ff' : '#F8591A',
  158. color: 'white',
  159. }}
  160. >{`Task ${sectionData.split(' ')[1]}`}</div>
  161. )}
  162. </Sticky>
  163. )}
  164. renderHeader={() => <span>header</span>}
  165. renderFooter={() => (<div style={{ padding: 30, textAlign: 'center' }}>
  166. {this.state.isLoading ? 'Loading...' : 'Loaded'}
  167. </div>)}
  168. renderRow={row}
  169. renderSeparator={separator}
  170. pageSize={4}
  171. onScroll={() => { console.log('scroll'); }}
  172. scrollEventThrottle={200}
  173. onEndReached={this.onEndReached}
  174. onEndReachedThreshold={10}
  175. />
  176. );
  177. }
  178. }
  179. ReactDOM.render(<Demo />, mountNode);
  1. .sticky-list .sticky-container .am-list-item { padding-left: 0; }
  2. .sticky-list .sticky-container .am-list-line { padding-right: 0; }
  3. .sticky-list .sticky-container .am-list-line .am-list-content { padding-top: 0; padding-bottom: 0; }
  4. .sticky-list .sticky-container .sticky { padding: 7px 15px; transform: none; }

索引列表(标题吸顶)

sticky index List

  1. import { province } from 'antd-mobile-demo-data';
  2. import { StickyContainer, Sticky } from 'react-sticky';
  3. import { ListView, List, SearchBar } from 'antd-mobile';
  4. const { Item } = List;
  5. function genData(ds, provinceData) {
  6. const dataBlob = {};
  7. const sectionIDs = [];
  8. const rowIDs = [];
  9. Object.keys(provinceData).forEach((item, index) => {
  10. sectionIDs.push(item);
  11. dataBlob[item] = item;
  12. rowIDs[index] = [];
  13. provinceData[item].forEach((jj) => {
  14. rowIDs[index].push(jj.value);
  15. dataBlob[jj.value] = jj.label;
  16. });
  17. });
  18. return ds.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs);
  19. }
  20. class Demo extends React.Component {
  21. constructor(props) {
  22. super(props);
  23. const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
  24. const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID];
  25. const dataSource = new ListView.DataSource({
  26. getRowData,
  27. getSectionHeaderData: getSectionData,
  28. rowHasChanged: (row1, row2) => row1 !== row2,
  29. sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
  30. });
  31. this.state = {
  32. inputValue: '',
  33. dataSource,
  34. isLoading: true,
  35. };
  36. }
  37. componentDidMount() {
  38. // simulate initial Ajax
  39. setTimeout(() => {
  40. this.setState({
  41. dataSource: genData(this.state.dataSource, province),
  42. isLoading: false,
  43. });
  44. }, 600);
  45. }
  46. onSearch = (val) => {
  47. const pd = { ...province };
  48. Object.keys(pd).forEach((item) => {
  49. const arr = pd[item].filter(jj => jj.spell.toLocaleLowerCase().indexOf(val) > -1);
  50. if (!arr.length) {
  51. delete pd[item];
  52. } else {
  53. pd[item] = arr;
  54. }
  55. });
  56. this.setState({
  57. inputValue: val,
  58. dataSource: genData(this.state.dataSource, pd),
  59. });
  60. }
  61. render() {
  62. return (<div style={{ paddingTop: '44px', position: 'relative' }}>
  63. <div style={{ position: 'absolute', top: 0, left: 0, right: 0 }}>
  64. <SearchBar
  65. value={this.state.inputValue}
  66. placeholder="Search"
  67. onChange={this.onSearch}
  68. onClear={() => { console.log('onClear'); }}
  69. onCancel={() => { console.log('onCancel'); }}
  70. />
  71. </div>
  72. <ListView.IndexedList
  73. dataSource={this.state.dataSource}
  74. className="am-list sticky-list"
  75. useBodyScroll
  76. renderSectionWrapper={sectionID => (
  77. <StickyContainer
  78. key={`s_${sectionID}_c`}
  79. className="sticky-container"
  80. style={{ zIndex: 4 }}
  81. />
  82. )}
  83. renderSectionHeader={sectionData => (
  84. <Sticky>
  85. {({
  86. style,
  87. }) => (
  88. <div
  89. className="sticky"
  90. style={{
  91. ...style,
  92. zIndex: 3,
  93. backgroundColor: sectionData.charCodeAt(0) % 2 ? '#5890ff' : '#F8591A',
  94. color: 'white',
  95. }}
  96. >{sectionData}</div>
  97. )}
  98. </Sticky>
  99. )}
  100. renderHeader={() => <span>custom header</span>}
  101. renderFooter={() => <span>custom footer</span>}
  102. renderRow={rowData => (<Item>{rowData}</Item>)}
  103. quickSearchBarStyle={{
  104. top: 85,
  105. }}
  106. delayTime={10}
  107. delayActivityIndicator={<div style={{ padding: 25, textAlign: 'center' }}>rendering...</div>}
  108. />
  109. </div>);
  110. }
  111. }
  112. ReactDOM.render(<Demo />, mountNode);
  1. .sticky-list .sticky-container .am-list-item { padding-left: 0; }
  2. .sticky-list .sticky-container .am-list-line { padding-right: 0; }
  3. .sticky-list .sticky-container .am-list-line .am-list-content { padding-top: 0; padding-bottom: 0; }
  4. .sticky-list .sticky-container .sticky { padding: 7px 15px; transform: none; }

ListView长列表 - 图1

API

属性说明类型默认值
dataSourceListView.DataSource 实例ListViewDataSource-
initialListSize指定在组件刚挂载的时候渲染多少行数据,用这个属性来确保首屏显示合适数量的数据number-
onEndReached当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用(event?) => {}-
onEndReachedThreshold调用onEndReached之前的临界值,单位是像素number1000
pageSize每次事件循环(每帧)渲染的行数number1
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当一个行接近屏幕范围多少像素之内的时候,就开始渲染这一行number1000
contentContainerStyle这些样式会应用到一个内层的内容容器上,所有的子视图都会包裹在内容容器内Object-
horizontal当此属性为true的时候,所有的的子视图会在水平方向上排成一行,而不是默认的在垂直方向上排成一列boolfalse
onContentSizeChange此函数会在 ScrollView 内部可滚动内容的视图发生变化时调用。(contentWidth, contentHeight) => {}-
onScroll在滚动的过程中,每帧最多调用一次此回调函数。调用的频率可以用scrollEventThrottle属性来控制。e => {}-
scrollEventThrottle控制在滚动过程中,scroll事件被调用的频率number50
onLayout当组件挂载或者布局变化的时候调用({nativeEvent:{ layout:{ width, height }}}) => {}-
——
renderBodyComponent自定义 body 的包裹组件() => renderable-
renderSectionWrapper渲染自定义的区块包裹组件(sectionID) => renderable-
renderSectionBodyWrapper渲染自定义的区块 body 包裹组件(sectionID) => renderable-
useBodyScroll使用 html 的 body 作为滚动容器boolfalse
pullToRefresh使用 pullToRefresh, 你需要和 PullToRefresh 组件一起使用boolfalse

方法

  • getMetrics() - 导出一些用于性能分析的数据。

  • scrollTo(…args) - 滚动到指定的x, y偏移处(暂不支持过渡动画)。

ListView.IndexedList

此组件常用于 “通讯录”/“城市列表” 等场景中,支持索引导航功能。

你可以使用 ListView 上的几乎所有 APIs。

注意:由于索引列表可以点击任一项索引来定位其内容、即内容需要直接滚动到任意位置,这样就难以做到像 ListView 一样能在滚动时自动懒渲染。目前实现上只支持分两步渲染,能借此达到首屏优先显示目的,但如果列表数据量过大时、整体性能仍会有影响。

属性说明类型默认值
quickSearchBarTop快捷导航栏最顶部按钮、常用于回到顶部object{value:string, label:string}{ value: '#', label: '#' }
quickSearchBarStylequickSearchBar 的 styleobject-
onQuickSearch快捷导航切换时调用(sectionID: any, topId?:any) => void-
showQuickSearchIndicatorwhether show quick search indicatorboolfalse
delayTime延迟渲染时间设置(用于首屏优化,一开始渲染initialListSize数量的数据,在此时间后、延迟渲染剩余的数据项、即totalRowCount - initialListSizenumber100ms
delayActivityIndicator延迟渲染的 loading 指示器react node-

提示

ListView 有两种类型的滚动容器:
  • 局部 div 容器

    • 默认,注意:需要手动给 ListView 设置高度
  • html 的 body 容器

    • 设置useBodyScroll后生效 (不需要设置高度)
对 dataSource 对象变化时的处理方式是什么?何时调用 onEndReached 方法? ListView 在 componentWillReceiveProps 里会监听 dataSource 对象的变化,并做一次this._renderMoreRowsIfNeeded() ,由于此时this.state.curRenderedRowsCount === this.props.dataSource.getRowCount()即已经渲染的数据与 dataSource 里已有的数据项个数相同,所以 ListView 认为应该再调用 onEndReached 方法。 onEndReached 为什么会不停调用?520#issuecomment-263510596其他问题:#633#573#541