Я использую плоский список для создания сетки элементов, содержащих изображения, видео, блоги и аудио. В плоском списке есть две колонки. Я добавил кнопку для добавления фиктивных сообщений в плоский список. Проблема в высоте предметов. Когда я добавляю блог в плоский список, в нем есть текст, изображения и элемент рядом с ним. то есть изображение или видео также получают высоту элемента блога. Можно ли задать переменную высоту для каждого элемента в строке. При добавлении блога в плоский список элемент видео также имеет высоту, соответствующую элементу блога.
Строка Flatlist с элементами разного размера
Ответы (4)
Согласно официальному документу, когда в плоском списке несколько столбцов, Items should all be the same height - masonry layouts are not supported.
Но я думаю, вы можете попробовать добавить оболочку View
для каждого элемента столбца, чтобы избежать этого правила. height
плоского списка будет установлено на обертке, высота вашего элемента все еще может быть настроена вами.
Я использую эту библиотеку для создания масонского списка. https://github.com/AppAndFlow/react-native-masonry-list .Он работает так, как ожидалось.
Вы можете попробовать использовать ScrollView внутри FlatList с одним элементом.
import React, {useEffect, useState} from 'react';
import {
View,
FlatList,
Image,
ScrollView,
Text,
Dimensions,
} from 'react-native';
const {height, width} = Dimensions.get('window');
const DATA = [
{
url:
'https://images.pexels.com/photos/4689912/pexels-photo-4689912.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 400,
},
{
url:
'https://images.pexels.com/photos/2736843/pexels-photo-2736843.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 100,
},
{
url:
'https://images.pexels.com/photos/4407291/pexels-photo-4407291.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 100,
},
{
url:
'https://images.pexels.com/photos/4553021/pexels-photo-4553021.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4689912/pexels-photo-4689912.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/2736843/pexels-photo-2736843.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4407291/pexels-photo-4407291.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4553021/pexels-photo-4553021.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4689912/pexels-photo-4689912.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/2736843/pexels-photo-2736843.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4407291/pexels-photo-4407291.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4553021/pexels-photo-4553021.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4689912/pexels-photo-4689912.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/2736843/pexels-photo-2736843.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4407291/pexels-photo-4407291.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
{
url:
'https://images.pexels.com/photos/4553021/pexels-photo-4553021.jpeg?auto=compress&cs=tinysrgb&dpr=2&w=500',
height: 200,
},
];
const W = width / 2 - 10 - 4;
const Item = ({url, height}) => {
const [dims, setDims] = useState({height: 0, width: 0});
useEffect(() => {
setDims({height: Math.random() * 1000});
// Image.getSize(url, ([h, w]) => {
// console.log('<<h, w', h, w);
// });
}, [url]);
return (
<View
style={{
width: W,
height: dims.height,
margin: 5,
padding: 1,
backgroundColor: 'red',
}}>
{/* <Text>{JSON.stringify(dims, null, 2)}</Text> */}
<Image
source={{uri: url}}
style={{
height: '100%',
width: '100%',
}}
/>
</View>
);
};
export default () => {
const [data, setData] = useState([...DATA]);
// let prevY = React.useRef(0).current;
// const offsetY = new Animated.Value(0);
return (
<View
style={{
flex: 1,
backgroundColor: '#DDDDDD',
flexDirection: 'row',
}}>
<FlatList
data={[1]}
onEndReached={() => {
setData((prev) => {
return [...prev, ...DATA];
});
console.log('<<EENDDD');
}}
renderItem={() => {
return (
<ScrollView
contentContainerStyle={{
flexDirection: 'row',
backgroundColor: 'blue',
marginVertical: 10,
}}>
<View>
{data.map((item) => {
return <Item {...item} />;
})}
</View>
<View>
{data.map((item) => {
return <Item {...item} />;
})}
</View>
</ScrollView>
);
}}
/>
</View>
);
};
Интересно, что этого можно добиться без каких-либо внешних библиотек. Хитрость заключается в использовании отрицательной маржи.
Реализация немного сложна — нам нужно применить отрицательное поле в свойстве VirtualizedList CellRendererComponent, чтобы заставить его правильно работать на Android.
JSX:
<View style={styles.container}>
<FlatList
style={styles.flatlist}
data={data}
keyExtractor={(item, index) => index.toString()}
CellRendererComponent={({ children, item, ...props }) => {
return (
<View {...props} style={{ marginTop: item.marginTop }}>
{children}
</View>
)
}}
renderItem={({ item }) => {
const { source: source1, height: height1, marginTop: marginTop1 } = item.image1;
const { source: source2, height: height2, marginTop: marginTop2 } = item.image2;
return (
<View style={Style.viewRow}>
<Image source={source1} style={[styles.image, { height: height1, marginTop: marginTop1 }]} />
<Image source={source2} style={[styles.image, { height: height2, marginTop: marginTop2 }]} />
</View>
)
}}
/>
</View>
Данные:
const source = { uri: 'https://placekitten.com/160/300' };
const data = [
{
marginTop: 0,
image1: { source, height: 300, marginTop: 0 },
image2: { source, height: 250, marginTop: 0 }
},
{
marginTop: -50,
image1: { source, height: 290, marginTop: 50 },
image2: { source, height: 300, marginTop: 0 }
},
{
marginTop: -40,
image1: { source, height: 250, marginTop: 40 },
image2: { source, height: 350, marginTop: 0 }
}
];
Стили:
const styles = StyleSheet.create({
container: {
flex: 1
},
flatList: {
width: '100%',
height: '100%'
},
viewRow: {
flexDirection: 'row'
},
image: {
width: '50%',
resizeMode: 'cover'
}
});
Обязательно правильно расположите данные изображений в массиве — всегда размещайте более высокое изображение на более короткой стороне, вычисляйте разницу в высоте и т. д.