UNPKG

react-native-img-browser

Version:

此组件基于react-native-photo-browser@0.4.0进行修改,主要修复其大量图片时,滑动动画不流畅;图片多时打开大图加载卡顿或加载不出来,将react-native-photo-browser中的FullScreenContainer文件中的ListView组件替换成FlatList组件

366 lines (319 loc) 11.7 kB
import React from 'react'; import PropTypes from 'prop-types'; import { DeviceEventEmitter, Dimensions, ListView, View, ViewPagerAndroid, StyleSheet, Platform, StatusBar, TouchableWithoutFeedback, ViewPropTypes, FlatList } from 'react-native'; import Constants from './constants'; import { BottomBar } from './bar'; import { Photo } from './media'; export default class FullScreenContainer extends React.Component { static propTypes = { style: ViewPropTypes.style, dataSource: PropTypes.instanceOf(ListView.DataSource).isRequired, mediaList: PropTypes.array.isRequired, /* * opens grid view */ onGridButtonTap: PropTypes.func, /* * Display top bar */ displayTopBar: PropTypes.bool, /* * updates top bar title */ updateTitle: PropTypes.func, /* * displays/hides top bar */ toggleTopBar: PropTypes.func, /* * refresh the list to apply selection change */ onMediaSelection: PropTypes.func, /* * those props are inherited from main PhotoBrowser component * i.e. index.js */ initialIndex: PropTypes.number, alwaysShowControls: PropTypes.bool, displayActionButton: PropTypes.bool, displayNavArrows: PropTypes.bool, alwaysDisplayStatusBar: PropTypes.bool, displaySelectionButtons: PropTypes.bool, enableGrid: PropTypes.bool, useCircleProgress: PropTypes.bool, onActionButton: PropTypes.func, onPhotoLongPress: PropTypes.func, delayLongPress: PropTypes.number }; static defaultProps = { initialIndex: 0, displayTopBar: true, displayNavArrows: false, alwaysDisplayStatusBar: false, displaySelectionButtons: false, enableGrid: true, onGridButtonTap: () => {}, onPhotoLongPress: () => {}, delayLongPress: 1000, }; static isListView = false; constructor(props, context) { super(props, context); this._renderRow = this._renderRow.bind(this); this._toggleControls = this._toggleControls.bind(this); this._onScroll = this._onScroll.bind(this); this._onPageSelected = this._onPageSelected.bind(this); this._onNextButtonTapped = this._onNextButtonTapped.bind(this); this._onPhotoLongPress = this._onPhotoLongPress.bind(this); this._onPreviousButtonTapped = this._onPreviousButtonTapped.bind(this); this._onActionButtonTapped = this._onActionButtonTapped.bind(this); this.photoRefs = []; this.state = { currentIndex: props.initialIndex, currentMedia: props.mediaList[props.initialIndex], controlsDisplayed: props.displayTopBar, }; } componentDidMount() { DeviceEventEmitter.addListener('didUpdateDimensions', () => { this.photoRefs.map(p => p && p.forceUpdate()); this.openPage(this.state.currentIndex, false); }); this.openPage(this.state.currentIndex, false); } openPage(index, animated) { if (!this.scrollView) { return; } if (Platform.OS === 'ios') { if(FullScreenContainer.isListView){ const screenWidth = Dimensions.get('window').width; this.scrollView.scrollTo({ x: index * screenWidth, animated, }); } else { /*this.scrollView.scrollToOffset({ x: index * screenWidth, animated, }); */ this.scrollView.scrollToIndex({viewPosition: 0, index: Number(index)}); } } else { this.scrollView.setPageWithoutAnimation(index); } this._updatePageIndex(index); } _updatePageIndex(index) { this.setState({ currentIndex: index, currentMedia: this.props.mediaList[index], }, () => { this._triggerPhotoLoad(index); const newTitle = `${index + 1} of ${this.props.dataSource.getRowCount()}`; this.props.updateTitle(newTitle); }); } _triggerPhotoLoad(index) { const photo = this.photoRefs[index]; if (photo) { photo.load(); } else { // HACK: photo might be undefined when user taps a photo from gridview // that hasn't been rendered yet. // photo is rendered after listView's scrollTo method call // and i'm deferring photo load method for that. setTimeout(this._triggerPhotoLoad.bind(this, index), 200); } } _toggleControls() { const { alwaysShowControls, toggleTopBar } = this.props; if (!alwaysShowControls) { const controlsDisplayed = !this.state.controlsDisplayed; this.setState({ controlsDisplayed, }); toggleTopBar(controlsDisplayed); } } _onNextButtonTapped() { let nextIndex = this.state.currentIndex + 1; // go back to the first item when there is no more next item if (nextIndex > this.props.dataSource.getRowCount() - 1) { nextIndex = 0; } this.openPage(nextIndex, false); } _onPreviousButtonTapped() { let prevIndex = this.state.currentIndex - 1; // go to the last item when there is no more previous item if (prevIndex < 0) { prevIndex = this.props.dataSource.getRowCount() - 1; } this.openPage(prevIndex, false); } _onActionButtonTapped() { const onActionButton = this.props.onActionButton; // action behaviour must be implemented by the client // so, call the client method or simply ignore this event if (onActionButton) { const { currentMedia, currentIndex } = this.state; onActionButton(currentMedia, currentIndex); } } _onScroll(e) { const event = e.nativeEvent; const layoutWidth = event.layoutMeasurement.width || Dimensions.get('window').width; const newIndex = Math.floor((event.contentOffset.x + 0.5 * layoutWidth) / layoutWidth); this._onPageSelected(newIndex); } _onPageSelected(page) { const { currentIndex } = this.state; let newIndex = page; // handle ViewPagerAndroid argument if (typeof newIndex === 'object') { newIndex = newIndex.nativeEvent.position; } if (currentIndex !== newIndex) { this._updatePageIndex(newIndex); if (this.state.controlsDisplayed && !this.props.displayTopBar) { this._toggleControls(); } } } _onPhotoLongPress() { const onPhotoLongPress = this.props.onPhotoLongPress; const { currentMedia, currentIndex } = this.state; onPhotoLongPress(currentMedia, currentIndex); } _renderRow(media: Object, sectionID: number, rowID: number) { const { displaySelectionButtons, onMediaSelection, useCircleProgress, } = this.props; return ( <View key={`row_${rowID}`} style={styles.flex}> <TouchableWithoutFeedback onPress={this._toggleControls} onLongPress={this._onPhotoLongPress} delayLongPress={this.props.delayLongPress}> <Photo ref={ref => this.photoRefs[rowID] = ref} lazyLoad useCircleProgress={useCircleProgress} uri={media.photo} displaySelectionButtons={displaySelectionButtons} selected={media.selected} onSelection={(isSelected) => { onMediaSelection(rowID, isSelected); }} /> </TouchableWithoutFeedback> </View> ); } _renderScrollableContent() { const { dataSource, mediaList } = this.props; if (Platform.OS === 'android') { return ( <ViewPagerAndroid style={styles.flex} ref={scrollView => this.scrollView = scrollView} onPageSelected={this._onPageSelected} > {mediaList.map((child, idx) => this._renderRow(child, 0, idx))} </ViewPagerAndroid> ); } if(FullScreenContainer.isListView){ return ( <ListView ref={scrollView => this.scrollView = scrollView} dataSource={dataSource} renderRow={this._renderRow} onScroll={this._onScroll} horizontal pagingEnabled showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} directionalLockEnabled scrollEventThrottle={16} /> ); } let w = Dimensions.get('window').width; return ( <FlatList ref={scrollView => this.scrollView = scrollView} data={mediaList} keyExtractor = {(item, index) => ("key" + index)} renderItem={({item,index,separators})=>this._renderRow(item,separators,index)} onScroll={this._onScroll} horizontal={true} pagingEnabled={true} showsHorizontalScrollIndicator={false} showsVerticalScrollIndicator={false} directionalLockEnabled={true} getItemLayout={(data,index)=>( {length: w, offset: w * index, index} )} //scrollEventThrottle={16} />); } render() { const { displayNavArrows, alwaysDisplayStatusBar, displayActionButton, onGridButtonTap, enableGrid, } = this.props; const { controlsDisplayed, currentMedia } = this.state; const BottomBarComponent = this.props.bottomBarComponent || BottomBar; return ( <View style={styles.flex}> <StatusBar hidden={alwaysDisplayStatusBar ? false : !controlsDisplayed} showHideTransition={'slide'} barStyle={'light-content'} animated translucent /> {this._renderScrollableContent()} <BottomBarComponent displayed={controlsDisplayed} height={Constants.TOOLBAR_HEIGHT} displayNavArrows={displayNavArrows} displayGridButton={enableGrid} displayActionButton={displayActionButton} caption={currentMedia.caption} media={currentMedia} onPrev={this._onPreviousButtonTapped} onNext={this._onNextButtonTapped} onGrid={onGridButtonTap} onAction={this._onActionButtonTapped} /> </View> ); } } const styles = StyleSheet.create({ flex: { flex: 1, }, });