OpenClaw 刘海屏适配主要涉及状态栏、导航栏和安全区域的适配。以下是完整的适配方案

openclaw openclaw解答 2

获取刘海屏信息

检测设备类型

import { Platform, Dimensions, StatusBar } from 'react-native';
import { getStatusBarHeight } from 'react-native-status-bar-height';
import DeviceInfo from 'react-native-device-info';
// 检测是否为刘海屏设备
export const isNotchDevice = () => {
  if (Platform.OS === 'ios') {
    // iOS 刘海屏判断
    const { height, width } = Dimensions.get('window');
    return (
      Platform.OS === 'ios' &&
      !Platform.isPad &&
      !Platform.isTV &&
      (height === 812 || width === 812 || height === 896 || width === 896)
    );
  }
  // Android 刘海屏判断
  if (Platform.OS === 'android') {
    return DeviceInfo.hasNotch() || DeviceInfo.hasDynamicIsland();
  }
  return false;
};
// 获取状态栏高度
export const getStatusBarHeight = () => {
  if (Platform.OS === 'ios') {
    return isNotchDevice() ? 44 : 20;
  }
  return StatusBar.currentHeight || 0;
};
// 获取安全区域底部高度
export const getBottomSpace = () => {
  if (Platform.OS === 'ios') {
    return isNotchDevice() ? 34 : 0;
  }
  return 0;
};

安全区域适配组件

SafeAreaView 组件

import React from 'react';
import {
  View,
  StyleSheet,
  SafeAreaView as RNSafeAreaView,
  Platform,
} from 'react-native';
import { getStatusBarHeight, getBottomSpace } from './device';
interface SafeAreaViewProps {
  children: React.ReactNode;
  style?: any;
  topColor?: string;
  bottomColor?: string;
  topInset?: boolean;
  bottomInset?: boolean;
}
export const SafeAreaView: React.FC<SafeAreaViewProps> = ({
  children,
  style,
  topColor = 'transparent',
  bottomColor = 'transparent',
  topInset = true,
  bottomInset = true,
}) => {
  const statusBarHeight = getStatusBarHeight();
  const bottomSpace = getBottomSpace();
  if (Platform.OS === 'ios') {
    return (
      <View style={[styles.container, style]}>
        {topInset && (
          <View style={[styles.topBar, { height: statusBarHeight, backgroundColor: topColor }]} />
        )}
        <View style={[styles.content, { marginTop: topInset ? 0 : -statusBarHeight }]}>
          {children}
        </View>
        {bottomInset && (
          <View style={[styles.bottomBar, { height: bottomSpace, backgroundColor: bottomColor }]} />
        )}
      </View>
    );
  }
  // Android 使用 RN 的 SafeAreaView
  return (
    <RNSafeAreaView style={[styles.androidContainer, style]}>
      {children}
    </RNSafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  topBar: {
    width: '100%',
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
  },
  content: {
    flex: 1,
  },
  bottomBar: {
    width: '100%',
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
  },
  androidContainer: {
    flex: 1,
  },
});

带安全区域的头部导航栏

import React from 'react';
import {
  View,
  Text,
  StyleSheet,
  TouchableOpacity,
  StatusBar,
} from 'react-native';
import { getStatusBarHeight } from './device';
interface HeaderProps { string;
  leftText?: string;
  onLeftPress?: () => void;
  rightText?: string;
  onRightPress?: () => void;
}
export const Header: React.FC<HeaderProps> = ({
  leftText,
  onLeftPress,
  rightText,
  onRightPress,
}) => {
  const statusBarHeight = getStatusBarHeight();
  return (
    <View style={[styles.container, { paddingTop: statusBarHeight }]}>
      <StatusBar
        translucent
        backgroundColor="transparent"
        barStyle="dark-content"
      />
      <View style={styles.header}>
        {leftText && (
          <TouchableOpacity style={styles.leftButton} onPress={onLeftPress}>
            <Text style={styles.buttonText}>{leftText}</Text>
          </TouchableOpacity>
        )}
        <View style={styles.titleContainer}>
          <Text style={styles.title} numberOfLines={1}>
            {title}
          </Text>
        </View>
        {rightText && (
          <TouchableOpacity style={styles.rightButton} onPress={onRightPress}>
            <Text style={styles.buttonText}>{rightText}</Text>
          </TouchableOpacity>
        )}
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    backgroundColor: '#ffffff',
    borderBottomWidth: StyleSheet.hairlineWidth,
    borderBottomColor: '#e0e0e0',
  },
  header: {
    height: 44,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingHorizontal: 12,
  },
  leftButton: {
    minWidth: 60,
    justifyContent: 'center',
  },
  rightButton: {
    minWidth: 60,
    alignItems: 'flex-end',
    justifyContent: 'center',
  },
  buttonText: {
    fontSize: 16,
    color: '#007AFF',
  },Container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  }, {
    fontSize: 17,
    fontWeight: '600',
    color: '#000000',
  },
});

