跳至主要內容

React Native

俞文健大约 5 分钟

核心组件

Image

图片。支持网络图片、静态图片、相册图片。

<Image
  source={require("@/assets/images/react-logo.png")}
  resizeMode="cover"
/>
  • resizeMode:设置图片的显示模式。

    • cover:(默认)保持宽高比,不留空白。图片可能完全覆盖或者超出容器。

    • contain:保持宽高比,完全显示图片。容器可能有空白。

    • stretch:不保持宽高比,图片会被拉伸到刚好填满容器。

    • repeat:保持原尺寸,图片会平铺直至填满容器。

    • center:居中不拉伸。

警告

require 是在编译时期执行,而不是运行时期。所以必须使用静态字符串!

// 正确
const icon = active
  ? require("@/assets/images/icon.png")
  : require("@/assets/images/icon.png")
return <Image source={icon} />

// 编译错误
const icon = active
  ? "@/assets/images/icon.png"
  : "@/assets/images/icon.png"
return <Image source={require(icon)} />

引用网络或 base64 数据的图片时,需要使用 uri 指定资源地址或 base64 编码,并设置尺寸。

// 正确
<Image
  source={{ uri: "http://cholez.cn/icon.png" }}
  style={{ width: 400, height: 400 }}
/>

// base64
<Image
  source={{ uri: "data:image/jpeg;base64..." }}
  style={{ width: 32, height: 32 }}
/>

// 错误,未设置尺寸
<Image source={{ uri: "http://cholez.cn/icon.png" }} />

TextInput

文本输入框

const [text, setText] = useState("hello react-native")

return <TextInput value={text} onChangeText={setText} />
  • keyboardType:设置键盘的类型。详见 React Native TextInput keyboardTypeopen in new window

    • default:默认键盘。

    • number-pad:数字键盘。

    • decimal-pad:数字键盘。

    • numeric:数字键盘。

    • email-address:英文键盘(电子邮件)。

    • phone-pad:数字键盘(电话号码)。

  • returnKeyType:设置 “确定” 按钮显示的内容(三星 & 搜狗键盘)。

    • done:完成。

    • go:转到 / 开始。

    • next:下一步。

    • search:搜索。

    • send:发送。

Button *

按钮。在 Android 和 iOS 会呈现不同的样式,不推荐使用

<Button title="Button" onPress={onPressFn} />

Pressable

按钮。RN 推荐使用 <Pressable>,可以自定义样式。它有两种触发情况:

  • 轻按:onPressIn => onPressOut => onPress

  • 长按:onPressIn => onLongPress => onPressOut

<Pressable> 默认会撑满整个屏幕宽度,设置 alignSelf: center 可以让它被内容撑开。

<Pressable
  onPressIn={/* 按压 */}
  onPressOut={/* 按压结束 */}
  onPress={/* 按压结束后 */}
  onLongPress={/* 长按 */}
  style={state => state.pressed ? styles.pressed : styles.button}
  hitSlop={{ top: 20, bottom: 30 }}
>
  {state => state.pressed
    ? <Text style={styles.pressedText}>Pressed</Text>
    : <Text style={styles.buttonText}>Button</Text>
  }
</Pressable>
  • style:接受一个提供 “按压状态” 的函数。我们可以根据按钮是否处于按压状态,设置不同的样式。

  • hitSlop:扩大触发范围,优化用户体验(用户可能不会准确按压触发区域)。

ScrollView

滚动视图。在 Web App 中,想要实现滚动视图的效果,需要借助第三方库。RN 直接提供了这样的组件。

<ScrollView>
  <Text style={{ fontSize: 96 }}>Scrolling down</Text>
  <Image source={logo} />
  <Image source={logo} />
  <Image source={logo} />
  <Image source={logo} />
  <Image source={logo} />
  {/* repeat ... */}
</ScrollView>

FlatList

长列表<FlatList> 优先渲染屏幕上可见的元素,而不是所有元素。

<FlatList
  data={[
    { name: "Devin" },
    { name: "Dan" },
    { name: "James" },
    { name: "Joel" },
    { name: "John" }
  ]}
  renderItem={({ item }) => <Text>{item.name}</Text>}
  keyExtractor={(item, index) => item.name + index}
/>

下拉刷新

onRefresh 会自动添加 <RefreshControl> 组件,以便实现 “下拉刷新” 功能。但是必须同时设置 refreshing

const [list, setList] = useState(initialList)

const [isFreshing, setFreshing] = useState(false)

const refresh = async () => {
  setFreshing(true)
  const response = await (await fetch(url)).json()
  setList(response.data)
  setFreshing(false)
}

return <FlatList
  data={list}
  renderItem={({ item }) => <Text>{item.desc}</Text>}
  keyExtractor={item => item.id}
  onRefresh={refresh}
  refreshing={isFreshing}
/>

上拉加载

当视图接近底部时,会触发 onEndReached 事件。设置 onEndReachedThreshold Prop 可以改变触发距离。

const [list, setList] = useState(initialList)

const [isReached, setReached] = useState(false)

const reach = async () => {
  setReached(true)
  const response = await (await fetch(url)).json()
  setList([...list, ...response.data])
  setReached(false)
}

return (
  <View>
    <FlatList
      data={list}
      renderItem={({ item }) => <Text>{item.desc}</Text>}
      keyExtractor={item => item.id}
      onEndReached={reach}
      onEndReachedThreshold={0.1}
    />
    {isReached && <ActivityIndicator />}
  </View>
)

