React Native: получение позиции элемента

Я укладываю Image компонент с flexbox должен находиться в центре экрана, который работает довольно хорошо. Теперь я хочу второй Image компонент, который будет отображаться непосредственно в верхней части первого. Второе изображение использует абсолютное позиционирование. В настоящее время я просто угадываю пиксели, чтобы они соответствовали, но, конечно, это не является точным и слишком много усилий по обслуживанию.

Я в значительной степени ищу React Native эквивалент jQuery's .offset(), Есть ли такая вещь, и если нет, то какой лучший способ достичь этого?

8 ответов

React Native обеспечивает .measure(...) метод, который принимает обратный вызов и вызывает его со смещением и шириной / высотой компонента:

myComponent.measure( (fx, fy, width, height, px, py) => {

    console.log('Component width is: ' + width)
    console.log('Component height is: ' + height)
    console.log('X offset to frame: ' + fx)
    console.log('Y offset to frame: ' + fy)
    console.log('X offset to page: ' + px)
    console.log('Y offset to page: ' + py)
})

Пример...

Следующее вычисляет макет пользовательского компонента после его визуализации:

class MyComponent extends React.Component {
    render() {
        return <View ref={view => { this.myComponent = view; }} />
    }
    componentDidMount() {
        // Print component dimensions to console
        this.myComponent.measure( (fx, fy, width, height, px, py) => {
            console.log('Component width is: ' + width)
            console.log('Component height is: ' + height)
            console.log('X offset to frame: ' + fx)
            console.log('Y offset to frame: ' + fy)
            console.log('X offset to page: ' + px)
            console.log('Y offset to page: ' + py)
        })        
    }
}

Заметки об ошибках

  • Обратите внимание, что иногда компонент не завершает рендеринг раньше componentDidMount() называется. Если вы получаете нули в результате measure(...) затем завернуть в setTimeout должен решить проблему, а именно:

    setTimeout( myComponent.measure(...), 0 )
    

Ты можешь использовать onLayoutчтобы получить ширину, высоту иотносительную к родительской позиции компонента в самый ранний момент, когда они доступны:

<View
  onLayout={event => {
    const layout = event.nativeEvent.layout;
    console.log('height:', layout.height);
    console.log('width:', layout.width);
    console.log('x:', layout.x);
    console.log('y:', layout.y);
  }}
>

По сравнению с использованием.measure() как показано в принятом ответе, это имеет то преимущество, что вам никогда не придется возиться с отсрочкой .measure() звонки с setTimeout чтобы убедиться, что измерения доступны, но недостатком является то, что они не дают смещений относительно всей страницы, а только смещений относительно родительского элемента.

У меня была похожая проблема, и я решил ее, объединив ответы выше.

class FeedPost extends React.Component {
  constructor(props) {
    ...
    this.handleLayoutChange = this.handleLayoutChange.bind(this);
  }


handleLayoutChange() {
    this.feedPost.measure( (fx, fy, width, height, px, py) => {
      console.log('Component width is: ' + width)
      console.log('Component height is: ' + height)
      console.log('X offset to page: ' + px)
      console.log('Y offset to page: ' + py)
    })
  }

  render {
    return(
      <View onLayout={(event) => {this.handleLayoutChange(event) }} 
      ref={view => { this.feedPost = view; }} >
...

Теперь я могу видеть положение моего элемента feedPost в журналах:

08-24 11:15:36.838  3727 27838 I ReactNativeJS: Component width is: 156
08-24 11:15:36.838  3727 27838 I ReactNativeJS: Component height is: 206
08-24 11:15:36.838  3727 27838 I ReactNativeJS: X offset to page: 188
08-24 11:15:36.838  3727 27838 I ReactNativeJS: Y offset to page: 870

Мне нужно было найти положение элемента внутри ListView и использовать этот фрагмент, который работает вроде .offset:

const UIManager = require('NativeModules').UIManager;
const handle = React.findNodeHandle(this.refs.myElement);
UIManager.measureLayoutRelativeToParent(
  handle, 
  (e) => {console.error(e)}, 
  (x, y, w, h) => {
    console.log('offset', x, y, w, h);
  });

Это предполагает, что у меня был ref='myElement' на моем компоненте.

Если вы используете функциональные компоненты и не хотите использовать forwardRefчтобы измерить абсолютную компоновку вашего компонента, вы можете получить ссылку на него из LayoutChangeEvent в onLayout Перезвони.

Таким образом, вы можете получить абсолютную позицию элемента:

<MyFunctionComp
  onLayout={(event) => {
    event.target.measure(
      (x, y, width, height, pageX, pageX) => {
        doSomethingWithAbsolutePosition({
          x: x + pageX, 
          y: y + pageY,
        });
      },
    );
  }}
/>

Протестировано с React Native 0.63.3.

В объекте аргумента ref есть свойство measureInWindow, которое можно использовать следующим образом:

      const [offset, setOffset] = React.useState();

<View ref={(view) =>
    if(!view) return;
    view.measureInWindow((x, y) => {
        setOffset({ x, y });
    })
}>
</View>

Похоже, что это изменилось в последней версии React Native при использовании ссылок для расчета.

Объявите ссылки таким образом.

  <View
    ref={(image) => {
    this._image = image
  }}>

И найдите ценность таким образом.

  _measure = () => {
    this._image._component.measure((width, height, px, py, fx, fy) => {
      const location = {
        fx: fx,
        fy: fy,
        px: px,
        py: py,
        width: width,
        height: height
      }
      console.log(location)
    })
  }

У меня сработало следующее.

  1. Получить позицию на макете.
      // ...
const [position, setPosition] = useState({x: 0, y: 0});
// ...
return (
  // ...
  <View
    onLayout={event => {
      event.target.measure((x, y, width, height, pageX, pageY) => {
        setPosition({x: x + pageX, y: y + pageY});
      });
    }}
  />
  // ...
);
  1. Получить позицию по вызову.
      const ref = useRef(null);
// ...
const [position, setPosition] = useState({x: 0, y: 0});
// ...
return (
  // ...
  <TouchableOpacity
    ref={ref}
    activeOpacity={0.75}
    onPress={() => {
      ref.current.measure((x, y, width, height, pageX, pageY) => {
        setPosition({x: x + pageX, y: y + pageY});
      });
    }}
  />
  // ...
);
Другие вопросы по тегам