Site icon May's Notes

React Native 奇幻之旅(17)-防鍵盤遮擋(KeyboardAvoidingView)

React Native logo

這是一個很普通的表單畫面,左邊是 Android 右邊是 iOS。

當我們點擊輸入框時 Android 會自動在展開鍵盤的同時擠壓畫面以避免輸入框被鍵盤遮住,而 iOS 並不會做任何事情,因此我們需要做額外的設置。

KeyboardAvoidingView

RN 內建提供了避免鍵盤遮擋的組件 <KeyboardAvoidingView />,使用方式是將 KeyboardAvoidingView 包在表單外層:

import { KeyboardAvoidingView } from 'react-native'

return (
  <KeyboardAvoidingView style={{ flexGrow: 1 }} behavior="padding">
    <View style={{ flex: 1 }}>
        // form
    </View>
  </KeyboardAvoidingView>
)

但悲劇了…在 iOS 上一切正常,反而變成 Android 一團糟,這是因為我把 behavior 屬性設為了 padding

iOSAndroid

behavior 屬性

behavior 屬性是用於設置當鍵盤彈出時視圖該如何調整才能不遮擋輸入框。有 height, position, padding 三個模式可選:

對 Android 和 iOS 來說,不同的 behavior 可能會有不同的顯示結果,所以最好是雙系統都要個別設置。

以我目前的 UI 為例,height 對 iOS 無效,Android 正常

iOSAndroid

position 直接雙系統 UI 都跑版了

測試後發現我的 UI 在 Android 適合使用 height,而 iOS 適合使用 padding

所以可以改成:

import { Platform, KeyboardAvoidingView } from 'react-native'

return (
  <KeyboardAvoidingView
    style={{ flexGrow: 1 }}
    behavior={Platform.select({ ios: 'padding', android: 'height' })}
  >
    // ...
  </KeyboardAvoidingView>
)

如果覺得畫面擠壓的高度不太夠,可以再去設置 keyboardVerticalOffset

注意

KeyboardAvoidingView 與 ScrollView 一起使用

將 KeyboardAvoidingView 包在 ScrollView 外面:

import { KeyboardAvoidingView, ScrollView } from 'react-native'

export const App = () => {
  return (
    <KeyboardAvoidingView
      style={{ flexGrow: 1 }}
      behavior={Platform.select({ ios: 'padding', android: 'height' })}
    >
     <ScrollView>
      // ...
     </ScrollView>
    </KeyboardAvoidingView>
  )
}

Android 的鍵盤遮擋 BUG (Expo)

我遇到 BUG 的專案使用的 expo 和 RN 版本:

相關 issues

https://github.com/facebook/react-native/issues/38152
https://github.com/expo/expo/issues/23564

在 Expo SDK 升級到 49 之後,如果在 KeyboardAvoidingView 中使用 ScrollView,那點擊輸入框展開鍵盤時並不會擠壓(Android),並且 Development build 測試跟本地無差,要 build 出來使用才會發生。

RN 0.72.4 修復了這個 bug:

解決方式

  1. 升級 Expo SDK 到最新版本
  2. 使用 "softwareKeyboardLayoutMode": "pan" 能工作,但效果可能並不完美

點擊背景關閉鍵盤

跟點背景關閉 Modal 的寫法一樣,都是在外層包一個 TouchableWithoutFeedback

import { TouchableWithoutFeedback, Keyboard, KeyboardAvoidingView, Platform } from 'react-native'

// ...

<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
  <KeyboardAvoidingView style={{ flexGrow: 1 }} behavior={Platform.select({ ios: 'padding', android: 'height' })}>
    // form
  </KeyboardAvoidingView>
</TouchableWithoutFeedback>

參考資料

Exit mobile version