React Native:按两下以退出应用程序

时间:2018-09-10 07:34:43

标签: react-native react-native-android exit react-native-ios double-click

如何通过两次单击后退按钮退出应用程序而无需Redux

我一直在寻找一种解决方案,以限制用户,并且一键单击“响应本机”就不会退出应用程序。

9 个答案:

答案 0 :(得分:12)

import React, {Component} from 'react';
import {BackHandler, View, Dimensions, Animated, TouchableOpacity, Text} from 'react-native';

let {width, height} = Dimensions.get('window');


export default class App extends Component<Props> {

    state = {
        backClickCount: 0
    };

    constructor(props) {
        super(props);

        this.springValue = new Animated.Value(100);

    }

    componentWillMount() {
        BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }

    componentWillUnmount() {
        BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }

    _spring() {
        this.setState({backClickCount: 1}, () => {
            Animated.sequence([
                Animated.spring(
                    this.springValue,
                    {
                        toValue: -.15 * height,
                        friction: 5,
                        duration: 300,
                        useNativeDriver: true,
                    }
                ),
                Animated.timing(
                    this.springValue,
                    {
                        toValue: 100,
                        duration: 300,
                        useNativeDriver: true,
                    }
                ),

            ]).start(() => {
                this.setState({backClickCount: 0});
            });
        });

    }


    handleBackButton = () => {
        this.state.backClickCount == 1 ? BackHandler.exitApp() : this._spring();

        return true;
    };


    render() {

        return (
            <View style={styles.container}>
                <Text>
                    container box
                </Text>

                <Animated.View style={[styles.animatedView, {transform: [{translateY: this.springValue}]}]}>
                    <Text style={styles.exitTitleText}>press back again to exit the app</Text>

                    <TouchableOpacity
                        activeOpacity={0.9}
                        onPress={() => BackHandler.exitApp()}
                    >
                        <Text style={styles.exitText}>Exit</Text>
                    </TouchableOpacity>

                </Animated.View>
            </View>
        );
    }
}


const styles = {
    container: {
        flex: 1,
        justifyContent: "center",
        alignItems: "center"
    },
    animatedView: {
        width,
        backgroundColor: "#0a5386",
        elevation: 2,
        position: "absolute",
        bottom: 0,
        padding: 10,
        justifyContent: "center",
        alignItems: "center",
        flexDirection: "row",
    },
    exitTitleText: {
        textAlign: "center",
        color: "#ffffff",
        marginRight: 10,
    },
    exitText: {
        color: "#e5933a",
        paddingHorizontal: 10,
        paddingVertical: 3
    }
};

在便笺簿中运行:https://snack.expo.io/HyhD657d7

答案 1 :(得分:4)

我已经通过分离功能组件的方式解决了它:

import * as React from 'react';
import {useEffect, useState} from 'react';
import {Platform, BackHandler, ToastAndroid} from 'react-native';

export const ExecuteOnlyOnAndroid = (props) => {
  const {message} = props;
  const [exitApp, setExitApp] = useState(0);
  const backAction = () => {
    setTimeout(() => {
      setExitApp(0);
    }, 2000); // 2 seconds to tap second-time

    if (exitApp === 0) {
      setExitApp(exitApp + 1);

      ToastAndroid.show(message, ToastAndroid.SHORT);
    } else if (exitApp === 1) {
      BackHandler.exitApp();
    }
    return true;
  };
  useEffect(() => {
    const backHandler = BackHandler.addEventListener(
      'hardwareBackPress',
      backAction,
    );
    return () => backHandler.remove();
  });
  return <></>;
};

export default function DoubleTapToClose(props) {
  const {message = 'tap back again to exit the App'} = props;
  return Platform.OS !== 'ios' ? (
    <ExecuteOnlyOnAndroid message={message} />
  ) : (
    <></>
  );
}

您只需要在应用程序中包含此组件。因为IOS没有后退按钮,所以IOS不需要此功能。上面的组件会自动检测Device是否为Android。

默认情况下,Toast中显示的消息是用英语预定义的,但是如果在DoubleTapToClose-Component中添加名为message的属性,则可以设置自己的消息。

