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

React Native中Grid替代方案:实现堆叠书籍样式筛选器

问题描述

我想创建一个堆叠书籍样式的筛选器,预期效果类似有声书App里的书籍堆叠筛选组件。尝试用CSS Grid布局实现,但React Native不兼容这种写法,无法正常工作,求可行的解决方案。

附上我的代码:

import { ScrollView, StyleSheet, Text, View, ImageBackground, Image, TouchableOpacity } from 'react-native'
import React, { useState } from 'react'
import LibraryBookCard from '../components/libraryBookCard'
import * as Font from 'expo-font';
import { globalStyles } from '../utils/GlobalStyles';

const LibraryScreen = () => {
  const [fontLoaded, setFontLoaded] = useState(false);

  const loadFonts = async () => {
    await Font.loadAsync({
      'Hensa': require('../assets/fonts/Hensa.ttf'),
    });
    setFontLoaded(true);
  };

  React.useEffect(() => {
    loadFonts();
  }, []);

  let dummyData = [
    {title: 'The Lighthouse', author: 'Jess Thompson', prompt: "Magic Waterfall", genre: 'Action'},
    {title: 'Growing Pains', author: 'Cassidy Hue', prompt: "Magic Bean", genre: 'Fantasy'},
    {title: 'Lovesick', author: 'Alexa Pea', prompt: "Tinder", genre: 'Romance'},
    {title: 'Masks and Muggles', author: 'Emma Haas', prompt: "Wizardry", genre: 'Fantasy'},
    {title: 'The Magician', author: 'Joshy B', prompt: "Thee Sword", genre: 'Fantasy'},
  ]
  const handleButtonPress = () => {
    console.log("pressed");
  }

  return (
    <ImageBackground
      source={require('../assets/bg/general.png')}
      style={styles.backgroundImage}
    >
      {fontLoaded && (
        <View>
          <Text style={styles.heading}>Library</Text>
          <Text style={styles.body}>Let’s find the perfect story for you.</Text>

          <View style={styles.buttonContainer}>
            <TouchableOpacity style={[styles.book, styles.book1]}>
            <Text style={styles.bookText}>Book 1</Text>
          </TouchableOpacity>

          <TouchableOpacity style={[styles.book, styles.book2]}>
            <Text style={styles.bookText}>Book 2</Text>
          </TouchableOpacity>

          <TouchableOpacity style={[styles.book, styles.book3]}>
            <Text style={styles.bookText}>Book 3</Text>
          </TouchableOpacity>

          
          <TouchableOpacity style={[styles.book, styles.book4]}>
            <Text style={styles.bookText}>Book 4</Text>
          </TouchableOpacity>

                      
          <TouchableOpacity style={[styles.book, styles.book5]}>
            <Text style={styles.bookText}>Book 5</Text>
          </TouchableOpacity>
          </View>

          <Text style={styles.subHeading}>My Collection</Text>
          <ScrollView style={styles.scrollView}>
            {dummyData.map((project, index) => (
              <LibraryBookCard key={index} data={project}/>
            ))}
          </ScrollView>
        </View>
      )}
    </ImageBackground>
  )
}

export default LibraryScreen

const styles = StyleSheet.create({
  backgroundImage: {
    ...StyleSheet.absoluteFillObject,
    paddingLeft: 30,
    paddingRight: 30,
    paddingBottom: 30,
    justifyContent: 'center',
    alignItems: 'center',
    resizeMode: 'cover',
  },
  heading: {
    fontFamily: 'Hensa',
    fontSize: 52,
    color: 'white',
    width: 350,
    paddingTop: 65,
    paddingLeft: 20,
    lineHeight: 50,
  },
  body: {
    color: 'white',
    width: 350,
    paddingLeft: 20,
    paddingTop: 10,
    paddingBottom: 40,
  },
  scrollView: {
    height: 100,
  },
  subHeading:{
    color: 'white',
    fontWeight: 600,
    fontSize: 18,
    paddingLeft: 20,

  },
  buttonCcontainer: {
    flex: 1,
    display: 'grid',
    gridTemplateColumns: 'repeat(5, 1fr)',
    gridTemplateRows: 'repeat(5, 1fr)',
    gridColumnGap: 0,
    gridRowGap: 0,
    alignItems: 'center',
    justifyContent: 'center',
  },
  book1: {
    gridArea: '1 / 1 / 2 / 4',
    backgroundColor: '#B68C4C'
  },
  book2: {
    gridArea: '1 / 4 / 2 / 6',
    backgroundColor: '#9F3824'
  },
  book3: {
    gridArea: '2 / 1 / 5 / 2',
    backgroundColor: '#9F3824'
  },
  book4: {
    gridArea: '4 / 2 / 5 / 6',
    backgroundColor: '#6B8DFF'
  },
  book5: {
    gridArea: '2 / 2 / 4 / 6',
    backgroundColor: '#B68C4C'
  },
});
解决方案