页面布局适配

基础页面布局

import React from 'react';
import {
  View,
  StyleSheet,
  ScrollView,
  KeyboardAvoidingView,
  Platform,
} from 'react-native';
import { SafeAreaView } from './SafeAreaView';
import { getBottomSpace } from './device';
interface PageProps {
  children: React.ReactNode;
  safeAreaTop?: boolean;
  safeAreaBottom?: boolean;
  scrollable?: boolean;
  style?: any;
}
export const Page: React.FC<PageProps> = ({
  children,
  safeAreaTop = true,
  safeAreaBottom = true,
  scrollable = false,
  style,
}) => {
  const Container = scrollable ? ScrollView : View;
  return (
    <SafeAreaView
      topInset={safeAreaTop}
      bottomInset={safeAreaBottom}
      style={[styles.container, style]}
    >
      <KeyboardAvoidingView
        style={styles.keyboardView}
        behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      >
        <Container
          style={[
            styles.content,
            scrollable && styles.scrollContent,
            { paddingBottom: safeAreaBottom ? getBottomSpace() : 0 }
          ]}
          showsVerticalScrollIndicator={false}
        >
          {children}
        </Container>
      </KeyboardAvoidingView>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  keyboardView: {
    flex: 1,
  },
  content: {
    flex: 1,
  },
  scrollContent: {
    flexGrow: 1,
  },
});

底部TabBar适配

import React from 'react';
import {
  View,
  TouchableOpacity,
  Text,
  StyleSheet,
  Dimensions,
} from 'react-native';
import { getBottomSpace } from './device';
const { width } = Dimensions.get('window');
interface TabBarItem {
  label: string;
  icon: React.ReactNode;
  onPress: () => void;
  active: boolean;
}
interface TabBarProps {
  items: TabBarItem[];
}
export const TabBar: React.FC<TabBarProps> = ({ items }) => {
  const bottomSpace = getBottomSpace();
  const tabWidth = width / items.length;
  return (
    <View style={[styles.container, { paddingBottom: bottomSpace }]}>
      <View style={styles.tabBar}>
        {items.map((item, index) => (
          <TouchableOpacity
            key={index}
            style={[styles.tabItem, { width: tabWidth }]}
            onPress={item.onPress}
            activeOpacity={0.8}
          >
            <View style={styles.iconContainer}>
              {item.icon}
            </View>
            <Text style={[
              styles.label,
              item.active && styles.activeLabel
            ]}>
              {item.label}
            </Text>
          </TouchableOpacity>
        ))}
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  container: {
    backgroundColor: '#ffffff',
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: '#e0e0e0',
  },
  tabBar: {
    flexDirection: 'row',
    height: 49,
  },
  tabItem: {
    alignItems: 'center',
    justifyContent: 'center',
  },
  iconContainer: {
    marginBottom: 2,
  },
  label: {
    fontSize: 10,
    color: '#8e8e93',
  },
  activeLabel: {
    color: '#007AFF',
  },
});

工具函数

设备信息工具

