React Native: Отримання позиції елемента


79

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

Я в значній мірі шукаю React Native еквівалент jQuery .offset(). Чи є така річ, і якщо немає, який найкращий спосіб цього досягти?

Відповіді:


103

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(...), тоді обгортання його в a setTimeoutмає вирішити проблему, тобто:

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

4
Цей спосіб виклику у measureмене не спрацював. Довелося пройти дескриптор елемента:const handle = findNodeHandle(this.refs.dropdown); UIManager.measure(handle, (x, y, ...) ...)
Брок

setImmediate (...) тепер існує для заміни setTimeout (..., 0). Він має однакову поведінку, але забезпечує певну семантику щодо часу виклику.
JesusTheHun

@tohster Перший раз прокрутка рухається у правильне положення. Але після цього зміщення Y до значення сторінки є неправильним. Чи знаєте ви, чому це відбувається?
Баласубраманіан,

onLayout є найкращим способом для цього
hakazvaka

Це працює на android? Я намагався отримати відношення до батьківської позиції (fy) і отримати 0. На ios працює добре.
Віктор Моліна

60

Ви можете використовувати, 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щоб переконатися, що вимірювання доступні, але недоліком є ​​те, що це не дає вам зсувів щодо цілу сторінку, лише одну щодо батьківського елемента.


17
Ви можете поєднати відповіді - у мірі виклику onLayout ()
Juozas Kontvainis

Це набагато лаконічніше, ніж грати з тайм-аутами!
Нічний

22

Мені потрібно було знайти позицію елемента всередині 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'компонент.


3
Зауважте, що, як і прийнята відповідь, можливо, вам доведеться обернути виклик UIManager.measureLayoutRelativeToParentу setTimeoutдзвінок, щоб запобігти помилковому виклику, якщо ви виконуєте цей код із точки життєвого циклу компонента до того, як він був відтворений. Зауважте також, що для сучасного React Native ви хочете зробити, import { UIManager, findNodeHandler } from 'react-native'а не вимоги, наведені тут.
Mark Amery

1
UIManagerі findNodeHandleтепер їх можна імпортувати безпосередньо з "рідної реакції". тобто,import { UIManager, findNodeHandle} from "react-native"
HBSKan

20

Я мав подібну проблему і вирішив її, об’єднавши відповіді вище

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

1
Звідки походить змінна this.feedPost? Ви десь його ініціалізуєте? Я отримую undefined не є функцією this.feedPost.measure негайно, коли я запускаю свій додаток
jsparks

у методі візуалізації у частині повернення у мене є кореневий вигляд: <View onLayout {(event) => {this.handleLayoutChange(event) }} ref={view => { this.feedPost = view; }} > .... Цей основний вигляд - це this.feedPost.
Mislavoo7,

@jsparks feedPost - це назва класу: class FeedPost
Rickard

Це ефект при використанні гачків і єдиний спосіб, який я знайшов, щоб отримати позицію сторінки чи екрана x / y
Kerkness,

3

Здається, це змінилося в останній версії 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
-1; Я не можу довести необхідність використання ._componentу React Native 0.51 (остання версія, випущена за 3 дні до того, як ви опублікували цю відповідь). Не впевнений, звідки ви це берете; для мене перший аргумент refзворотного виклику - це компонент із .measureметодом.
Mark Amery

2
@MarkAmery вам це потрібно, якщо ви використовуєте Animated.Viewзамість View, наприклад, наприклад.
elzi

1

Якщо ви використовуєте функціональні компоненти і не хочете використовувати a 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.