React-Native带你一步一步实现侧滑删除
前言:好久沒有寫博客了,回想起剛開始寫博客的時候對自己的要求,“每周至少一篇!!!“(還有當初說減肥跟寫博客同步進行,結果越減越肥。),嗯嗯,說多了都是淚,最近在一直在學習h5,然后看到現在rn項目中有小伙伴在用一個第三方的側滑刪除控件,于是想去看看那些大神是咋實現的,最后發現,也就這樣哈~沒想象中的那么難,寫這篇博客的目的也就是當作一個學習筆記,大牛勿噴!!2017對我來說是不平凡的一年,期間換了幾次工作,但最后還是找到了自己的歸宿,所以對2017還是比較滿意的,感恩!!2018迎來了又一個本命年,回頭想想自己,其實也不小了,但心里卻總是對自己說:“我還是一個小鮮肉!“,唉唉!感嘆時光的流逝,身邊的親人一個一個離去,有些時候一個人發呆的時候總問自己“你到底想做什么?你能做什么?“我卻被自己問的啞口無言,騷年!現實點吧~ 也請對自己和身邊的人好一點,堅持自己的初衷,永遠不要做思想的巨人行動的矮子,嗯嗯!說了那么多廢話,想必大家也覺得我無聊,管你們無不無聊!我開心就行,哈哈哈哈~~~
最后實現的效果呢也很簡單,大概是這樣的:
先說一下我們的大體思路,很簡單!底部絕對定位放一個默認的(含有“刪除“按鈕)的view,然后上面蓋住一個默認的需要渲染的view,給整個item一個滑動監聽,然后慢慢的漏出底部view,嗯嗯!! 原理真的很簡單,下面我們一步一步的擼我們的代碼哈~
首先新建一個很干凈的項目叫SwipeDemo然后跑起來:
照著我們的思路簡單實現一下,代碼很簡單,我直接貼出來了:
/*** Sample React Native App* https://github.com/facebook/react-native* @flow*/import React, {Component} from 'react'; import {Platform,StyleSheet,Text,View,TouchableOpacity } from 'react-native';export default class App extends Component {render() {return (<View style={styles.container}><View style={styles.swipeContainer}>{/*絕對在底部的view*/}<View style={styles.swipeActions}><TouchableOpacitystyle={styles.delTextContainer}onPress={()=>{alert('ss');}}><Textstyle={styles.deleteTextStyle}>刪除</Text></TouchableOpacity></View>{/*內容content*/}<View style={styles.content}><Text>我是item的內容</Text></View></View></View>);} }const styles = StyleSheet.create({container: {flex: 1,alignItems: 'center',backgroundColor: '#F5FCFF',},swipeContainer: {width: '100%',marginTop: 100,height:100,},swipeActions:{backgroundColor: 'grey',width: '100%',overflow:'hidden',...StyleSheet.absoluteFillObject,flexDirection:'row',justifyContent:'flex-end'},delTextContainer:{width:100,backgroundColor:'red',alignItems:'center',justifyContent:'center'},deleteTextStyle:{color:'#fff',},content:{width: '100%',flex:1,backgroundColor:'yellow',justifyContent:'center',alignItems:'center',} });運行代碼:
然后我們給內容content一個向左的偏移量:
render() {return (<View style={styles.container}><View style={styles.swipeContainer}>{/*絕對在底部的view*/}<View style={styles.swipeActions}><TouchableOpacity style={styles.delTextContainer}onPress={()=>{alert('ss');}}><Text style={styles.deleteTextStyle}>刪除</Text></TouchableOpacity></View>{/*內容content*/}<View style={[styles.content,{transform:[{translateX:-10}]}]}><Text>我是item的內容</Text></View></View></View>);我們向左偏移了10:
可以看到我們絕對在底部的view漏出了一點點~
我們再向左偏移左偏移了100:
可以看到我們絕對在底部的view完全漏出來了~
所以看到這里小伙伴是不是有點明白了呢??我們只需要給一個手勢在move的時候動態的設置左偏移量,然后手指松開的時候做一些邏輯判斷,最后給一個動畫,就可以簡單的實現我們的側滑刪除功能了。
在寫組件之前建議大家了解一下rn的手勢,也就是PanResponder組件,大家可以參考下官網及這篇文章:
React Native 觸摸事件處理詳解
PanResponder
好啦~ 我們先創建一個view叫SwipeRow.js:
/*** @author YASIN* @version [React-Native Ocj V01, 2018/3/13]* @date 17/2/23* @description SwipeRow*/ import React, {Component, } from 'react'; import PropTypes from 'prop-types'; import {Animated,PanResponder,Platform,StyleSheet,TouchableOpacity,ViewPropTypes,View,Text } from 'react-native';export default class SwipeRow extends Component {render() {return (<View style={[styles.swipeContainer, this.props.style]}><View style={styles.swipeActions}>{this.props.children[0]}</View>{this.props.children[1]}</View>);} } const styles = StyleSheet.create({swipeContainer: {width: '100%',},swipeActions: {backgroundColor: 'grey',width: '100%',overflow: 'hidden',...StyleSheet.absoluteFillObject,flexDirection: 'row',justifyContent: 'flex-end'}, });然后App.js代碼改為:
/*** Sample React Native App* https://github.com/facebook/react-native* @flow*/import React, {Component} from 'react'; import {Platform,StyleSheet,Text,View,TouchableOpacity } from 'react-native'; import SwipeRow from './SwipeRow';export default class App extends Component {render() {return (<View style={styles.container}><SwipeRow style={{marginTop: 100}}>{/*絕對在底部的view*/}<TouchableOpacitystyle={styles.delTextContainer}onPress={() => {alert('ss');}}><Textstyle={styles.deleteTextStyle}>刪除</Text></TouchableOpacity>{/*內容content*/}<View style={[styles.content, {transform: [{translateX: -100}]}]}><Text>我是item的內容</Text></View></SwipeRow></View>);} }const styles = StyleSheet.create({container: {flex: 1,alignItems: 'center',backgroundColor: '#F5FCFF',width: '100%',},delTextContainer: {width: 100,backgroundColor: 'red',alignItems: 'center',justifyContent: 'center'},deleteTextStyle: {color: '#fff',},content: {width: '100%',height: 100,backgroundColor: 'yellow',justifyContent: 'center',alignItems: 'center',} });運行代碼:
可以發現我們得到的是一樣的效果,我們只是簡單的封裝到了一個叫SwipeRow的組件中了~
接著我們封裝一個手勢的處理類:
/*** @author YASIN* @version [React-Native Ocj V01, 2018/3/13]* @date 17/2/23* @description SwipeRow*/ import React, {Component, } from 'react'; import PropTypes from 'prop-types'; import {Animated,PanResponder,Platform,StyleSheet,TouchableOpacity,ViewPropTypes,View,Text } from 'react-native';export default class SwipeRow extends Component {// 構造constructor(props) {super(props);// 初始狀態this._panResponder = PanResponder.create({onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture,onPanResponderGrant: this._handlePanResponderGrant,onPanResponderMove: this._handlePanResponderMove,onPanResponderRelease: this._handlePanResponderEnd,onPanResponderTerminate: this._handlePanResponderEnd,onShouldBlockNativeResponder: (event, gestureState) => false,//表示是否用 Native 平臺的事件處理,默認是禁用的,全部使用 JS 中的事件處理,注意此函數目前只能在 Android 平臺上使用});}render() {return (<View style={[styles.swipeContainer, this.props.style]}><View style={styles.swipeActions}>{this.props.children[0]}</View>{this.renderRowContent()}</View>);}renderRowContent() {return (<Animated.View{...this._panResponder.panHandlers}>{this.props.children[1]}</Animated.View>);}/*** 是否需要成為move事件響應者,返回true直接走onPanResponderMove* @param event* @param gestureState* @returns {boolean}* @private*/_handleMoveShouldSetPanResponderCapture(event: Object, gestureState: Object,): boolean {//當垂直滑動的距離<10 水平滑動的距離>10的時候才讓捕獲事件console.log('_handleMoveShouldSetPanResponderCapture');return gestureState.dy < 10 && Math.abs(gestureState.dx) > 10;}/*** 表示申請成功,組件成為了事件處理響應者* @param event* @param gestureState* @private*/_handlePanResponderGrant(event: Object, gestureState: Object): void {console.log('_handlePanResponderGrant');}/*** 處理滑動事件* @param event* @param gestureState* @private*/_handlePanResponderMove(event: Object, gestureState: Object): void {console.log('_handlePanResponderMove');}/*** 結束事件的時候回調* @param event* @param gestureState* @private*/_handlePanResponderEnd(event: Object, gestureState: Object): void {console.log('_handlePanResponderEnd');} } const styles = StyleSheet.create({swipeContainer: {width: '100%',},swipeActions: {backgroundColor: 'grey',width: '100%',overflow: 'hidden',...StyleSheet.absoluteFillObject,flexDirection: 'row',justifyContent: 'flex-end'}, });然后運行代碼(手指從右向左拖動)看log:
好啦,我們在_handlePanResponderMove方法中處理下滑動事件:
/*** 處理滑動事件* @param event* @param gestureState* @private*/_handlePanResponderMove(event: Object, gestureState: Object): void {if (this._previousLeft === null) {this._previousLeft = this.state.currentLeft._value}let nowLeft = this._previousLeft + gestureState.dx / 0.5;//右滑最大距離為0(邊界值)nowLeft = Math.min(nowLeft, 0);this.state.currentLeft.setValue(nowLeft,);}然后去除App.js中的{transform: [{translateX: -100}]:
{/*內容content*/}<View style={[styles.content, {transform: [{translateX: -100}]}]}><Text>我是item的內容</Text></View>SwipeRow組件中我們添加一個:
//上一次滑動最后的left偏移量this._previousLeft = 0;//left偏移動畫this.state = {currentLeft: new Animated.Value(this._previousLeft),};然后在move跟end的時候操作:
/*** 處理滑動事件* @param event* @param gestureState* @private*/_handlePanResponderMove(event: Object, gestureState: Object): void {if (this._previousLeft === null) {this._previousLeft = this.state.currentLeft._value}let nowLeft = this._previousLeft + gestureState.dx / 0.5;//右滑最大距離為0(邊界值)nowLeft = Math.min(nowLeft, 0);this.state.currentLeft.setValue(nowLeft,);}/*** 結束事件的時候回調* @param event* @param gestureState* @private*/_handlePanResponderEnd(event: Object, gestureState: Object): void {console.log('_handlePanResponderEnd');this._previousLeft = null;}最后付給動畫組件:
render() {return (<View style={[styles.swipeContainer, this.props.style]}><View style={styles.swipeActions}>{this.props.children[0]}</View>{this.renderRowContent()}</View>);}renderRowContent() {return (<Animated.View{...this._panResponder.panHandlers}style={{transform: [{translateX: this.state.currentLeft}]}}>{this.props.children[1]}</Animated.View>);}全部代碼:
/*** @author YASIN* @version [React-Native Ocj V01, 2018/3/13]* @date 17/2/23* @description SwipeRow*/ import React, {Component, } from 'react'; import PropTypes from 'prop-types'; import {Animated,PanResponder,Platform,StyleSheet,TouchableOpacity,ViewPropTypes,View,Text } from 'react-native';export default class SwipeRow extends Component {// 構造constructor(props) {super(props);// 初始狀態this._panResponder = PanResponder.create({onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture.bind(this),onPanResponderGrant: this._handlePanResponderGrant.bind(this),onPanResponderMove: this._handlePanResponderMove.bind(this),onPanResponderRelease: this._handlePanResponderEnd.bind(this),onPanResponderTerminate: this._handlePanResponderEnd.bind(this),onShouldBlockNativeResponder: (event, gestureState) => false,//表示是否用 Native 平臺的事件處理,默認是禁用的,全部使用 JS 中的事件處理,注意此函數目前只能在 Android 平臺上使用});//上一次滑動最后的left偏移量this._previousLeft = 0;//left偏移動畫this.state = {currentLeft: new Animated.Value(this._previousLeft),};}render() {return (<View style={[styles.swipeContainer, this.props.style]}><View style={styles.swipeActions}>{this.props.children[0]}</View>{this.renderRowContent()}</View>);}renderRowContent() {return (<Animated.View{...this._panResponder.panHandlers}style={{transform: [{translateX: this.state.currentLeft}]}}>{this.props.children[1]}</Animated.View>);}/*** 是否需要成為move事件響應者,返回true直接走onPanResponderMove* @param event* @param gestureState* @returns {boolean}* @private*/_handleMoveShouldSetPanResponderCapture(event: Object, gestureState: Object,): boolean {//當垂直滑動的距離<10 水平滑動的距離>10的時候才讓捕獲事件console.log('_handleMoveShouldSetPanResponderCapture');return Math.abs(gestureState.dy) < 10 && Math.abs(gestureState.dx) > 10;}/*** 表示申請成功,組件成為了事件處理響應者* @param event* @param gestureState* @private*/_handlePanResponderGrant(event: Object, gestureState: Object): void {console.log('_handlePanResponderGrant');}/*** 處理滑動事件* @param event* @param gestureState* @private*/_handlePanResponderMove(event: Object, gestureState: Object): void {if (this._previousLeft === null) {this._previousLeft = this.state.currentLeft._value}let nowLeft = this._previousLeft + gestureState.dx / 0.5;//右滑最大距離為0(邊界值)nowLeft = Math.min(nowLeft, 0);this.state.currentLeft.setValue(nowLeft,);}/*** 結束事件的時候回調* @param event* @param gestureState* @private*/_handlePanResponderEnd(event: Object, gestureState: Object): void {console.log('_handlePanResponderEnd');this._previousLeft = null;} } const styles = StyleSheet.create({swipeContainer: {width: '100%',},swipeActions: {backgroundColor: 'grey',width: '100%',overflow: 'hidden',...StyleSheet.absoluteFillObject,flexDirection: 'row',justifyContent: 'flex-end'}, });最后運行效果:
我們只差最后一步了,就是在手指提起的時候讓滑動部分反彈回去就可以了~ 先留給小伙伴自己實現吧,好吧~~感覺很簡單的東西,寫成文字還不是件容易的事情,吃飯去啦!!下節見咯~
總結
以上是生活随笔為你收集整理的React-Native带你一步一步实现侧滑删除的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑操作最忌讳十八个小动作,你有几个?
- 下一篇: MVC、MVVM、RaectiveCoc