Quantcast
Channel: Active questions tagged react-native+android - Stack Overflow
Viewing all articles
Browse latest Browse all 30356

How to set dynamic height for each row with react-native-swipe-list-view?

$
0
0

Description

I am working on a react-native project using expo SDK36.

I want to do a swipe left/right list view. I use react-native-swipe-list-view to achieve it.

So far everything worked perfectly, the default example uses a fixed height: 50 per row, while I want to set the height of each row dynamically.

Every attempt where a failure, note that I already use <SwipeListView recalculateHiddenLayout={true} />

This is bad for the UX, since the default line is having a small height: 50, it is nearly impossible to drag the line on iOS and android properly.

Reproduction

import React from 'react';
import { Dimensions, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';

// You can import from local files
import SwipeListView from './components/SwipeListView';

// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.paragraph}>
          Change code in the editor and watch it change on your phone! Save to get a shareable url.
        </Text>
        <Card>
          <SwipeListView 
            dimensions={Dimensions.get('window')}
            listViewData={Array(20).fill('').map((d, i) => ({ 
              ...d, 
              title: `Item ${i}`, 
              description: `This is a very long description for item number #${i}, 
                it should be so long that you cannot see all the content, 
                the issue is about fixing the dynamic height for each row` 
              }))
            }
            minHeight={200}
          />
        </Card>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
});

This is my components/SwipeListView.js

import React, { Component } from 'react';
import {
  Animated,
  Image,
  StyleSheet,
  TouchableOpacity,
  TouchableHighlight,
  View,
} from 'react-native';
import {
  Avatar,
  Button,
  Text,
  Title,
  Subheading,
  TouchableRipple,
  withTheme,
} from 'react-native-paper';
import { SwipeListView as SwipeListViewDefault } from 'react-native-swipe-list-view';

/* eslint-disable react/prop-types, react/destructuring-assignment, react/no-access-state-in-setstate  */
class SwipeListView extends Component {

  leftBtnRatio = 0.25;
  rightBtnRatio = 0.75;

  constructor(props) {
    super(props);
    this.state = {
      listType: 'FlatList',
      listViewData: props.listViewData
        .map((data, i) => ({ key: `${i}`, ...data })),
    };

    this.rowTranslateAnimatedValues = {};
    props.listViewData
      .forEach((data, i) => {
        this.rowTranslateAnimatedValues[`${i}`] = new Animated.Value(1);
      });
  }

  getStyles() {
    const { minHeight, theme } = this.props;
    const { colors } = theme;

    return StyleSheet.create({
      rowFrontContainer: {
        overflow: 'hidden',
      },
      rowFront: {
        alignItems: 'center',
        backgroundColor: colors.surface,
        borderBottomColor: colors.accent,
        borderBottomWidth: 1,
        justifyContent: 'center',
        minHeight: '100%',
        flex: 1,
      },
      rowBack: {
        alignItems: 'center',
        backgroundColor: colors.surface,
        flexDirection: 'row',
        justifyContent: 'space-between',
        paddingLeft: 15,
        minHeight: '100%',
      },
      backBtn: {
        alignItems: 'center',
        bottom: 0,
        justifyContent: 'center',
        position: 'absolute',
        top: 0,
      },
      backLeftBtn: {
        backgroundColor: colors.primary,
        left: 0,
        width: `${this.leftBtnRatio * 100}%`,
      },
      backRightBtn: {
        backgroundColor: colors.accent,
        right: 0,
        width: `${this.rightBtnRatio * 100}%`,
      },
    });
  }

  onRowDidOpen = (rowKey) => {
    console.log('This row opened', rowKey);
  };

  onSwipeValueChange = swipeData => {
    const { dimensions } = this.props;
    const { key, value } = swipeData;
    if (value < -dimensions.width * this.rightBtnRatio && !this.animationIsRunning) {
      this.animationIsRunning = true;
      Animated.timing(this.rowTranslateAnimatedValues[key], {
        toValue: 0,
        duration: 200,
      }).start(() => {
        const newData = [...this.state.listViewData];
        const prevIndex = this.state.listViewData.findIndex(item => item.key === key);
        newData.splice(prevIndex, 1);
        this.setState({listViewData: newData});
        this.animationIsRunning = false;
      });
    }
  };

