React Native:获取元素的位置

IT技术 javascript ios reactjs react-native
2021-01-15 05:54:54

我正在Image使用 flexbox设计一个组件,使其位于屏幕中央,效果很好。现在我希望第二个Image组件直接显示在第一个组件的顶部。第二张图片使用绝对定位。目前,我只是在猜测像素以使其适合,但这当然不准确,并且在可维护性方面付出了太多努力。

我几乎在寻找 jQuery 的 React Native 等价物.offset()有没有这样的事情,如果没有什么是实现这一目标的最佳方法?

6个回答

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 )
    
onLayout 是执行此操作的首选方式
2021-03-17 05:54:54
这种调用的方式measure对我不起作用。必须传递元素句柄:const handle = findNodeHandle(this.refs.dropdown); UIManager.measure(handle, (x, y, ...) ...)
2021-03-27 05:54:54
@tohster 第一次滚动到正确的位置。但在那之后,Y 偏移到页面值是不正确的。你知道为什么会这样吗?
2021-04-05 05:54:54
setImmediate(...) 现在存在以替换 setTimeout(..., 0) 。它具有相同的行为,但提供了一些围绕您的调用时间的语义。
2021-04-08 05:54:54
这在安卓上有效吗?我试图获得相对于父母的位置 (fy) 并获得 0。在 ios 上运行良好。
2021-04-09 05:54:54

您可以使用它们在可用的最早时刻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以确保测量可用,但缺点是它不会为您提供相对于整个页面,只有与元素的父元素相关的页面。

这比玩超时要简洁得多!
2021-03-21 05:54:54
您可以结合答案 - 在 onLayout 调用 measure()
2021-03-28 05:54:54

我有一个类似的问题并通过结合上面的答案解决了它

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
在返回部分的渲染方法中,我有根视图: <View onLayout {(event) => {this.handleLayoutChange(event) }} ref={view => { this.feedPost = view; }} > .... 这个主视图是 this.feedPost。
2021-03-18 05:54:54
我认为我们不需要将事件传递给 handleLayoutChange 因为它不使用它对吗?
2021-03-19 05:54:54
this.feedPost 变量从何而来?你在某处初始化它吗?当我运行我的应用程序时,我得到 undefined 不是函数 this.feedPost.measure
2021-03-24 05:54:54
@jsparks feedPost 是类名:class FeedPost
2021-03-30 05:54:54
这是使用钩子时的效果,也是我发现获取页面或屏幕 x / y 位置的唯一方法
2021-03-30 05:54:54

我需要在 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'我的组件上有一个

UIManagerfindNodeHandle现在可以从“反应天然的”直接导入。IE,import { UIManager, findNodeHandle} from "react-native"
2021-03-16 05:54:54
此方法使用现已弃用的 API。请使用event.target.measure, 并且对于 TypeScript 用户,请使您的 ref 成为View访问measure方法类型
2021-03-16 05:54:54
请注意 - 就像接受的答案一样 -如果您从组件生命周期中的某个时间点执行此代码,然后再渲染它,您可能需要将调用包装在调用UIManager.measureLayoutRelativeToParentsetTimeout以防止它出错。另请注意,对于现代 React Native,您需要执行的操作import { UIManager, findNodeHandler } from 'react-native'而不是此处显示的要求。
2021-03-24 05:54:54

如果您使用的功能部件,并且不希望使用forwardRef来衡量你的组件的绝对布局,你可以从它的一个引用LayoutChangeEventonLayout回调。

这样,您就可以获取元素绝对位置:

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

使用 React Native 0.63.3 测试。

@Żabojad 试试这个 ref.current.measure((width, height, px, py, fx, fy) => {console.log(fx, fy, width, height, px, py)}
2021-04-13 05:54:54
刚刚在 Android 和 RN 0.63.3 上尝试过,它给出了错误的值(例如,x 和 y 的值为 0)。
2021-04-14 05:54:54