React Native 小記 – TouchableOpacity 單次點選無效

cafeting發表於2019-03-02

一般是焦點問題,解決方法:即在 ScrollView 標籤內,根據情況設定其 keyboardShouldPersistTaps 屬性值為 always 或者 handled 。詳細解決過程如下:

0x00 描述

收到測試人員提交的 Bug:帳號密碼輸入完畢按返回鍵關閉鍵盤後,點選登入沒反應,再點一次才執行登入操作。網上類似的情況還有 “當點選 TouchableOpacity 時,要點選兩下才會觸發 onPress() ”、“在 ScrollView 中 TouchableOpacity 需要在 TextInput 失去焦點後才能點選”等。

0x01 問題查詢

作為一個 Android 開發者,看到情況描述,聯想到是焦點問題。類似於 Android 原生開發的『ListView 的 Item 中 包含 EditText Button 時:EditText 與 Button 如何獲取焦點、無法點選、ListView 不能滑動等』。

由於我遇到的是 ScrollView 使用時出現的問題,檢視下 scrollview 的官方文件 發現有個 keyboardShouldPersistTaps 的屬性,用於處理此類情況。

此外,在 stackoverflow 上也搜尋到相關的回答,說是 ListView 也有此屬性,但我本地 react-native-0.57.2 ListView 原始碼中並沒有此屬性。

0x02 解決方案

再次看文件:( 官方文件 | 中文文件

scrollview @ keyboardShouldPersistTaps

如果當前介面有軟鍵盤,那麼點選 ScrollView 後是否收起鍵盤,取決於本屬性的設定。

  • `never` (預設值),點選 TextInput 以外的子元件會使當前的軟鍵盤收起。此時子元素不會收到點選事件。
  • `always`,鍵盤不會自動收起,ScrollView 也不會捕捉點選事件,但子元件可以捕獲。
  • `handled`,當點選事件被子元件捕獲時,鍵盤不會自動收起。這樣切換 TextInput 時鍵盤可以保持狀態。多數帶有TextInput 的情況下你應該選擇此項。
  • false,已過時,請使用 `never`代替。
  • true,已過時,請使用 `always` 代替。

經測試,使用 always 或者 handled 均可解決發生的問題,由於我這裡是 ScrollView 內部存在多個 TextInput,故選擇 handled 值。

示例程式碼:

<ScrollView
	style={styles.mScrollView}
	keyboardShouldPersistTaps={`handled`}
	>
	<View style={styles.root}>
		<View style={styles.userRoot}>
			<Text style={styles.userRootV1}>使用者名稱:</Text>
			<View style={{
                            flex: 1,
                            borderBottomColor: `#f0f0f0`,
                            borderBottomWidth: 1
                        }}>
                            <LessBorderTextInput
                                style={styles.userRootV2}
                                multiline={false}
                                placeholder={`請輸入使用者名稱`}
                                placeholderTextColor={`#ccc`}
                                autoFocus={false}
                                onChangeText={(newText) => this.updateUser(newText)}
                                returnKeyType={`next`}
                                onSubmitEditing={() => {
                                    this._input.focus();
                                }}
                            />
                        </View>
                    </View>
                    <View style={styles.userPwdRoot}>
                        <Text style={styles.userPwdRootV1}>密碼:</Text>
                        <View style={{
                            flexDirection: `row`,
                            flex: 1,
                            borderBottomColor: `#f0f0f0`,
                            borderBottomWidth: 1,
                            alignItems: `center`,
                        }}>
                            <LessBorderTextInput
                                style={styles.userPwdRootV2}
                                placeholder={`請輸入密碼`}
                                multiline={false}
                                placeholderTextColor={`#ccc`}
                                secureTextEntry={!this.state.showPwd}
                                onChangeText={(newText) => this.updatePwd(newText)}
                                returnKeyType={`done`}
                                ref={(c) => this._input = c}
                            />
                            <TouchableOpacity
                                onPress={() => this._showPwd(!this.state.showPwd)}>
                                <Image
                                    source={pwd_icon}
                                    style={styles.userPwdRootV3}
                                />
                            </TouchableOpacity>
                        </View>
                    </View>
	<TouchableOpacity
		onPress={() => _login()}>
		<Text style={styles.userSignIn}>登入</Text>
	</TouchableOpacity>
</ScrollView>
複製程式碼

其中 LessBorderTextInput 是我參考官方文件封裝後無邊框(方便實現各種 UI 設計要求)的 TextInput ,並且增加了支援 ref 屬性的功能,可用於多處需要填寫內容時直接在鍵盤上點選下一項即自動進入下一項的輸入。參見部落格的相關文章。

0x03 總結

發現問題,藉助搜尋工具能很快得到解決方案,我這裡也特地把解決方法直接寫到了文章的開頭,至於如何解決問題,是給想了解原因的人準備的一個思路和說明。如果你有更好的見解,歡迎和我一起討論。

如果有什麼建議或者問題可以隨時聯絡我,共同探討學習:

相關文章