Detailed explanation of the reason why RN TouchableOpacity click event does not respond

problem


First, the entire page layout is as follows:

In the actual development, it is found that all area click events in the following red box have no effect and will not be triggered

Problem location

RN basic touch component attempt

TouchableHighlight,TouchableNativeFeedback, TouchableOpacity , TouchableWithoutFeedback

  • onPressIn: click start;
  • onPressOut: click end or leave;
  • onPress: click the event callback;
  • onLongPress: long press event callback.

The above touch events are invalid in the red box area no matter how you try

Pressable

Pressable Is a package of core components, which can detect the press interaction of any sub component at different stages

This click is also invalid. At that time, a prompt in the official document: the touch area will not exceed the bound parent view. When pressing the overlapping brother view, the view with higher z-index will take precedence

Inspired to try:

Positioning layout & Solutions

First, open the layout border of Android debugging tool on the real machine to show the layout effect of the front end as follows

  • The first is the banner area with background at the top
  • Then there is the check-in card
  • It is obvious from the layout that the banner and the check-in card overlap
  • Check step by step from code positioning

    const styles = StyleSheet.create({
      banner: {
            height: 170,
            width: width,
            paddingTop: 25,
            paddingLeft: 15,
            backgroundColor: Colors.F4F5F7,
        },
    })
    <ImageBackground
          style={styles.banner}
          source={imageBanner}
          resizeMode="contain"
      >
          <Text style={styles.bannerTitle}>Currently available points</Text>
          <View style={styles.bannerLeft}>
              <Text style={styles.bannerLeftCount}>
                  {taskInfo?.totalPoints}
              </Text>
              <TouchableOpacity
                  onPress={() => {
                      jumpToNewTask();
                  }}
              >
                  <ImageBackground
                      style={styles.newTaskBtn}
                      source={NewTaskBtn}
                  >
                      <Image
                          style={styles.bannerLeftImage}
                          source={MoneyImage}
                      />
                      <Text style={styles.newTaskText}>Newbie Task </Text>
                      <Image
                          style={styles.triangle}
                          source={TriangleRight}
                      />
                  </ImageBackground>
              </TouchableOpacity>
          </View>
          <TouchableOpacity
              style={styles.redeemBtnContent}
              onPress={() => {
                  jumpToRedeem();
              }}
          >
              <ImageBackground
                  style={styles.redeemBtn}
                  source={RedeemBtn}
              >
                  <Text style={styles.redeemBtnText}>Point exchange</Text>
              </ImageBackground>
          </TouchableOpacity>
              {/* Seven day check-in area */}
          <SignList
              data={taskDaily}
              info={taskInfo}
              signCallback={() => {
                  getTaskInfo();
                  getSignListInit();
              }}
          />
      </ImageBackground>
  • It is obvious from the code that the check-in card is wrapped inside the ImageBackground of the banner, while the height of the banner is only 170px
  • In this way, according to the official, the touch area will not exceed the parent container area, and the actual clickable area is only the touchable area within the banner height 170
  • Then the problem is very clear. You need to modify the overall layout of the page, as follows

    const styles = StyleSheet.create({
      banner: {
            height: 170,
            width: width,
            paddingTop: 25,
            paddingLeft: 15,
            backgroundColor: Colors.F4F5F7,
        },
      signListContent: {
            borderRadius: 8,
            backgroundColor: Colors.FFFFFF,
            width: width - 30,
            marginTop: 25,
            paddingVertical: 20,
            position: 'absolute',
            top: 80,
            left: 15,
        }
    })
    <ImageBackground
          style={styles.banner}
          source={imageBanner}
          resizeMode="contain"
      >
          <Text style={styles.bannerTitle}>Currently available points</Text>
          <View style={styles.bannerLeft}>
             ...
          </View>
          ...
      </ImageBackground>
      {/* Seven day check-in area */}
      <SignList
          data={taskDaily}
          info={taskInfo}
          signCallback={() => {
              getTaskInfo();
              getSignListInit();
          }}
      />
  • The check-in card is directly taken out and leveled with the banner, and then the positioning absolute is used to overlay the card to the banner area, so that its parent container is the outermost body document stream, so that the interior of the card is a touchable area, which perfectly solves the problem

Keywords: Front-end React react-native

Added by magic-eyes on Mon, 20 Dec 2021 14:45:59 +0200