React Native 不支持 CSS Grid 的 grid-area、gridTemplateColumns 等属性,要实现这种堆叠书籍的布局,推荐用绝对定位或者嵌套 Flexbox 来模拟,以下是具体实现方案:

方案1:绝对定位实现堆叠布局

通过给每个书籍组件设置 position: absolute,调整 top、left、width、height 实现重叠效果,同时用 zIndex 控制层级:

修改你的 buttonContainer 和书籍样式:

const styles = StyleSheet.create({
  // ... 其他样式保持不变
  buttonContainer: {
    width: 350,
    height: 200, // 定义容器大小,按需调整
    position: 'relative', // 作为绝对定位的父容器
    marginBottom: 40,
    paddingLeft: 20,
  },
  book: {
    position: 'absolute',
    borderRadius: 8, // 添加圆角模拟书籍边角
    justifyContent: 'center',
    alignItems: 'center',
    padding: 10,
  },
  bookText: {
    color: 'white',
    fontWeight: 'bold',
  },
  book1: {
    top: 0,
    left: 0,
    width: 180,
    height: 60,
    backgroundColor: '#B68C4C',
    zIndex: 2,
  },
  book2: {
    top: 0,
    left: 190,
    width: 140,
    height: 60,
    backgroundColor: '#9F3824',
    zIndex: 2,
  },
  book3: {
    top: 70,
    left: 0,
    width: 60,
    height: 120,
    backgroundColor: '#9F3824',
    zIndex: 2,
  },
  book4: {
    top: 130,
    left: 70,
    width: 260,
    height: 60,
    backgroundColor: '#6B8DFF',
    zIndex: 2,
  },
  book5: {
    top: 70,
    left: 70,
    width: 260,
    height: 55,
    backgroundColor: '#B68C4C',
    zIndex: 3, // 让中间书籍在最上层
  },
});

方案2:嵌套 Flexbox 模拟网格布局

如果不想用绝对定位,可通过嵌套 Flex 容器拆分区域:

  1. 顶部横向 Flex 容器放 book1 和 book2
  2. 下半部分纵向 Flex 容器,左边放 book3,右边用纵向 Flex 放 book5 和 book4

代码示例:

// 修改 render 中的 buttonContainer 部分
<View style={styles.buttonContainer}>
  {/* 顶部行 */}
  <View style={styles.topRow}>
    <TouchableOpacity style={[styles.book, styles.book1]}>
      <Text style={styles.bookText}>Book 1</Text>
    </TouchableOpacity>
    <TouchableOpacity style={[styles.book, styles.book2]}>
      <Text style={styles.bookText}>Book 2</Text>
    </TouchableOpacity>
  </View>
  {/* 底部行 */}
  <View style={styles.bottomRow}>
    <TouchableOpacity style={[styles.book, styles.book3]}>
      <Text style={styles.bookText}>Book 3</Text>
    </TouchableOpacity>
    <View style={styles.rightColumn}>
      <TouchableOpacity style={[styles.book, styles.book5]}>
        <Text style={styles.bookText}>Book 5</Text>
      </TouchableOpacity>
      <TouchableOpacity style={[styles.book, styles.book4]}>
        <Text style={styles.bookText}>Book 4</Text>
      </TouchableOpacity>
    </View>
  </View>
</View>

// 对应的样式
const styles = StyleSheet.create({
  // ... 其他样式
  buttonContainer: {
    width: 350,
    paddingLeft: 20,
    gap: 10, // 上下行间距
    marginBottom: 40,
  },
  topRow: {
    flexDirection: 'row',
    gap: 10,
  },
  bottomRow: {
    flexDirection: 'row',
    gap: 10,
  },
  rightColumn: {
    flex: 1,
    gap: 5,
  },
  book: {
    borderRadius: 8,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 10,
  },
  bookText: {
    color: 'white',
    fontWeight: 'bold',
  },
  book1: {
    flex: 3, // 占3份宽度
    height: 60,
    backgroundColor: '#B68C4C',
  },
  book2: {
    flex: 2, // 占2份宽度
    height: 60,
    backgroundColor: '#9F3824',
  },
  book3: {
    width: 60,
    flex: 1, // 占满高度
    backgroundColor: '#9F3824',
  },
  book5: {
    flex: 1, // 占上半部分高度
    backgroundColor: '#B68C4C',
  },
  book4: {
    flex: 1, // 占下半部分高度
    backgroundColor: '#6B8DFF',
  },
});

注意事项

  • 绝对定位更灵活,适合复杂堆叠效果;Flexbox 更符合 React Native 布局习惯,维护性更好
  • 可根据设计稿调整 width、height、gap、borderRadius 等参数
  • 确保每个 TouchableOpacity 的交互区域不被完全遮挡,通过 zIndex 控制层级

内容的提问来源于stack exchange,提问作者Alexa Pettitt

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:32:22