React-Native带你一步一步实现侧滑删除
前言:好久沒有寫博客了,回想起剛開始寫博客的時(shí)候?qū)ψ约旱囊?#xff0c;“每周至少一篇!!!“(還有當(dāng)初說減肥跟寫博客同步進(jìn)行,結(jié)果越減越肥。),嗯嗯,說多了都是淚,最近在一直在學(xué)習(xí)h5,然后看到現(xiàn)在rn項(xiàng)目中有小伙伴在用一個(gè)第三方的側(cè)滑刪除控件,于是想去看看那些大神是咋實(shí)現(xiàn)的,最后發(fā)現(xiàn),也就這樣哈~沒想象中的那么難,寫這篇博客的目的也就是當(dāng)作一個(gè)學(xué)習(xí)筆記,大牛勿噴!!2017對(duì)我來說是不平凡的一年,期間換了幾次工作,但最后還是找到了自己的歸宿,所以對(duì)2017還是比較滿意的,感恩!!2018迎來了又一個(gè)本命年,回頭想想自己,其實(shí)也不小了,但心里卻總是對(duì)自己說:“我還是一個(gè)小鮮肉!“,唉唉!感嘆時(shí)光的流逝,身邊的親人一個(gè)一個(gè)離去,有些時(shí)候一個(gè)人發(fā)呆的時(shí)候總問自己“你到底想做什么?你能做什么?“我卻被自己問的啞口無言,騷年!現(xiàn)實(shí)點(diǎn)吧~ 也請(qǐng)對(duì)自己和身邊的人好一點(diǎn),堅(jiān)持自己的初衷,永遠(yuǎn)不要做思想的巨人行動(dòng)的矮子,嗯嗯!說了那么多廢話,想必大家也覺得我無聊,管你們無不無聊!我開心就行,哈哈哈哈~~~
最后實(shí)現(xiàn)的效果呢也很簡單,大概是這樣的:
先說一下我們的大體思路,很簡單!底部絕對(duì)定位放一個(gè)默認(rèn)的(含有“刪除“按鈕)的view,然后上面蓋住一個(gè)默認(rèn)的需要渲染的view,給整個(gè)item一個(gè)滑動(dòng)監(jiān)聽,然后慢慢的漏出底部view,嗯嗯!! 原理真的很簡單,下面我們一步一步的擼我們的代碼哈~
首先新建一個(gè)很干凈的項(xiàng)目叫SwipeDemo然后跑起來:
照著我們的思路簡單實(shí)現(xiàn)一下,代碼很簡單,我直接貼出來了:
/*** 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}>{/*絕對(duì)在底部的view*/}<View style={styles.swipeActions}><TouchableOpacitystyle={styles.delTextContainer}onPress={()=>{alert('ss');}}><Textstyle={styles.deleteTextStyle}>刪除</Text></TouchableOpacity></View>{/*內(nèi)容content*/}<View style={styles.content}><Text>我是item的內(nèi)容</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',} });運(yùn)行代碼:
然后我們給內(nèi)容content一個(gè)向左的偏移量:
render() {return (<View style={styles.container}><View style={styles.swipeContainer}>{/*絕對(duì)在底部的view*/}<View style={styles.swipeActions}><TouchableOpacity style={styles.delTextContainer}onPress={()=>{alert('ss');}}><Text style={styles.deleteTextStyle}>刪除</Text></TouchableOpacity></View>{/*內(nèi)容content*/}<View style={[styles.content,{transform:[{translateX:-10}]}]}><Text>我是item的內(nèi)容</Text></View></View></View>);我們向左偏移了10:
可以看到我們絕對(duì)在底部的view漏出了一點(diǎn)點(diǎn)~
我們?cè)傧蜃笃谱笃屏?00:
可以看到我們絕對(duì)在底部的view完全漏出來了~
所以看到這里小伙伴是不是有點(diǎn)明白了呢??我們只需要給一個(gè)手勢在move的時(shí)候動(dòng)態(tài)的設(shè)置左偏移量,然后手指松開的時(shí)候做一些邏輯判斷,最后給一個(gè)動(dòng)畫,就可以簡單的實(shí)現(xiàn)我們的側(cè)滑刪除功能了。
在寫組件之前建議大家了解一下rn的手勢,也就是PanResponder組件,大家可以參考下官網(wǎng)及這篇文章:
React Native 觸摸事件處理詳解
PanResponder
好啦~ 我們先創(chuàng)建一個(gè)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}}>{/*絕對(duì)在底部的view*/}<TouchableOpacitystyle={styles.delTextContainer}onPress={() => {alert('ss');}}><Textstyle={styles.deleteTextStyle}>刪除</Text></TouchableOpacity>{/*內(nèi)容content*/}<View style={[styles.content, {transform: [{translateX: -100}]}]}><Text>我是item的內(nèi)容</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',} });運(yùn)行代碼:
可以發(fā)現(xiàn)我們得到的是一樣的效果,我們只是簡單的封裝到了一個(gè)叫SwipeRow的組件中了~
接著我們封裝一個(gè)手勢的處理類:
/*** @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 {// 構(gòu)造constructor(props) {super(props);// 初始狀態(tài)this._panResponder = PanResponder.create({onMoveShouldSetPanResponderCapture: this._handleMoveShouldSetPanResponderCapture,onPanResponderGrant: this._handlePanResponderGrant,onPanResponderMove: this._handlePanResponderMove,onPanResponderRelease: this._handlePanResponderEnd,onPanResponderTerminate: this._handlePanResponderEnd,onShouldBlockNativeResponder: (event, gestureState) => false,//表示是否用 Native 平臺(tái)的事件處理,默認(rèn)是禁用的,全部使用 JS 中的事件處理,注意此函數(shù)目前只能在 Android 平臺(tái)上使用});}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事件響應(yīng)者,返回true直接走onPanResponderMove* @param event* @param gestureState* @returns {boolean}* @private*/_handleMoveShouldSetPanResponderCapture(event: Object, gestureState: Object,): boolean {//當(dāng)垂直滑動(dòng)的距離<10 水平滑動(dòng)的距離>10的時(shí)候才讓捕獲事件console.log('_handleMoveShouldSetPanResponderCapture');return gestureState.dy < 10 && Math.abs(gestureState.dx) > 10;}/*** 表示申請(qǐng)成功,組件成為了事件處理響應(yīng)者* @param event* @param gestureState* @private*/_handlePanResponderGrant(event: Object, gestureState: Object): void {console.log('_handlePanResponderGrant');}/*** 處理滑動(dòng)事件* @param event* @param gestureState* @private*/_handlePanResponderMove(event: Object, gestureState: Object): void {console.log('_handlePanResponderMove');}/*** 結(jié)束事件的時(shí)候回調(diào)* @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'}, });然后運(yùn)行代碼(手指從右向左拖動(dòng))看log:
好啦,我們?cè)赺handlePanResponderMove方法中處理下滑動(dòng)事件:
/*** 處理滑動(dòng)事件* @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}]:
{/*內(nèi)容content*/}<View style={[styles.content, {transform: [{translateX: -100}]}]}><Text>我是item的內(nèi)容</Text></View>SwipeRow組件中我們添加一個(gè):
//上一次滑動(dòng)最后的left偏移量this._previousLeft = 0;//left偏移動(dòng)畫this.state = {currentLeft: new Animated.Value(this._previousLeft),};然后在move跟end的時(shí)候操作:
/*** 處理滑動(dòng)事件* @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,);}/*** 結(jié)束事件的時(shí)候回調(diào)* @param event* @param gestureState* @private*/_handlePanResponderEnd(event: Object, gestureState: Object): void {console.log('_handlePanResponderEnd');this._previousLeft = null;}最后付給動(dòng)畫組件:
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 {// 構(gòu)造constructor(props) {super(props);// 初始狀態(tài)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 平臺(tái)的事件處理,默認(rèn)是禁用的,全部使用 JS 中的事件處理,注意此函數(shù)目前只能在 Android 平臺(tái)上使用});//上一次滑動(dòng)最后的left偏移量this._previousLeft = 0;//left偏移動(dòng)畫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事件響應(yīng)者,返回true直接走onPanResponderMove* @param event* @param gestureState* @returns {boolean}* @private*/_handleMoveShouldSetPanResponderCapture(event: Object, gestureState: Object,): boolean {//當(dāng)垂直滑動(dòng)的距離<10 水平滑動(dòng)的距離>10的時(shí)候才讓捕獲事件console.log('_handleMoveShouldSetPanResponderCapture');return Math.abs(gestureState.dy) < 10 && Math.abs(gestureState.dx) > 10;}/*** 表示申請(qǐng)成功,組件成為了事件處理響應(yīng)者* @param event* @param gestureState* @private*/_handlePanResponderGrant(event: Object, gestureState: Object): void {console.log('_handlePanResponderGrant');}/*** 處理滑動(dòng)事件* @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,);}/*** 結(jié)束事件的時(shí)候回調(diào)* @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'}, });最后運(yùn)行效果:
我們只差最后一步了,就是在手指提起的時(shí)候讓滑動(dòng)部分反彈回去就可以了~ 先留給小伙伴自己實(shí)現(xiàn)吧,好吧~~感覺很簡單的東西,寫成文字還不是件容易的事情,吃飯去啦!!下節(jié)見咯~
總結(jié)
以上是生活随笔為你收集整理的React-Native带你一步一步实现侧滑删除的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑操作最忌讳十八个小动作,你有几个?
- 下一篇: 软件灰色按钮 隐藏按钮破解