React-Native仿某电商商品详情页面
前言: 一周又過去了,一直在趕需求,除了自己利用空余時間學習一下外壓根就沒時間去研究新東西,唉~程序猿就是這樣,活到老學到老!! 廢話不多說了,公司產品覺得某電商的商品詳情頁面很nice,問我們能不能實現(寶寶心里苦),于是研究了一波,下面把我研究的東西分享一下,小伙伴有啥好的實現方式還謝謝分享一下哦~拜謝!
先看一下最終實現的效果:
ios/android
簡單來說就是分為兩個部分(上下),兩個都是(scrollview、flatlist)等滑動組件,第一個scrollview滑動到底部的時候,繼續上拉顯示第二個scrollview,第二個scrollview下拉到頂部的時候,繼續下拉回到第一個scrollview并且第一個scrollview回到頂部.
下面說一下我的大體思路:
第一種方式:
利用rn手勢,然后監聽scrollview的滑動事件,我們都知道rn中的事件傳遞都是從父控件一層一層往下傳遞,所以父控件能夠攔截scrollview也就是子控件的能力,當scrollview滑動到底部或者頂部的時候攔截scrollview的事件,把事件給父控件,然后通過控制父控件的垂直偏移量首先分頁功能,說了那么多理論的東西小伙伴估計都累了,我們后面結合代碼一起來說一下.
第二種方式:
封裝一個native的組件實現上拉和下拉的操作,rn只需要做一些簡單的監聽就可以.
兩種方式對比來看,能用rn實現最好,因為rn存在的目的也就是為了實現跨平臺目的,都用native實現了還用rn干嘛!! 話雖然這樣說,但是rn還是給開發人員提供了自定義的方法,用第二種方式實現有點就是性能和體驗上要優于rn實現,我接下來會結合兩種方式來實現.
先說一下第一種方式
第一步(把頁面分為上下兩部分):
render() {return (<View style={[styles.container]}>{/*第一部分*/}<Animated.View style={[styles.container1,{marginTop:this._aniBack.interpolate({inputRange:[0,1],outputRange:[0,-SCREEN_H],})}]}{...this._panResponder.panHandlers}><Animated.View ref={(ref) => this._container1 = ref}style={{width: SCREEN_W, height: SCREEN_H,marginTop:this._aniBack1.interpolate({inputRange:[0,1],outputRange:[0,-100],})}}><ScrollView ref={(ref)=>this._scroll1=ref}bounces={false}scrollEventThrottle={10}onScroll={this._onScroll.bind(this)}overScrollMode={'never'}>{this._getContent()}</ScrollView></Animated.View><View style={{width:SCREEN_W,height:100,backgroundColor:'white',alignItems:'center',justifyContent:'center'}}><Text>上拉查看詳情</Text></View></Animated.View>{/*第二部分*/}<View style={styles.container2}{...this._panResponder2.panHandlers}><Animated.View ref={(ref) => this._container2 = ref}style={{width:SCREEN_W,height:100,backgroundColor:'white',alignItems:'center',justifyContent:'center',marginTop:this._aniBack2.interpolate({inputRange:[0,1],outputRange:[-100,0],})}}><Text>下拉回到頂部</Text></Animated.View><View style={{width: SCREEN_W, height: SCREEN_H,}}><ScrollView ref={(ref)=>this._scroll2=ref}bounces={false}scrollEventThrottle={10}onScroll={this._onScroll2.bind(this)}overScrollMode={'never'}>{this._getContent()}</ScrollView></View></View></View>);}代碼我待會會貼出來,原理很簡單,我大體說一下我的實現思路,運行代碼你會發現,頁面是顯示了一個紅色頁面(也就是上部分).
讓我們把第一個頁面的marginTop調為-SCREEN_H(屏幕高度)的時候,我們會看到第二屏藍色頁面
所以我們只需要在第一個紅色頁面的scrollview滑動到底部的時候,然后攔截事件,手指抬起的時候,讓第一個頁面的marginTop從(0到-屏幕高度)的轉變,我們同時給個動畫實現.那么問題來了,我們該怎么監聽scrollview到達頂部或者底部呢?我們又該怎么攔截scrollview的事件呢?
監聽scrollview到達頂部或者底部:
到達頂部我們都知道,當scrollview的y軸偏移量=0的時候我們就認為scrollview到達頂部了,轉為代碼就是:
_onScroll2(event){this._reachEnd2=false;let y = event.nativeEvent.contentOffset.y;if(y<=0){//到達頂部了this._reachEnd2=true;}}到達底部也就是當(子控件的高度=y軸滑動的距離+父控件的高度)的時候,轉為代碼為:
_onScroll(event){this._reachEnd1=false;let y = event.nativeEvent.contentOffset.y;let height = event.nativeEvent.layoutMeasurement.height;let contentHeight = event.nativeEvent.contentSize.height;if (contentHeight > height && (y + height >= contentHeight)) {//到達頂部了this._reachEnd1=true;}}父控件攔截子控件的事件:
我們在onMoveShouldSetPanResponderCapture返回true,父控件就是攔截掉滑動事件,然后交給自己處理(onPanResponderMove),那么我們紅色頁面(也就是第一頁)的scrollview到達底部的時候,再往上拉的時候,我們攔截事件
我們第二個頁面(也就是藍色頁面)當scrollview滑動到頂部并且繼續往下拉的時候,攔截事件:
_handleMoveShouldSetPanResponderCapture2(event: Object, gestureState: Object,): boolean {console.log(gestureState.dy);console.log('_handleMoveShouldSetPanResponderCapture2');//當滑動到頂部并且繼續往下拉的時候return this._reachEnd2&&gestureState.dy>=0;}好啦~我們第一個頁面的父控件拿到滑動事件后,我們繼續往上拉,也就是把往上拉的距離賦給我們的“上拉查看詳情“組件了:
_handlePanResponderMove(event: Object, gestureState: Object): void {
//防止事件攔截不準,我們把scrollview的scrollEnabled:false設置為false
this._scroll1.setNativeProps({
scrollEnabled:false
})
let nowLeft =gestureState.dy*0.5;
//控制一個頁面的“上拉查看詳情“組件顯示
this._container1.setNativeProps({
marginTop:nowLeft
})
console.log(‘_handlePanResponderMove’,gestureState.dy);
代碼很簡單,我就不一一解釋了,小伙伴自己去運行看看哈,下面是第一種方式的所有實現代碼,直接運行就可以了:
/*** Sample React Native App* https://github.com/facebook/react-native* @flow*/import React, {Component} from 'react'; import {Platform,StyleSheet,Text,View,TouchableOpacity,Dimensions,ScrollView,PanResponder,Animated,StatusBar, } from 'react-native'; const SCREEN_W = Dimensions.get('window').width; const SCREEN_H = Dimensions.get('window').height-(Platform.OS==='android'?StatusBar.currentHeight:0); import SwipeRow from './SwipeRow';export default class App extends Component {// 構造constructor(props) {super(props);// 初始狀態// 初始狀態this._panResponder = PanResponder.create({onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture.bind(this),onStartShouldSetResponderCapture:this._handleMoveShouldSetPanResponderCapture.bind(this),onPanResponderMove: this._handlePanResponderMove.bind(this),onPanResponderRelease: this._handlePanResponderEnd.bind(this),onPanResponderGrant:this._handlePanGrant.bind(this),onPanResponderTerminate:()=>{console.log('onPanResponderTerminate');this._aniBack1.setValue(0);Animated.spring(this._aniBack1,{toValue:1}).start(()=>{this._handlePanResponderEnd();});},onShouldBlockNativeResponder: (event, gestureState) => false,//表示是否用 Native 平臺的事件處理,默認是禁用的,全部使用 JS 中的事件處理,注意此函數目前只能在 Android 平臺上使用});this._panResponder2 = PanResponder.create({onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture2.bind(this),onStartShouldSetResponderCapture:this._handleMoveShouldSetPanResponderCapture2.bind(this),onPanResponderMove: this._handlePanResponderMove2.bind(this),onPanResponderRelease: this._handlePanResponderEnd2.bind(this),onPanResponderGrant:this._handlePanGrant2.bind(this),onPanResponderTerminate:()=>{this._container2.setNativeProps({marginTop:0})this._aniBack2.setValue(0);Animated.spring(this._aniBack2,{toValue:1}).start(()=>{this._handlePanResponderEnd2();});console.log('onPanResponderTerminate2');},onShouldBlockNativeResponder: (event, gestureState) => false,//表示是否用 Native 平臺的事件處理,默認是禁用的,全部使用 JS 中的事件處理,注意此函數目前只能在 Android 平臺上使用});this._reachEnd1=false;this._reachEnd2=true;this._aniBack=new Animated.Value(0);this._aniBack1=new Animated.Value(0);this._aniBack2=new Animated.Value(0);}_handlePanGrant(event: Object, gestureState: Object,){this._scroll1.setNativeProps({scrollEnabled:false})}_handleMoveShouldSetPanResponderCapture(event: Object, gestureState: Object,): boolean {console.log('_handleMoveShouldSetPanResponderCapture');console.log(gestureState.dy);return this._reachEnd1&&gestureState.dy<0;}_handlePanResponderMove(event: Object, gestureState: Object): void {this._scroll1.setNativeProps({scrollEnabled:false})let nowLeft =gestureState.dy*0.5;this._container1.setNativeProps({marginTop:nowLeft})console.log('_handlePanResponderMove',gestureState.dy);}_handlePanResponderEnd(event: Object, gestureState: Object): void {this._aniBack.setValue(0);this._scroll1.setNativeProps({scrollEnabled: true})this._scroll1.scrollTo({y:0},true);Animated.timing(this._aniBack, {duration: 500,toValue: 1}).start();this._aniBack1.setValue(1);Animated.spring(this._aniBack1, {toValue: 0}).start();}_handleMoveShouldSetPanResponderCapture2(event: Object, gestureState: Object,): boolean {console.log(gestureState.dy);console.log('_handleMoveShouldSetPanResponderCapture2');return this._reachEnd2&&gestureState.dy>=0;}_handlePanResponderMove2(event: Object, gestureState: Object): void {console.log('_handlePanResponderMove2');let nowLeft =gestureState.dy*0.5;this._scroll2.setNativeProps({scrollEnabled:false})this._container2.setNativeProps({marginTop:-100+nowLeft})console.log('_handlePanResponderMove2',gestureState.dy);}_handlePanGrant2(event: Object, gestureState: Object,){this._scroll2.setNativeProps({scrollEnabled:false})}_handlePanResponderEnd2(event: Object, gestureState: Object): void {this._aniBack.setValue(1);this._scroll2.setNativeProps({scrollEnabled: true})Animated.timing(this._aniBack, {duration: 500,toValue: 0}).start();this._aniBack2.setValue(1);Animated.spring(this._aniBack2, {toValue: 0}).start();}render() {return (<Viewstyle={[styles.container]}>{/*第一部分*/}<Animated.Viewstyle={[styles.container1,{marginTop:this._aniBack.interpolate({inputRange:[0,1],outputRange:[0,-SCREEN_H],})}]}{...this._panResponder.panHandlers}><Animated.Viewref={(ref) => this._container1 = ref}style={{width: SCREEN_W, height: SCREEN_H,marginTop:this._aniBack1.interpolate({inputRange:[0,1],outputRange:[0,-100],})}}><ScrollViewref={(ref)=>this._scroll1=ref}bounces={false}scrollEventThrottle={10}onScroll={this._onScroll.bind(this)}overScrollMode={'never'}>{this._getContent()}</ScrollView></Animated.View><View style={{width:SCREEN_W,height:100,backgroundColor:'white',alignItems:'center',justifyContent:'center'}}><Text>上拉查看詳情</Text></View></Animated.View>{/*第二部分*/}<Viewstyle={styles.container2}{...this._panResponder2.panHandlers}><Animated.Viewref={(ref) => this._container2 = ref}style={{width:SCREEN_W,height:100,backgroundColor:'white',alignItems:'center',justifyContent:'center',marginTop:this._aniBack2.interpolate({inputRange:[0,1],outputRange:[-100,0],})}}><Text>下拉回到頂部</Text></Animated.View><Viewstyle={{width: SCREEN_W, height: SCREEN_H,}}><ScrollViewref={(ref)=>this._scroll2=ref}bounces={false}scrollEventThrottle={10}onScroll={this._onScroll2.bind(this)}overScrollMode={'never'}>{this._getContent()}</ScrollView></View></View></View>);}_onScroll(event){this._reachEnd1=false;let y = event.nativeEvent.contentOffset.y;let height = event.nativeEvent.layoutMeasurement.height;let contentHeight = event.nativeEvent.contentSize.height;if (contentHeight > height && (y + height >= contentHeight)) {this._reachEnd1=true;}}_onScroll2(event){this._reachEnd2=false;let y = event.nativeEvent.contentOffset.y;if(y<=0){this._reachEnd2=true;}}_getContent(){let contents=[];for (let i = 0; i < 50; i++) {contents.push(<Text style={{color:'#fff',marginTop:10}} key={'item'+i}>content-->{i}</Text>);}return contents;} }const styles = StyleSheet.create({container: {flex: 1,backgroundColor: '#F5FCFF',width: '100%',// transform:[{translateY:-SCREEN_H}]},container1:{width:SCREEN_W,height:SCREEN_H,backgroundColor:'red'},container2:{width:SCREEN_W,height:SCREEN_H,backgroundColor:'blue',overflow:'hidden'}, });簡單說一下第二種方式(我之后會把項目的全部代碼貼出來):
第一步(你需要封裝一個上拉和下拉刷新的控件給rn,)為了方便我就直接去引用了一個第三方的下拉框架:
Android智能下拉刷新框架-SmartRefreshLayout
感謝這哥們的無私奉獻~~我是懷著膜拜的心態看完了這哥們代碼,大牛一枚,鑒定完畢~!! 哈哈~ 小伙伴也可以去看看哈,我就不解釋這框架了,直接拿過來用了.
最后rn用的時候很簡單:
/*** @author YASIN* @version [React-Native Ocj V01, 2018/3/28]* @date 17/2/23* @description App2*/ import React, {Component} from 'react'; import {Platform,StyleSheet,Text,View,TouchableOpacity,Dimensions,ScrollView,PanResponder,Animated,StatusBar, } from 'react-native';const SCREEN_W = Dimensions.get('window').width; const SCREEN_H = Dimensions.get('window').height - (Platform.OS === 'android' ? StatusBar.currentHeight : 0);import SmallLayout from './SmartRefreshLayout';export default class App2 extends Component {// 構造constructor(props) {super(props);// 初始狀態this.state = {loadMore1: false,loadMore1: false,};this.aniBack = new Animated.Value(0);}render() {return (<Animated.Viewstyle={[styles.container, {marginTop: this.aniBack.interpolate({inputRange: [0, 1],outputRange: [0, -SCREEN_H],})}]}>{/*第一部分*/}<SmallLayoutstyle={{width: '100%', height: SCREEN_H, backgroundColor: 'red'}}loadMore={this.state.loadMore1}refreshEnable={false}onLoadMore={() => {this._startAniNext();}}><ScrollView>{this._getContent()}</ScrollView></SmallLayout>{/*第二部分*/}<SmallLayoutkey={'small2'}style={{width: '100%', height: SCREEN_H, backgroundColor: 'blue'}}refreshing={this.state.loadMore2}loadMoreEnable={false}onRefresh={()=>{this._startAniBackTop();}}><ScrollView>{this._getContent()}</ScrollView></SmallLayout></Animated.View>);}_startAniBackTop() {this.aniBack.setValue(1);Animated.timing(this.aniBack, {duration: 1000,toValue: 0,}).start(() => {});}_startAniNext() {this.aniBack.setValue(0);Animated.timing(this.aniBack, {duration: 1000,toValue: 1,}).start(() => {});}_getContent() {let contents = [];for (let i = 0; i < 50; i++) {contents.push(<Text style={{color: '#fff', marginTop: 10}} key={'item' + i}>content-->{i}</Text>);}return contents;} } const styles = StyleSheet.create({container: {flex: 1,width: '100%',} });是不是很簡單?對的,簡簡單單100行代碼就搞定了,然后效果咋樣?
事件銜接很平滑,體驗比rn好!當然,我只實現了android部分,小伙伴懂ios的也可以把ios的實現一下~~
我簡單說一下與native的橋接(原諒我只能以android為例子)
第一步(新建一個SmartRefreshLayoutManager繼承ViewGroupManager,然后提供rn調用的屬性跟方法還有回調):
/*** Copyright (c) 2015-present, Facebook, Inc.* All rights reserved.* <p>* This source code is licensed under the BSD-style license found in the* LICENSE file in the root directory of this source tree. An additional grant* of patent rights can be found in the PATENTS file in the same directory.*/package com.swipedemo;import android.support.annotation.NonNull;import com.facebook.react.bridge.ReactContext; import com.facebook.react.common.MapBuilder; import com.facebook.react.module.annotations.ReactModule; import com.facebook.react.uimanager.ThemedReactContext; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewGroupManager; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.events.Event; import com.facebook.react.uimanager.events.EventDispatcher; import com.scwang.smartrefresh.layout.SmartRefreshLayout; import com.scwang.smartrefresh.layout.api.RefreshLayout; import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener; import com.scwang.smartrefresh.layout.listener.OnRefreshListener;import java.util.Map;/*** ViewManager for {@link com.facebook.react.views.swiperefresh.ReactSwipeRefreshLayout} which allows the user to "pull to refresh" a* child view. Emits an {@code onRefresh} event when this happens.*/ @ReactModule(name = SmartRefreshLayoutManager.REACT_CLASS) public class SmartRefreshLayoutManager extends ViewGroupManager<SmartRefreshLayout> {protected static final String REACT_CLASS = "AndroidSmartRefreshLayout";@ReactProp(name = "refreshing")public void setRefreshing(SmartRefreshLayout view, boolean refreshing) {if (refreshing) {view.autoRefresh();} else {view.finishRefresh();}}@ReactProp(name = "loadMore")public void setLoadMore(SmartRefreshLayout view, boolean refreshing) {if (refreshing) {view.autoLoadMore();} else {view.finishLoadMore();}}@ReactProp(name = "loadMoreEnable")public void setLoadMoreEnable(SmartRefreshLayout view, boolean enable) {view.setEnableLoadMore(enable);}@ReactProp(name = "refreshEnable")public void setRefreshEnable(SmartRefreshLayout view, boolean enable) {view.setEnableRefresh(enable);}@Overrideprotected SmartRefreshLayout createViewInstance(ThemedReactContext reactContext) {final SmartRefreshLayout smartRefreshLayout = new SmartRefreshLayout(reactContext);smartRefreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {@Overridepublic void onLoadMore(@NonNull final RefreshLayout refreshLayout) {dispatchEvent(smartRefreshLayout,new LoadMoreEvent(smartRefreshLayout.getId()));}});smartRefreshLayout.setOnRefreshListener(new OnRefreshListener() {@Overridepublic void onRefresh(@NonNull RefreshLayout refreshLayout) {dispatchEvent(smartRefreshLayout,new RefreshEvent(smartRefreshLayout.getId()));}});//觸發自動刷新smartRefreshLayout.setRefreshHeader(new CustHeader(reactContext)); // smartRefreshLayout.setEnableOverScrollBounce(true);smartRefreshLayout.setEnableOverScrollDrag(false);smartRefreshLayout.setEnableAutoLoadMore(false);smartRefreshLayout.setRefreshFooter(new CustFooter(reactContext));return smartRefreshLayout;}@Overridepublic String getName() {return REACT_CLASS;}@Overridepublic Map<String, Object> getExportedCustomDirectEventTypeConstants() {return MapBuilder.<String, Object>builder().put("topRefresh", MapBuilder.of("registrationName", "onRefresh")).put("bottomRefresh", MapBuilder.of("registrationName", "onLoadMore")).build();}protected static void dispatchEvent(SmartRefreshLayout refreshLayout, Event event) {ReactContext reactContext = (ReactContext) refreshLayout.getContext();EventDispatcher eventDispatcher =reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher();eventDispatcher.dispatchEvent(event);} }我只能說還是需要一點android基礎才能看懂這代碼的,哈哈~ 原諒我又裝了一個b, 會android的我就不用解釋這是啥了~~不懂的可以查看原生給rn封裝的自帶的下拉刷新組件:
哈哈~ 我也是仿照自帶的AndroidSwipeRefreshLayout組件封裝的,所以android的童鞋沒事研究一下源碼還是很有必要的.
好啦~ 逼裝完了,我們繼續哈,
然后新建一個叫SmartRefreshLayoutPackage類繼承ReactPackage把SmartRefreshLayoutManager添加進去:
package com.swipedemo;import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager;import java.util.Arrays; import java.util.Collections; import java.util.List;/*** Created by yinqingyang on 2018/3/28.*/public class SmartRefreshLayoutPackage implements ReactPackage {@Overridepublic List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {return Collections.emptyList();}@Overridepublic List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {return Arrays.<ViewManager>asList(new SmartRefreshLayoutManager());} }最后將我們的SmartRefreshLayoutPackage添加進rn,我們來到我們的MainApplication:
public class MainApplication extends Application implements ReactApplication {static {SmartRefreshLayout.setDefaultRefreshHeaderCreator(new DefaultRefreshHeaderCreator() {@NonNull@Overridepublic RefreshHeader createRefreshHeader(@NonNull Context context, @NonNull RefreshLayout layout) {layout.setPrimaryColorsId(R.color.colorPrimaryDark, android.R.color.white);//全局設置主題顏色return new ClassicsHeader(context).setTimeFormat(new DynamicTimeFormat("更新于 %s")).setArrowResource(R.mipmap.ic_launcher);}});SmartRefreshLayout.setDefaultRefreshFooterCreator(new DefaultRefreshFooterCreator() {@NonNull@Overridepublic RefreshFooter createRefreshFooter(@NonNull Context context, @NonNull RefreshLayout layout) {return new ClassicsFooter(context);}});}private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {@Overridepublic boolean getUseDeveloperSupport() {return BuildConfig.DEBUG;}@Overrideprotected List<ReactPackage> getPackages() {return Arrays.<ReactPackage>asList(new MainReactPackage(),//添加我們的自定義packagenew SmartRefreshLayoutPackage());}@Overrideprotected String getJSMainModuleName() {return "index";}};好啦!我們已經搞定原生部分了,那么我們的rn怎么才能拿到我們原生提供的view呢?
回到rn
第一步:我們創建一個SmartRefreshLayout.android.js文件,提供跟native 一樣的屬性:
export default class SmartRefreshLayout extends Component {static propTypes = {...ViewPropTypes,refreshing: PropTypes.bool,loadMore: PropTypes.bool,onLoadMore: PropTypes.func,onRefresh: PropTypes.func,loadMoreEnable: PropTypes.bool,refreshEnable: PropTypes.bool,};static defaultProps = {refreshing: false,loadMore: false,refreshEnable: true,loadMoreEnable: true,};記住!! native有的屬性,我們定義的view一定要有.
然后通過rn自帶的requireNativeComponent方法把native的view橋接過來:
const AndroidSmartRefreshLayout = requireNativeComponent('AndroidSmartRefreshLayout', SmartRefreshLayout);最后渲染出來:
render() {const {children, style, loadMore, refreshing, refreshEnable, loadMoreEnable} = this.props;return (<AndroidSmartRefreshLayoutref={ref => {this._nativeRef = ref;}}style={[style]}onLoadMore={this._onLoadMore}onRefresh={this._onRefresh}loadMore={loadMore}refreshEnable={refreshEnable}loadMoreEnable={loadMoreEnable}refreshing={refreshing}>{React.Children.toArray(children)}</AndroidSmartRefreshLayout>);}全部代碼:
/*** @author YASIN* @version [React-Native Ocj V01, 2018/3/28]* @date 17/2/23* @description SmartRefreshLayout.android*/ import React, {Component} from 'react'; import {Platform,StyleSheet,Text,View, } from 'react-native';const PropTypes = require('prop-types'); const ViewPropTypes = require('ViewPropTypes'); const requireNativeComponent = require('requireNativeComponent'); export default class SmartRefreshLayout extends Component {static propTypes = {...ViewPropTypes,refreshing: PropTypes.bool,loadMore: PropTypes.bool,onLoadMore: PropTypes.func,onRefresh: PropTypes.func,loadMoreEnable: PropTypes.bool,refreshEnable: PropTypes.bool,};static defaultProps = {refreshing: false,loadMore: false,refreshEnable: true,loadMoreEnable: true,};// 構造constructor(props) {super(props);this._lastNativeLoadMore = false;this._lastNativeRefresh = false;}render() {const {children, style, loadMore, refreshing, refreshEnable, loadMoreEnable} = this.props;return (<AndroidSmartRefreshLayoutref={ref => {this._nativeRef = ref;}}style={[style]}onLoadMore={this._onLoadMore}onRefresh={this._onRefresh}loadMore={loadMore}refreshEnable={refreshEnable}loadMoreEnable={loadMoreEnable}refreshing={refreshing}>{React.Children.toArray(children)}</AndroidSmartRefreshLayout>);}componentDidMount() {this._lastNativeLoadMore = this.props.refreshing;};componentDidUpdate(prevProps: { loadMore: boolean }) {// RefreshControl is a controlled component so if the native refreshing// value doesn't match the current js refreshing prop update it to// the js value.if (this.props.loadMore !== prevProps.loadMore) {this._lastNativeLoadMore = this.props.loadMore;} else if (this.props.loadMore !== this._lastNativeLoadMore) {this._nativeRef.setNativeProps({loadMore: this.props.loadMore});this._lastNativeLoadMore = this.props.loadMore;}if (this.props.refreshing !== prevProps.refreshing) {this._lastNativeRefresh = this.props.refreshing;} else if (this.props.refreshing !== this._lastNativeRefresh) {this._nativeRef.setNativeProps({refreshing: this.props.refreshing});this._lastNativeRefresh = this.props.refreshing;}}_onLoadMore = (event) => {this._lastNativeLoadMore = true;this.props.onLoadMore && this.props.onLoadMore();// The native component will start refreshing so force an update to// make sure it stays in sync with the js component.this.forceUpdate();}_onRefresh=(event)=>{this._lastNativeRefresh = true;this.props.onRefresh && this.props.onRefresh();// The native component will start refreshing so force an update to// make sure it stays in sync with the js component.this.forceUpdate();} } const AndroidSmartRefreshLayout = requireNativeComponent('AndroidSmartRefreshLayout', SmartRefreshLayout);好啦! 我們按照我們第一種實現方式的部分代碼實現一下:
/*** @author YASIN* @version [React-Native Ocj V01, 2018/3/28]* @date 17/2/23* @description App2*/ import React, {Component} from 'react'; import {Platform,StyleSheet,Text,View,TouchableOpacity,Dimensions,ScrollView,PanResponder,Animated,StatusBar, } from 'react-native';const SCREEN_W = Dimensions.get('window').width; const SCREEN_H = Dimensions.get('window').height - (Platform.OS === 'android' ? StatusBar.currentHeight : 0);import SmallLayout from './SmartRefreshLayout';export default class App2 extends Component {// 構造constructor(props) {super(props);// 初始狀態this.state = {loadMore1: false,loadMore1: false,};this.aniBack = new Animated.Value(0);}render() {return (<Animated.Viewstyle={[styles.container, {marginTop: this.aniBack.interpolate({inputRange: [0, 1],outputRange: [0, -SCREEN_H],})}]}>{/*第一部分*/}<SmallLayoutstyle={{width: '100%', height: SCREEN_H, backgroundColor: 'red'}}loadMore={this.state.loadMore1}refreshEnable={false}onLoadMore={() => {this._startAniNext();}}><ScrollView>{this._getContent()}</ScrollView></SmallLayout>{/*第二部分*/}<SmallLayoutkey={'small2'}style={{width: '100%', height: SCREEN_H, backgroundColor: 'blue'}}refreshing={this.state.loadMore2}loadMoreEnable={false}onRefresh={()=>{this._startAniBackTop();}}><ScrollView>{this._getContent()}</ScrollView></SmallLayout></Animated.View>);}_startAniBackTop() {this.aniBack.setValue(1);Animated.timing(this.aniBack, {duration: 1000,toValue: 0,}).start(() => {});}_startAniNext() {this.aniBack.setValue(0);Animated.timing(this.aniBack, {duration: 1000,toValue: 1,}).start(() => {});}_getContent() {let contents = [];for (let i = 0; i < 50; i++) {contents.push(<Text style={{color: '#fff', marginTop: 10}} key={'item' + i}>content-->{i}</Text>);}return contents;} } const styles = StyleSheet.create({container: {flex: 1,width: '100%',} });最后運行代碼就可以看到我們之前截圖的效果了.
歡迎交流,歡迎入群~~
項目github地址:
https://github.com/913453448/SwipeDemo
總結
以上是生活随笔為你收集整理的React-Native仿某电商商品详情页面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 实战 SQL:亚马逊、京东等电商平台的销
- 下一篇: Readyfor4GB帮你的32位Win