...
import DoubleTapToClose from '../lib/android_doubleTapToClose'; 
...
return(
    <>
        <DoubleTapToClose />
        ...other Stuff goes here
    </>

根据导航结构,您必须检查您是否位于应用程序的initialScreen中。 就我而言,我有一个抽屉,里面有多个StackNavigatiors。 因此,我检查当前屏幕是否为初始屏幕(索引:0)。如果是这样,我设置了一个Hook-Variable,仅用于那些初始屏幕。

看起来像这样:

const isCurrentScreenInitialOne = (state) => {
  const route = state.routes[state.index];
  if (route.state) {
    // Dive into nested navigators
    return isCurrentScreenInitialOne(route.state);
  }
  return state.index === 0;
};

...
...

export default function App() {
...
  const [isInitialScreen, setIsInitialScreen] = useState(true);

  {isInitialScreen && (<DoubleTapToClose message="Tap again to exit app" />)}

...
...
<NavigationContainer
          ...
          onStateChange={(state) => {
            setIsInitialScreen(isCurrentScreenInitialOne(state));
          }}>

如果该描述对您有所帮助,请不要错过投票。

答案 2 :(得分:2)

下面的代码说明自己。 诀窍是在 主AppContainer 中拥有它,而不是在 每页

中拥有它
import {  Alert,  BackHandler,  ToastAndroid } from 'react-native';
import {  StackActions } from 'react-navigation';
// common statless class variable.
let backHandlerClickCount = 0;

class App extends React.Component {
    constructor(props) {
      super(props);
      // add listener to didFocus
      this._didFocusSubscription = props.navigation.addListener('didFocus', payload =>
        BackHandler.addEventListener('hardwareBackPress', () => this.onBackButtonPressAndroid(payload)));
    }

    // remove listener on unmount 
    componentWillUnmount() {
      if (this._didFocusSubscription) {
        this._didFocusSubscription.remove();
      }
    }

    onBackButtonPressAndroid = () => {
      const shortToast = message => {
        ToastAndroid.showWithGravityAndOffset(
          message,
          ToastAndroid.SHORT,
          ToastAndroid.BOTTOM,
          25,
          50
        );

        const {
          clickedPosition
        } = this.state;
        backHandlerClickCount += 1;
        if ((clickedPosition !== 1)) {
          if ((backHandlerClickCount < 2)) {
            shortToast('Press again to quit the application!');
          } else {
            BackHandler.exitApp();
          }
        }

        // timeout for fade and exit
        setTimeout(() => {
          backHandlerClickCount = 0;
        }, 2000);

        if (((clickedPosition === 1) &&
            (this.props.navigation.isFocused()))) {
          Alert.alert(
            'Exit Application',
            'Do you want to quit application?', [{
              text: 'Cancel',
              onPress: () => console.log('Cancel Pressed'),
              style: 'cancel'
            }, {
              text: 'OK',
              onPress: () => BackHandler.exitApp()
            }], {
              cancelable: false
            }
          );
        } else {
          this.props.navigation.dispatch(StackActions.pop({
            n: 1
          }));
        }
        return true;
      }

    }

答案 3 :(得分:2)

更好的方法是简单地使用BackHandler和ToastAndroid

import { BackHandler, ToastAndroid } from 'react-native';
//rest of imports

class SomeClass extends Component{
    constructor(state, props) {
        super(state, props)
        this.state = {
            validCloseWindow: false
        }
    }
    async componentDidMount() {
        BackHandler.addEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }

    componentWillUnmount() {
        BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton.bind(this));
    }
    handleBackButton = () => {
        if (!this.props.navigation.canGoBack()) {
            if (this.state.validCloseWindow)
                return false;
            this.state.validCloseWindow = true
            setTimeout(() => {
                this.state.validCloseWindow = false
            }, 3000);
            ToastAndroid.show("Press Again To Exit !", ToastAndroid.SHORT);
            return true;
        }
    };
//rest of component code
}

只需确保在initialRoute页面上使用它即可进行导航。

答案 4 :(得分:2)

对不起,如果我参加聚会迟到了,但是我有一个类似的要求,并通过创建自己的自定义钩子来解决了这个问题!

let currentCount = 0;
export const useDoubleBackPressExit = (exitHandler: () => void) => {
  if (Platform.OS === "ios") return;
  const subscription = BackHandler.addEventListener("hardwareBackPress", () => {
    if (currentCount === 1) {
      exitHandler();
      subscription.remove();
      return true;
    }
    backPressHandler();
    return true;
  });
};
const backPressHandler = () => {
  if (currentCount < 1) {
    currentCount += 1;
    WToast.show({
      data: "Press again to close!",
      duration: WToast.duration.SHORT,
    });
  }
  setTimeout(() => {
    currentCount = 0;
  }, 2000);
};

现在,只需执行以下操作,我便可以在任何地方使用它:

useDoubleBackPressExit(() => { 
   // user has pressed "back" twice. Do whatever you want! 
});

答案 5 :(得分:1)

<dependency>
            <groupId>io.dropwizard.metrics</groupId>
            <artifactId>metrics-core</artifactId>
            <version>4.1.0</version>
</dependency>
<dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient_dropwizard</artifactId>
            <version>0.6.0</version>
</dependency>
<dependency>
            <groupId>io.prometheus</groupId>
            <artifactId>simpleclient_servlet</artifactId>
            <version>0.6.0</version></dependency>

答案 6 :(得分:1)

目前最简单的方法:

在App.js中:

componentDidMount() {
    const backHandler=BackHandler.addEventListener('hardwareBackPress', ()=>{
        if(this.backHandler){
            return false;
        }
        Toast.show('再按一次退出应用');
        this.backHandler=backHandler;
        setTimeout(()=>{
            this.backHandler=null;
        },2000);
        return true;
    });
}

componentWillUnmount() {
    this.backHandler.remove();
}

答案 7 :(得分:1)

我在应用程序中使用的最简单的解决方案是这个。它在react- Navigation 4.4.1上运行良好,并且比此处提供的其他一些正确答案要短得多。

${variableName}

答案 8 :(得分:0)

另一种解决方案是使用exit-on-double-back软件包。

enter image description here