// deviceUtils.ts
import { Dimensions, Platform } from 'react-native';
import DeviceInfo from 'react-native-device-info';
export const DeviceUtils = {
  // 获取屏幕尺寸
  getScreenSize() {
    const { width, height } = Dimensions.get('window');
    return { width, height };
  },
  // 判断是否为刘海屏
  isNotchScreen() {
    const { height, width } = Dimensions.get('window');
    if (Platform.OS === 'ios') {
      // iPhone X, Xs, 11 Pro, 12 mini, 13 mini
      if (height === 812 || width === 812) return true;
      // iPhone XR, Xs Max, 11, 11 Pro Max, 12, 12 Pro, 13, 13 Pro, 14
      if (height === 896 || width === 896) return true;
      // iPhone 12 Pro Max, 13 Pro Max, 14 Plus
      if (height === 926 || width === 926) return true;
      // iPhone 14 Pro
      if (height === 852 || width === 852) return true;
      // iPhone 14 Pro Max
      if (height === 932 || width === 932) return true;
      return false;
    }
    if (Platform.OS === 'android') {
      return DeviceInfo.hasNotch();
    }
    return false;
  },
  // 获取安全区域
  getSafeAreaInsets() {
    const isNotch = this.isNotchScreen();
    if (Platform.OS === 'ios') {
      return {
        top: isNotch ? 44 : 20,
        bottom: isNotch ? 34 : 0,
        left: 0,
        right: 0,
      };
    }
    if (Platform.OS === 'android') {
      const statusBarHeight = Platform.Version >= 21 ? 24 : 0;
      return {
        top: statusBarHeight,
        bottom: isNotch ? 16 : 0,
        left: 0,
        right: 0,
      };
    }
    return { top: 0, bottom: 0, left: 0, right: 0 };
  },
  // 是否为全面屏
  isFullScreen() {
    const screenSize = this.getScreenSize();
    const aspectRatio = screenSize.height / screenSize.width;
    return aspectRatio > 1.8;
  },
};

样式适配

全局样式设置

// styles.ts
import { StyleSheet } from 'react-native';
import { DeviceUtils } from './deviceUtils';
const { top, bottom } = DeviceUtils.getSafeAreaInsets();
export const GlobalStyles = StyleSheet.create({
  // 安全区域容器
  safeContainer: {
    flex: 1,
    paddingTop: top,
    paddingBottom: bottom,
  },
  // 页面容器
  pageContainer: {
    flex: 1,
    backgroundColor: '#FFFFFF',
  },
  // 头部安全间距
  headerSafeTop: {
    height: top,
    backgroundColor: '#FFFFFF',
  },
  // 底部安全间距
  bottomSafeArea: {
    height: bottom,
    backgroundColor: '#FFFFFF',
  },
  // 列表底部间距
  listFooter: {
    height: bottom,
  },
  // 输入框底部间距(用于键盘弹出时)
  inputBottomMargin: {
    marginBottom: Platform.OS === 'ios' ? bottom + 10 : 10,
  },
  // 按钮底部固定
  fixedButtonContainer: {
    position: 'absolute',
    left: 16,
    right: 16,
    bottom: bottom + 16,
  },
});

使用示例

import React from 'react';
import { Page } from './Page';
import { Header } from './Header';
import { TabBar } from './TabBar';
const HomeScreen: React.FC = () => {
  return (
    <Page safeAreaTop safeAreaBottom scrollable={false}>
      <Header title="首页" />
      {/* 页面内容 */}
      <View style={{ flex: 1 }}>
        {/* 你的页面内容 */}
      </View>
      {/* 底部TabBar */}
      <TabBar
        items={[
          {
            label: '首页',
            icon: <HomeIcon />,
            onPress: () => {},
            active: true,
          },
          // ... 其他tab项
        ]}
      />
    </Page>
  );
};

注意事项

  1. 测试不同设备:在多种刘海屏设备上测试
  2. 横屏适配:考虑横屏时的安全区域
  3. 键盘处理:键盘弹出时需要调整布局
  4. 动态岛适配:对于iPhone 14 Pro及以上机型,需要考虑动态岛的交互
  5. Android多样:不同Android厂商的刘海实现不同,需要兼容处理

这个适配方案涵盖了刘海屏适配的主要场景,可以根据具体需求进行调整和扩展。

OpenClaw 刘海屏适配主要涉及状态栏、导航栏和安全区域的适配。以下是完整的适配方案-第1张图片-官方openclaw下载|openclaw官网-国内ai小龙虾下载

标签: OpenClaw 刘海屏适配

抱歉,评论功能暂时关闭!