You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Expo构建的React Native APK在OnePlus 5T上字符串末尾内容缺失

字符串显示截断问题(OnePlus 5T设备,React Native + NativeBase)

我用React Native开发的注册页面,通过eas build -p android --profile preview生成Android预览版APK。在Pixel 5模拟器上运行正常,但安装到OnePlus 5T设备后,部分字符串显示不完整:

  • 'Username'显示为'Usernam',末尾'e'缺失
  • 'Password'显示为'Passwor',末尾'd'缺失
  • 'Login here.'显示为'Login',末尾'here.'缺失

试过在字符串后加空格或{" "},Expo Go模拟器里偶尔能解决,但APK版本完全无效。相关代码如下:


1. RegisterScreen.tsx

import { Text } from '@components';
import { useAlertToast } from '@hooks';
import { useNavigation } from '@react-navigation/native';
import { setAuthState, useRegisterMutation } from '@store';
import { Button, Center, FormControl, Input, Link, Stack, WarningOutlineIcon } from 'native-base';
import React, { useCallback, useEffect, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { KeyboardAvoidingView, StyleSheet } from 'react-native';
import { useDispatch } from 'react-redux';
import { StackNavProps } from 'StackNavigation';

import { saveEntryAsJson } from '../../utils/AsyncStorage';
import { Screens } from '../../utils/constants';

type RegisterationForm = {
  name: string;
  email: string;
  password: string;
};

export const Register = () => {
  const {
    control,
    handleSubmit,
    formState: { errors },
  } = useForm<RegisterationForm, any>({
    defaultValues: {
      name: "",
      email: "",
      password: "",
    },
  });

  const { navigate } = useNavigation<StackNavProps>();

  const [register, registerResult] = useRegisterMutation();
  const { showErrorToast } = useAlertToast();
  const dispath = useDispatch();

  useEffect(() => {
    if (registerResult.isSuccess) {
      saveEntryAsJson("userData", registerResult.data);
      dispath(setAuthState(registerResult.data));
      navigate(Screens.VERIFY_OTP);
    } else if (registerResult.isError) {
      //@ts-ignore
      showErrorToast(registerResult.error.data.message);
    }
  }, [registerResult.isSuccess]);

  const onSubmit = useCallback(async (data: RegisterationForm) => {
    await register({
      ...data,
    }).unwrap();
  }, []);

  return (
    <KeyboardAvoidingView style={styles.container}>
      <Center style={styles.innerContainer}>
        <FormControl isInvalid={!!errors.name} marginY={3}>
          <Stack mx="4">
            <FormControl.Label>
              <Text style={styles.inputLabel}>Username </Text>
            </FormControl.Label>
            <Controller
              control={control}
              rules={{
                required: true,
              }}
              render={({ field: { onChange, onBlur, value } }) => (
                <Input
                  onBlur={onBlur}
                  onChangeText={onChange}
                  value={value}
                  placeholder="Enter user name"
                />
              )}
              name="name"
            />
            {errors.name?.type === "required" && (
              <FormControl.ErrorMessage
                leftIcon={<WarningOutlineIcon size="xs" />}
              >
                User name is required
              </FormControl.ErrorMessage>
            )}
          </Stack>
        </FormControl>
        <FormControl isInvalid={!!errors.email} marginY={3}>
          <Stack mx="4">
            <FormControl.Label>
              <Text style={styles.inputLabel}>Email </Text>
            </FormControl.Label>
            <Controller
              control={control}
              rules={{
                required: true,
                // pattern: RegExp("/^w+([.-]?w+)*@w+([.-]?w+)*(.ww+)+$/"),
              }}
              render={({ field: { onChange, onBlur, value } }) => (
                <Input
                  onBlur={onBlur}
                  onChangeText={onChange}
                  value={value}
                  placeholder="Enter email address"
                  keyboardType="email-address"
                />
              )}
              name="email"
            />
            {errors.email?.type === "pattern" && (
              <FormControl.ErrorMessage
                leftIcon={<WarningOutlineIcon size="xs" />}
              >
                Invalid email address
              </FormControl.ErrorMessage>
            )}
            {errors.email?.type === "required" && (
              <FormControl.ErrorMessage
                leftIcon={<WarningOutlineIcon size="xs" />}
              >
                Email address required
              </FormControl.ErrorMessage>
            )}
          </Stack>
        </FormControl>
        <FormControl isInvalid={!!errors.password} marginY={3}>
          <Stack mx="4">
            <FormControl.Label>
              <Text style={styles.inputLabel}>Password</Text>
            </FormControl.Label>
            <Controller
              control={control}
              rules={{
                required: true,
                minLength: 8,
              }}
              render={({ field: { onChange, onBlur, value } }) => (
                <Input
                  onBlur={onBlur}
                  onChangeText={onChange}
                  value={value}
                  placeholder="Enter password"
                  type="password"
                />
              )}
              name="password"
            />
            {errors.password?.type === "minLength" && (
              <FormControl.ErrorMessage
                leftIcon={<WarningOutlineIcon size="xs" />}
              >
                Atleast 8 characters are required.
              </FormControl.ErrorMessage>
            )}
            {errors.password?.type === "required" && (
              <FormControl.ErrorMessage
                leftIcon={<WarningOutlineIcon size="xs" />}
              >
                Password required.
              </FormControl.ErrorMessage>
            )}
          </Stack>
        </FormControl>
        <Button
          colorScheme={"orange"}
          style={styles.registerButton}
          onPress={handleSubmit(onSubmit)}
        >
          {"Register "}
        </Button>
        <Link style={styles.otherLink} onPress={() => navigate(Screens.LOGIN)}>
          <Text>Already registered?. Login here.</Text>
        </Link>
      </Center>
    </KeyboardAvoidingView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  inputLabel: {
    fontWeight: "700",
    color: "black",
  },
  innerContainer: {
    flex: 1,
    paddingHorizontal: 5,
  },
  registerButton: {
    borderRadius: 5,
    padding: 5,
    marginTop: 20,
  },
  otherLink: {
    marginTop: 10,
  },
});

2. Text Component

import { ITextProps, Text as NativeText } from 'native-base';

export const Text = ({ children, ...rest }: ITextProps) => {
  return (
    <NativeText
      _light={{ color: "black" }}
      _dark={{ color: "white" }}
      {...rest}
    >
      {children}
    </NativeText>
  );
};

解决思路及方案

1. 调整Label/Text组件的布局约束

NativeBase的FormControl.Label在部分设备上可能存在宽度计算不足的问题,导致文本被截断。可以给Label或内部Text添加样式:

// 给FormControl.Label添加右侧内边距
<FormControl.Label style={{ paddingRight: 8 }}>
  <Text style={styles.inputLabel}>Username</Text>
</FormControl.Label>

// 或者给Text添加flexShrink:0,禁止文本压缩
<Text style={{ ...styles.inputLabel, flexShrink: 0 }}>Username</Text>

2. 临时替换自定义Text组件

自定义Text组件的默认样式可能和NativeBase的Label存在冲突,暂时改用原生NativeBase Text测试:

// 替换import语句
import { Text } from 'native-base';

如果问题消失,说明自定义Text的_light/_dark配置影响了布局,需要调整样式优先级。

3. 关闭OnePlus设备的自适应字体缩放

OnePlus系统默认的文字缩放优化可能强制截断文本,在设备设置中操作:

  • 打开「设置」→「显示」→「字体大小与显示」
  • 关闭「自适应字体缩放」选项,重启应用查看效果

4. 添加零宽空格强制保留文本

在文本末尾添加零宽空格(\u200B)或不换行空格(\u00A0),避免被截断:

<Text style={styles.inputLabel}>Username\u200B</Text>
<Text>Already registered?. Login here.\u00A0</Text>

5. 禁用打包时的资源压缩

EAS Build的资源压缩可能导致字符丢失,在eas.json的preview配置中添加:

"preview": {
  "android": {
    "buildType": "apk",
    "extraParams": {
      "android.enableResourceOptimizations": false
    }
  }
}

内容的提问来源于stack exchange,提问作者Atharva Kalsekar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 10:18:10