美文网首页
React-Native 瀑布流布局

React-Native 瀑布流布局

作者: 唯有她美 | 来源:发表于2019-02-19 20:09 被阅读0次

参差不起的列表页对于视觉效果确实有很好的体验,今天就介绍一下React-Native实现瀑布流的效果。

实现效果参考与:https://www.jianshu.com/p/88a56de0191d

效果图:

创建MasonryList.js在需要的地方引用

import*asReactfrom'react';

import{

VirtualizedList,

View,

ScrollView,

StyleSheet,

findNodeHandle,

RefreshControl,

}from'react-native';

typeColumn= {

index:number,

totalHeight:number,

data:Array,

heights:Array,

};

const_stateFromProps= ({numColumns,data,getHeightForItem})=>{

constcolumns:Array =Array.from({

length:numColumns,

}).map((col,i)=>({

index:i,

totalHeight:0,

data:[],

heights:[],

}));

data.forEach((item,index)=>{

constheight=getHeightForItem({item,index});

constcolumn=columns.reduce(

(prev,cur)=>(cur.totalHeight

columns[0],

);

column.data.push(item);

column.heights.push(height);

column.totalHeight+=height;

});

return{columns};

};

exporttypeProps= {

data:Array,

numColumns:number,

renderItem: ({item:any,index:number,column:number})=>?React.Element<

any,

>,

getHeightForItem: ({item:any,index:number})=>number,

ListHeaderComponent?: ?React.ComponentType,

ListEmptyComponent?: ?React.ComponentType,

/**

  * Used to extract a unique key for a given item at the specified index. Key is used for caching

  * and as the react key to track item re-ordering. The default extractor checks `item.key`, then

  * falls back to using the index, like React does.

  */

keyExtractor?: (item:any,index:number)=>string,

// onEndReached will get called once per column, not ideal but should not cause

// issues with isLoading checks.

onEndReached?: ?(info: {distanceFromEnd:number})=>void,

contentContainerStyle?:any,

onScroll?: (event:Object)=>void,

onScrollBeginDrag?: (event:Object)=>void,

onScrollEndDrag?: (event:Object)=>void,

onMomentumScrollEnd?: (event:Object)=>void,

onEndReachedThreshold?: ?number,

scrollEventThrottle:number,

renderScrollComponent: (props:Object)=>React.Element,

/**

  * Set this true while waiting for new data from a refresh.

  */

refreshing?: ?boolean,

/**

  * If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make

  * sure to also set the `refreshing` prop correctly.

  */

onRefresh?: ?Function,

};

typeState= {

columns:Array,

};

// This will get cloned and added a bunch of props that are supposed to be on

// ScrollView so we wan't to make sure we don't pass them over (especially

// onLayout since it exists on both).

classFakeScrollViewextendsReact.Component<{style?:any,children?:any}> {

render() {

return(

<Viewstyle={this.props.style}>

{this.props.children}

</View>

);

}

}

exportdefaultclassMasonryListextendsReact.Component {

staticdefaultProps= {

scrollEventThrottle:50,

numColumns:1,

renderScrollComponent:(props:Props)=>{

if(props.onRefresh&&props.refreshing!=null) {

return(

<ScrollView

{...props}

refreshControl={

<RefreshControl

refreshing={props.refreshing}

onRefresh={props.onRefresh}

/>

}

/>

);

}

return<ScrollView{...props}/>;

},

};

state=_stateFromProps(this.props);

_listRefs:Array = [];

_scrollRef: ?ScrollView;

_endsReached=0;

componentWillReceiveProps(newProps:Props) {

this.setState(_stateFromProps(newProps));

}

getScrollResponder() {

if(this._scrollRef&&this._scrollRef.getScrollResponder) {

returnthis._scrollRef.getScrollResponder();

}

returnnull;

}

getScrollableNode() {

if(this._scrollRef&&this._scrollRef.getScrollableNode) {

returnthis._scrollRef.getScrollableNode();

}

returnfindNodeHandle(this._scrollRef);

}

scrollToOffset({offset,animated}:any) {

if(this._scrollRef) {

this._scrollRef.scrollTo({y:offset,animated});

}

}

_onLayout=event=>{

this._listRefs.forEach(

list=>list&&list._onLayout&&list._onLayout(event),

);

};

_onContentSizeChange= (width,height)=>{

this._listRefs.forEach(

list=>

list&&

list._onContentSizeChange&&

list._onContentSizeChange(width,height),

);

};

_onScroll=event=>{

if(this.props.onScroll) {

this.props.onScroll(event);

}

this._listRefs.forEach(

list=>list&&list._onScroll&&list._onScroll(event),

);

};

_onScrollBeginDrag=event=>{

if(this.props.onScrollBeginDrag) {

this.props.onScrollBeginDrag(event);

}

this._listRefs.forEach(

list=>list&&list._onScrollBeginDrag&&list._onScrollBeginDrag(event),

);

};

_onScrollEndDrag=event=>{

if(this.props.onScrollEndDrag) {

this.props.onScrollEndDrag(event);

}

this._listRefs.forEach(

list=>list&&list._onScrollEndDrag&&list._onScrollEndDrag(event),

);

};

_onMomentumScrollEnd=event=>{

if(this.props.onMomentumScrollEnd) {

this.props.onMomentumScrollEnd(event);

}

this._listRefs.forEach(

list=>

list&&list._onMomentumScrollEnd&&list._onMomentumScrollEnd(event),

);

};

_getItemLayout= (columnIndex,rowIndex)=>{

constcolumn=this.state.columns[columnIndex];

letoffset=0;

for(letii=0;ii

offset+=column.heights[ii];

}

return{length:column.heights[rowIndex],offset,index:rowIndex};

};

_renderScrollComponent= ()=><FakeScrollViewstyle={styles.column}/>;

_getItemCount=data=>data.length;

_getItem= (data,index)=>data[index];

_captureScrollRef=ref=>(this._scrollRef=ref);

render() {

const{

renderItem,

ListHeaderComponent,

ListEmptyComponent,

keyExtractor,

onEndReached,

...props

} =this.props;

letheaderElement;

if(ListHeaderComponent) {

headerElement=<ListHeaderComponent/>;

}

letemptyElement;

if(ListEmptyComponent) {

emptyElement=<ListEmptyComponent/>;

}

constcontent= (

<Viewstyle={styles.contentContainer}>

{this.state.columns.map(col=>

<VirtualizedList

{...props}

ref={ref=>(this._listRefs[col.index] =ref)}

key={`$col_${col.index}`}

data={col.data}

getItemCount={this._getItemCount}

getItem={this._getItem}

getItemLayout={(data,index)=>

this._getItemLayout(col.index,index)}

renderItem={({item,index})=>

renderItem({item,index,column:col.index})}

renderScrollComponent={this._renderScrollComponent}

keyExtractor={keyExtractor}

onEndReached={onEndReached}

onEndReachedThreshold={this.props.onEndReachedThreshold}

removeClippedSubviews={false}

/>,

)}

</View>

);

constscrollComponent=React.cloneElement(

this.props.renderScrollComponent(this.props),

{

ref:this._captureScrollRef,

removeClippedSubviews:false,

onContentSizeChange:this._onContentSizeChange,

onLayout:this._onLayout,

onScroll:this._onScroll,

onScrollBeginDrag:this._onScrollBeginDrag,

onScrollEndDrag:this._onScrollEndDrag,

onMomentumScrollEnd:this._onMomentumScrollEnd,

},

headerElement,

emptyElement&&this.props.data.length===0?emptyElement:content,

);

returnscrollComponent;

}

}

conststyles=StyleSheet.create({

contentContainer:{

flexDirection:'row',

},

column:{

flex:1,

},

});

实现代码

importReact, {Component}from'react';

import{

Platform,

StyleSheet,

Text,

View,

TouchableOpacity,

Dimensions,

Image,

TextInput,

AsyncStorage,

StatusBar,

BackHandler,

ToastAndroid,

ScrollView,

ImageBackground,

SafeAreaView

}from'react-native';

import{commonStyle}from'../../../utils/commonStyle';

import*asnavutilfrom'../../../utils/navutil';

importRefreshListViewfrom'../../component/RefreshListView';

importMasonryListfrom'../../component/MasonryList';

import*ascommonfrom'../../../utils/common';

import{width,height}from'../../../utils/Device';

import*asCommonUtilsfrom'../../../utils/CommonUtils';

importImagePickerfrom'react-native-syan-image-picker'

constitemWidth= (width-16) /2;//瀑布

import{observer,inject}from'mobx-react'

@inject("wbStore")

@observer

exportdefaultclassAppextendsComponent{

constructor(props){

super(props);

this.state={

refreshing:false,

data:[],

}

}

componentDidMount(){

this.onRefreshing()

}

onRefreshing= ()=>{//下拉刷新,未优化

this.setState({

refreshing:true,

})

letformData=newFormData();

formData.append('page',1);

formData.append('count',4);

formData.append('channel_category_id',this.props.items.channel_category_id) ;

formData.append('type',0);

formData.append('is_app',1);

formData.append('oauth_token',common.rundata.session.oauth_token);

formData.append('oauth_token_secret',common.rundata.session.oauth_token_secret);

fetch(common.urls.domain+"?"+common.urls.channelList, {

method:'POST',

headers:{

'Charset':'utf-8',

'Content-Type':'multipart/form-data',

},

body:formData,

}).then((response)=>{

if(response.ok) {

returnresponse.json();

}

}).then((json)=>{

console.log(json.data.feed_list)

console.log(8888)

this.setState({

refreshing:false,

data:json.data.feed_list,

})

}).catch((error)=>{

console.error(error);

});

}

_onEndReached= ()=>{//上拉加载,未优化

letformData=newFormData();

formData.append('page',this.state.data.length/10+1);

formData.append('count',4);

formData.append('channel_category_id',this.props.items.channel_category_id) ;

formData.append('type',0);

formData.append('is_app',1);

formData.append('oauth_token',common.rundata.session.oauth_token);

formData.append('oauth_token_secret',common.rundata.session.oauth_token_secret);

fetch(common.urls.domain+"?"+common.urls.channelList, {

method:'POST',

headers:{

'Charset':'utf-8',

'Content-Type':'multipart/form-data',

},

body:formData,

}).then((response)=>{

if(response.ok) {

returnresponse.json();

}

}).then((json)=>{

console.log(json.data.feed_list)

console.log(9999)

this.setState({

refreshing:false,

data:[...this.state.data,...json.data.feed_list]

})

}).catch((error)=>{

console.error(error);

});

}

_getHeightForItem= ({item})=>{//获取图片高

returnMath.max(itemWidth,itemWidth/item.image[0].attach_origin_width*item.image[0].attach_origin_height);

}

_renderItem= ({item})=>{

constitemHeight=this._getHeightForItem({item});

return(

<TouchableOpacity

activeOpacity={0.7}

onPress={()=>this._onPressContent(item)}

style={{marginRight:15,marginTop:15}}>

<Imagestyle={{width:itemWidth,height:itemHeight,borderRadius:4}}source={{uri:item.image[0].attach_origin}}/>

<Viewstyle={{backgroundColor:'#FFF',borderBottomColor:'#808080',borderBottomWidth:0.6,padding:8}}>

<TextnumberOfLines={5}style={{lineHeight:25,color:'#808080',}}>{item.content}</Text>

<Viewstyle={{flexDirection:'row',alignItems:'center'}}>

<Imagestyle={{width:15,height:15}}source={require('../../../images/ic_anonymous_like.png')}/>

<Textstyle={{marginLeft:8}}>{item.Fabulous}</Text>

</View>

</View>

<Viewstyle={{flexDirection:'row',backgroundColor:'#FFF',padding:8}}>

<TouchableOpacityactiveOpacity={0.7}onPress={()=>{

navutil.push_navigator("MyPage",{uid:item.user_id})

}}style={{width:35}}>

<Viewstyle={{width:35,height:35,borderRadius:15}}>

<Imagestyle={{width:35,height:35,borderRadius:15}}source={{uri:item.avatar_middle}}/>

</View>

</TouchableOpacity>

<Viewstyle={{flex:1}}>

<Viewstyle={{flexDirection:'row',alignItems:'center',marginLeft:8}}>

<Viewstyle={{flex:1}}>

<Textstyle={{fontSize:13,color:'black'}}>{item.user_name}</Text>

<Viewstyle={{flexDirection:'row',alignItems:'center',marginTop:2}}>

{

item.sex=='男'?<Imagestyle={{width:10,height:10}}source={require('../../../images/nan.png')}/>

:

<Imagestyle={{width:10,height:10}}source={require('../../../images/nv.png')}/>

}

<Textstyle={{fontSize:12,color:'#808080',marginLeft:5}}>{item.grade}</Text>

</View>

</View>

</View>

</View>

</View>

</TouchableOpacity>

)

}

_onPressContent= (item)=>{

navutil.push_navigator('TjDetail',{id:item.feed_id})

}

render(){

return(

<Viewstyle={{flex:1,backgroundColor:'#DDD',paddingLeft:15}}>

<SafeAreaView>

<MasonryList

data={this.state.data}

numColumns={2}//横向数目

renderItem={this._renderItem}

getHeightForItem={this._getHeightForItem}

refreshing={this.state.refreshing}

onRefresh={this.onRefreshing}

onEndReachedThreshold={0.5}

onEndReached={this._onEndReached}

keyExtractor={(myItem,index)=>("index"+index)}

/>

</SafeAreaView>

</View>

)

}

}

conststyles=StyleSheet.create({

item:{

}

});

相关文章

  • 瀑布流布局 的学习

    1- 实现瀑布流布局效果 瀑布流效果瀑布流代码木桶布局效果木桶布局代码瀑布流布局 2- 实现一个新闻瀑布流新闻...

  • 瀑布流、木桶布局

    瀑布流 瀑布流效果代码 木桶布局 木桶布局效果(加载有点慢)代码

  • 瀑布流照片墙布局

    title: 瀑布流照片墙布局 瀑布流概念 瀑布流布局是错落式的布局方式。它有一个特点,整个布局以列为单位,下一个...

  • CSS经典布局

    圣杯布局 瀑布流

  • 瀑布流

    瀑布流布局瀑布流jsonp新闻页

  • 瀑布流布局_木桶布局

    题目1: 实现一个瀑布流布局效果 瀑布流 题目2:实现木桶布局效果 木桶布局 题目3:**实现一个新闻瀑布流新闻网...

  • 瀑布流和懒加载结合

    实现一个瀑布流布局效果 瀑布流

  • 瀑布流

    什么是瀑布流: 欣赏 pinterest 样式分析 瀑布流,又被称作为瀑布流式布局,是一种比较流行的网站页面布局方...

  • 瀑布流

    1、什么是瀑布流 瀑布流,又称瀑布流式布局。是比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚...

  • 瀑布流的三种实现

    先来欣赏三个瀑布流的网站 pinterest 淘宝爱逛街 蘑菇街 什么是瀑布流? 瀑布流,又称瀑布流式布局。 这种...

网友评论

      本文标题:React-Native 瀑布流布局

      本文链接:https://www.haomeiwen.com/subject/cwijoftx.html