React Native:获取元素的位置

Posted

技术标签:

【中文标题】React Native:获取元素的位置【英文标题】:React Native: Getting the position of an element 【发布时间】:2015-07-17 17:30:02 【问题描述】:

我正在设计一个带有 flexbox 的 Image 组件,使其位于屏幕中心,效果很好。现在我想要第二个Image 组件直接显示在第一个组件的顶部。第二张图片使用绝对定位。目前我只是在猜测像素以使其适合,但这当然不准确,而且维护工作量太大。

我非常期待与 jQuery 的 .offset() 等效的 React Native。有没有这样的事情,如果没有什么是实现这一目标的最佳方法?

【问题讨论】:

【参考方案1】:

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 )

【讨论】:

这种调用measure 的方式对我不起作用。必须通过元素句柄:const handle = findNodeHandle(this.refs.dropdown); UIManager.measure(handle, (x, y, ...) ...) setImmediate(...) 现在可以替换 setTimeout(..., 0) 。它具有相同的行为,但围绕您的调用时间提供了一些语义。 @tohster 第一次滚动移动到正确的位置。但在那之后,Y 偏移到页面值是不正确的。你知道为什么会这样吗? onLayout 是这样做的首选方式 这适用于安卓系统吗?我试图获得相对于父级的位置(fy)并获得 0。在 ios 上运行良好。【参考方案2】:

您可以使用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);
  
>

与using .measure() as shown in the accepted answer 相比,它的优点是您永远不必摆弄用setTimeout 推迟您的.measure() 调用以确保测量可用,但缺点是它没有给出您相对于整个页面偏移,仅相对于元素的父级偏移。

【讨论】:

您可以组合答案 - 在 onLayout 调用 measure() 中 这比玩超时要简洁得多!【参考方案3】:

我遇到了类似的问题,结合上面的答案解决了

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

【讨论】:

this.feedPost 变量从何而来?你在某处初始化它吗?当我运行我的应用程序时,我得到 undefined is not a function this.feedPost.measure 在返回部分的渲染方法中我有根视图:&lt;View onLayout (event) =&gt; this.handleLayoutChange(event) ref=view =&gt; this.feedPost = view; &gt; ....这个主视图是this.feedPost。 @jsparks feedPost 是类名:类 FeedPost 这是使用钩子时的效果,也是我发现获取页面或屏幕 x / y 位置的唯一方法 我认为我们不需要将事件传递给handleLayoutChange,因为它不使用它对吗?【参考方案4】:

我需要在 ListView 中找到一个元素的位置,并使用了这个类似于 .offset 的 sn-p:

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'

【讨论】:

请注意 - 就像接受的答案一样 - 您可能需要将对 UIManager.measureLayoutRelativeToParent 的调用包装在 setTimeout 调用中,以防止它在您从某个点执行此代码时出错组件在渲染之前的生命周期。另请注意,对于现代 React Native,您需要执行 import UIManager, findNodeHandler from 'react-native' 而不是此处显示的要求。 UIManagerfindNodeHandle 现在可以直接从“react-native”导入。即import UIManager, findNodeHandle from "react-native" 此方法使用现已弃用的 API。请使用event.target.measure,对于TypeScript 用户,请将您的引用设为View 类型以访问measure 方法。【参考方案5】:

如果您使用功能组件并且不想使用forwardRef 来衡量您的组件的绝对 布局,您可以从LayoutChangeEvent 中的onLayout 获得对其的引用回调。

这样就可以得到元素的绝对位置:

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

使用 React Native 0.63.3 测试。

【讨论】:

刚刚在 android 和 RN 0.63.3 上尝试过,它给出了错误的值(例如 x 和 y 的值为 0)。 @Żabojad 试试这个ref.current.measure((width, height, px, py, fx, fy) =&gt; console.log(fx, fy, width, height, px, py)【参考方案6】:

这似乎在最新版本的 React Native 中使用 refs 计算时发生了变化。

以这种方式声明引用。

  <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;我无法重现在 React Native 0.51 中使用 ._component 的需要(当前最新版本,在您发布此答案前 3 天发布)。不知道你从哪里得到的;对我来说,ref 回调的第一个参数一个带有.measure 方法的组件。 @MarkAmery 如果您使用Animated.View 而不是View,则需要它。【参考方案7】:

在 ref 参数对象上有一个 measureInWindow 属性,可以像这样使用:

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

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

【讨论】:

以上是关于React Native:获取元素的位置的主要内容,如果未能解决你的问题,请参考以下文章

react-native 获取组件的宽度和高度

在 React Native 中使用组件导入获取错误“元素类型无效预期字符串”

React Native End to End Tests with Detox:获取匹配元素的高度、宽度和其他属性

React中ref获取元素位置,并通过js滑动到对应位置

React-Native 获取位置历史

React Native MapBox 获取点击位置