SectionList

分组长列表<SectionList> 可以渲染一个标题,便于对数据进行分组。

<SectionList
  sections={[
    { title: "D", data: ["Devin", "Dan", "Dominic"] },
    { title: "J", data: ["Jackson", "James", "Jillian", "Jimmy"] }
  ]}
  renderSectionHeader={({ section }) => <Text>{section.title}</Text>}
  renderItem={({ item }) => <Text>{item}</Text>}
  keyExtractor={(item, index) => item + index}
/>

基础 API

StyleSheet

样式表。StyleSheet 是类似于 CSS 样式表的抽象。RN 建议使用 StyleSheet.create 来集中定义组件的样式。

const styles = StyleSheet.create({
  container: {
    flex: 1 // 在 React Native 中,flex 主轴默认垂直向下
  },
  title: {
    backgroundColor: "#ebc",
    color: "#fff",
    fontSize: 32,
    textAlign: "center"
  }
})

return (
  <View style={styles.container}>
    <Text style={styles.title}>React Native</Text>
  </View>
)

Alert

对话框。可以自定义样式与功能。

注意

Android 只能显示静态的提示框,iOS 支持文本输入框。

const alert = () => {
  Alert.alert("Alert", "Hello React-Native!", [
    {
      text: "No",
      onPress: () => console.log("cancel")
    },
    {
      text: "Yes",
      onPress: () => console.log("confirm")
    }
  ])
}

Keyboard

键盘事件。可以监听键盘行为,并做出响应。

useEffect(() => {
  const keyboardShowListener = Keyboard.addListener("keyboardDidShow", () => {
    console.log("Keyboard Show")
  })
  const keyboardHideListener = Keyboard.addListener("keyboardDidHide", () => {
    console.log("Keyboard Hide")
  })
  
  return () => {
    keyboardShowListener.remove()
    keyboardHideListener.remove()
  }
}, [])

return <TextInput onSubmitEditing={Keyboard.dismiss} />

AppState

应用状态。可以读取应用的运行状态,或监听状态的变化。

  • active:前台运行。

  • background:后台运行。

useEffect(() => {
  const listener = AppState.addEventListener("change", state => {
    AppState.currentState // => 'active' or 'background'
    AppState.currentState === state // => true
  })
  
  return listener.remove
}, [])

设备 API

Platform

设备信息。可以获取设备的相关信息。

// 系统名称
Platform.OS // android

// 系统版本
Platform.Version // 34

// 设备品牌
Platform.constants.Brand // samsung

// 设备型号
Platform.constants.Model // SM-G9960

// 是否是 iPad
Platform.isPad // false

// 是否是 TV
Platform.isTV // false

Appearance

外观偏好。可以获取或修改主题色模式。

// 主题色模式
Appearance.getColorScheme() // light

// 修改主题色模式
Appearance.setColorScheme("dark")

Dimensions

屏幕尺寸。可以获取屏幕的宽高和缩放系数。

const { width, height, scale } = Dimensions.get("window")

PixelRatio

像素比例。可以获取像素密度和字体缩放比例。

// 像素密度
PixelRatio.get() // 2.8125 (Galaxy21+)

// 字体缩放比例
PixelRatio.getFontScale() // 1

// 将布局尺寸(dp)转换为像素尺寸(px)
PixelRatio.getPixelSizeForLayoutSize(100) // 281

动画 API

LayoutAnimation

布局动画。动画会在下一次渲染或布局周期运行。用于透明度渐变、缩放。

// 在 Android 上使用此动画,需要启用
if (
  Platform.OS === "android" &&
  UIManager.setLayoutAnimationEnabledExperimental
) {
  UIManager.setLayoutAnimationEnabledExperimental(true)
}

return <Button
  title="Click"
  onPress={() => LayoutAnimation.configureNext(LayoutAnimation.Presets.spring)}
/>

Animated

更精细的动画

注意

不要直接修改动画值!可以将动画值赋给 ref.current

const fadeValue = useRef(new Animated.Value(0)).current

const fadeIn = () => {
  // Will change fadeValue to 1 in 5 seconds
  Animated.timing(fadeValue, {
    toValue: 1,
    duration: 5000,
    useNativeDriver: true
  }).start()
}

const fadeOut = () => {
  // Will change fadeValue to 0 in 3 seconds
  Animated.timing(fadeValue, {
    toValue: 0,
    duration: 3000,
    useNativeDriver: true
  }).start()
}

return (
  <SafeAreaView>
    <Animated.View style={{ opacity: fadeValue }}>
      <Text>Fading View!</Text>
    </Animated.View>
    <View>
      <Button title="Fade In View" onPress={fadeIn} />
      <Button title="Fade Out View" onPress={fadeOut} />
    </View>
  </SafeAreaView>
)

Expo SDK

ImagePicker

访问相册并选择图片。

import * as ImagePicker from "expo-image-picker"

const pickImage = async () => {
  // 询问用户权限
  const permission = await ImagePicker.requestMediaLibraryPermissionsAsync()
  if (!permission.granted) return alert("拒绝访问!")
  
  // 从相册获取图片
  const picker = await ImagePicker.launchImageLibraryAsync()
  if (picker.canceled) return alert("取消选择图片")
  
  setUri(picker.assets?.[0].uri!)
}

Sharing

分享文件。

import * as Sharing from "expo-sharing"

const share = async () => {
  if (Platform.OS === "web") return alert("平台不支持分享")
  await Sharing.shareAsync(uri)
}