//9.27,同时参考 http://www.tuicool.com/articles/Q36VJbj
在上一节课代码的基础上:
分析路由栈的结构(帖子后面有图片),见该集视频教程 栈:先进后出,后进先去!
componentWillMount() {
if (Platform.OS === ‘android’) {
BackAndroid.addEventListener(‘hardwareBackPress’, this.onBackAndroid);
}
}
componentWillUnmount() {
if (Platform.OS === ‘android’) {
BackAndroid.removeEventListener(‘hardwareBackPress’, this.onBackAndroid);
}
}
onBackAndroid = () => {
const { navigator } = this.props;
const routers = navigator.getCurrentRoutes();
console.log(‘当前路由长度:’+routers.length);
if (routers.length > 1) {
navigator.pop();
return true;//接管默认行为
}
return false;//默认行为
};
复制代码
需要注意的是,不论是bind还是箭头函数,
每次被执行都返回的是一个新的函数引用,
因此如果你还需要函数的引用去做一些别的事情(譬如卸载监听器),那么你必须自己保存这个引用
// 错误的做法
class PauseMenu extends React.Component{
componentWillMount(){
AppStateIOS.addEventListener(‘change’, this.onAppPaused.bind(this));
}
componentDidUnmount(){
AppStateIOS.removeEventListener(‘change’, this.onAppPaused.bind(this));
}
onAppPaused(event){
}
}
复制代码
// 正确的做法1
class PauseMenu extends React.Component{
constructor(props){
super(props);
this._onAppPaused = this.onAppPaused.bind(this);
}
componentWillMount(){
AppStateIOS.addEventListener(‘change’, this._onAppPaused);
}
componentDidUnmount(){
AppStateIOS.removeEventListener(‘change’, this._onAppPaused);
}
onAppPaused(event){
}
}
复制代码
// 正确的做法2
class PauseMenu extends React.Component{
componentWillMount(){
AppStateIOS.addEventListener(‘change’, this.onAppPaused);
}
componentDidUnmount(){
AppStateIOS.removeEventListener(‘change’, this.onAppPaused);
}
onAppPaused = (event) => {
//把方法直接作为一个arrow function的属性来定义,初始化的时候就绑定好了this指针
}
}
特别说明【回复本帖可见】:
本帖隐藏的内容
BackAndroid在iOS平台下是一个空实现,所以理论上不做这个Platform.OS === ‘android’判断也是安全的。
如果所有事件监听函数中,没有任何一个返回真值,就会默认调用默认行为
特别注意:navigator是同一个,这个事件在最外层注册就行(不是initialRoute的组件,是AppRegistry的组件),
否则会调用多次pop的,这个代码接管的是整个应用的后退键
放到initialRoute里会有问题,你两三个页面测不出来,页面层次多了组件会unmount,然后事件就丢了
addEventListener()第三个参数useCapture (Boolean)详细解析:
•true 的触发顺序总是在 false 之前;
•如果多个均为 true,则外层的触发先于内层;
•如果多个均为 false,则内层的触发先于外层。
例子:“再按一次退出应用”
//到了主页了
if (this.lastBackPressed && this.lastBackPressed + 2000 >= Date.now()) {
//最近2秒内按过back键,可以退出应用。
return false;
}
this.lastBackPressed = Date.now();
ToastAndroid.show(‘再按一次退出应用’,ToastAndroid.SHORT);
return true;
复制代码
我们在监听函数中不能决定是否要调用默认行为,要等待一个异步操作之后才调用默认行为,此时可以通过第二种办法:
使用BackAndroid.exitApp()来退出应用。
例子:在退出应用之前保存数据
写法1:
onBackAndroid = () =>{
saveData().then(()=>{
BackAndroid.exitApp();
});
return true;
}
复制代码
在监听函数中,我们开始异步事件,并直接return true。此时默认行为不会被调用。当保存完毕后,我们调用exitApp(),触发默认行为,退出应用。
写法2:
onBackAndroid = async () =>{
await saveData();
BackAndroid.exitApp();
}
复制代码
这里我们用了async函数,async 函数总是返回一个Promise,Promise作为一个对象,也被认为是一个“真值”,所以这种情况下默认行为总是不会被调用。当保存完毕后,我们调用exitApp(),触发默认行为,退出应用。
例子:根据当前界面决定作何动作
有时候我们有这样的需求:当用户处于某些界面下时,back键要做特殊的动作,如:提示用户是否要保存数据,或者解锁界面禁止back键返回等等。此时,最佳实践是在route或route中对应的Component上保存关于如何处理back键的信息:
onBackAndroid = () => {
const nav = this.navigator;
const routers = nav.getCurrentRoutes();
if (routers.length > 1) {
const top = routers[routers.length - 1];
if (top.ignoreBack || top.component.ignoreBack){
// 路由或组件上决定这个界面忽略back键
return true;
}
const handleBack = top.handleBack || top.component.handleBack;
if (handleBack) {
// 路由或组件上决定这个界面自行处理back键
return handleBack();
}
// 默认行为: 退出当前界面。
nav.pop();
return true;
}
return false;
};
复制代码