  closeRow(rowMap, rowKey) {
    if (rowMap[rowKey]) {
      rowMap[rowKey].closeRow();
    }
  }

  deleteRow(rowMap, rowKey) {
    this.closeRow(rowMap, rowKey);
    const newData = [...this.state.listViewData];
    const prevIndex = this.state.listViewData.findIndex(
      (item) => item.key === rowKey,
    );
    newData.splice(prevIndex, 1);
    this.setState({ listViewData: newData });
  }

  render() {
    const { minHeight, dimensions, theme } = this.props;
    const { colors } = theme;
    const styles = this.getStyles();
    return (
      <SwipeListViewDefault
        data={this.state.listViewData}
        renderItem={data => (
          <Animated.View
            style={[styles.rowFrontContainer, {
              height: this.rowTranslateAnimatedValues[data.item.key].interpolate({
                inputRange: [0, 1],
                outputRange: [0, minHeight],
              })}]}
          >
            <TouchableRipple
              onPress={() => console.log('You touched me')}
              style={styles.rowFront}
              underlayColor={colors.background}
            >
              <View>
                <Title>{data.item.title}</Title>
                <Text>
                  {data.item.description}
                </Text>
              </View>
            </TouchableRipple>
          </Animated.View>
        )}
        renderHiddenItem={(data, rowMap) => (
          <View style={styles.rowBack}>

            <TouchableOpacity
              style={[
                styles.backLeftBtn,
                styles.backBtn,
              ]}
              onPress={() => this.closeRow(rowMap, data.item.key)}
            >
              <Text>Tap pour annuler</Text>
            </TouchableOpacity>
            <TouchableOpacity
              style={[
                styles.backRightBtn,
                styles.backBtn,
              ]}
              onPress={() => this.deleteRow(rowMap, data.item.key)}
            >
              <Animated.View
                style={[
                  styles.trash,
                  {
                    transform: [
                      {
                        scale: this.rowTranslateAnimatedValues[
                          data.item.key
                          ].interpolate({
                          inputRange: [
                            45,
                            90,
                          ],
                          outputRange: [0, 1],
                          extrapolate:
                            'clamp',
                        }),
                      },
                    ],
                  },
                ]}
              >
                <Text>Swipe left to delete</Text>
              </Animated.View>
            </TouchableOpacity>
          </View>
        )}
        leftOpenValue={dimensions.width * this.leftBtnRatio}
        rightOpenValue={-dimensions.width * this.rightBtnRatio}
        previewRowKey={'0'}
        previewOpenValue={-40}
        previewOpenDelay={3000}
        onRowDidOpen={this.onRowDidOpen}
        onSwipeValueChange={this.onSwipeValueChange}
        recalculateHiddenLayout={true}
      />
    );
  }
}

export default withTheme(SwipeListView);

Expect

I expect when using recalculateHiddenLayout={true}, to get the hidden row height calculated dynamically

Result Screenshots

On the web, I am able to set the height:

image

but I when using iOS and Android, the height is forced.

image

Environment

  • OS: ios/android/web
  • RN Version: expo SDK36

How can I set the height of each row dynamically?

Important edit

The problem is the fixed value here in the animation:

              height: this.rowTranslateAnimatedValues[data.item.key].interpolate({
                inputRange: [0, 1],
                outputRange: [0, 200], // <--- here
              })}]}

I have replaced it in the example with props.minHeight:

              height: this.rowTranslateAnimatedValues[data.item.key].interpolate({
                inputRange: [0, 1],
                outputRange: [0, this.props.minHeight],
              })}]}

It doesn't permit dynamic height, how can I get the row height dynamically?


Viewing all articles
Browse latest Browse all 30356

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>