如何检测布局调整大小?

发布于 2024-10-01 19:03:14 字数 410 浏览 2 评论 0原文

Dianne Hackborn 在几个线程中提到,您可以检测布局何时调整大小,例如,何时打开或关闭软键盘。这样的线程就是这个... http://groups.google.com/group /android-developers/browse_thread/thread/d318901586313204/2b2c2c7d4bb04e1b

但是,我不明白她的回答:“通过使用所有相应的布局遍历和回调来调整视图层次结构的大小。”

有谁有关于如何检测这一点的进一步描述或一些示例?我可以链接到哪些回调来检测这一点?

谢谢

Dianne Hackborn mentioned in a couple threads that you can detect when a layout as been resized, for example, when the soft keyboard opens or closes. Such a thread is this one... http://groups.google.com/group/android-developers/browse_thread/thread/d318901586313204/2b2c2c7d4bb04e1b

However, I didn't understand her answer: "By your view hierarchy being resized with all of the corresponding layout traversal and callbacks."

Does anyone have a further description or some examples of how to detect this? Which callbacks can I link into in order to detect this?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

惟欲睡 2024-10-08 19:03:14

一种方法是 View。addOnLayoutChangeListener< /a>.在这种情况下,不需要对视图进行子类化。但您确实需要 API 级别 11。并且根据边界正确计算大小(API 中未记录)有时可能是一个陷阱。这是一个正确的示例:

view.addOnLayoutChangeListener( new View.OnLayoutChangeListener()
{
    public void onLayoutChange( View v,
      int left,    int top,    int right,    int bottom,
      int leftWas, int topWas, int rightWas, int bottomWas )
    {
        int widthWas = rightWas - leftWas; // Right exclusive, left inclusive
        if( v.getWidth() != widthWas )
        {
            // Width has changed
        }
        int heightWas = bottomWas - topWas; // Bottom exclusive, top inclusive
        if( v.getHeight() != heightWas )
        {
            // Height has changed
        }
    }
});

另一种方法(如 dacwe 的答案)是对视图进行子类化并覆盖 onSizeChanged

One way is View.addOnLayoutChangeListener. There's no need to subclass the view in this case. But you do need API level 11. And the correct calculation of size from bounds (undocumented in the API) can sometimes be a pitfall. Here's a correct example:

view.addOnLayoutChangeListener( new View.OnLayoutChangeListener()
{
    public void onLayoutChange( View v,
      int left,    int top,    int right,    int bottom,
      int leftWas, int topWas, int rightWas, int bottomWas )
    {
        int widthWas = rightWas - leftWas; // Right exclusive, left inclusive
        if( v.getWidth() != widthWas )
        {
            // Width has changed
        }
        int heightWas = bottomWas - topWas; // Bottom exclusive, top inclusive
        if( v.getHeight() != heightWas )
        {
            // Height has changed
        }
    }
});

Another way (as dacwe answers) is to subclass your view and override onSizeChanged.

最笨的告白 2024-10-08 19:03:14

对于 Kotlin 扩展:

inline fun View?.onSizeChange(crossinline runnable: () -> Unit) = this?.apply {
    addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
        val rect = Rect(left, top, right, bottom)
        val oldRect = Rect(oldLeft, oldTop, oldRight, oldBottom)
        if (rect.width() != oldRect.width() || rect.height() != oldRect.height()) {
            runnable();
        }
    }
}

使用如下:

myView.onSizeChange { 
     // Do your thing...
}

With Kotlin extensions:

inline fun View?.onSizeChange(crossinline runnable: () -> Unit) = this?.apply {
    addOnLayoutChangeListener { _, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
        val rect = Rect(left, top, right, bottom)
        val oldRect = Rect(oldLeft, oldTop, oldRight, oldBottom)
        if (rect.width() != oldRect.width() || rect.height() != oldRect.height()) {
            runnable();
        }
    }
}

Use thus:

myView.onSizeChange { 
     // Do your thing...
}
许一世地老天荒 2024-10-08 19:03:14

我的解决方案是在布局/片段的末尾添加一个不可见的微小哑视图(或将其添加为背景),因此布局大小的任何更改都将触发该视图的布局更改事件,该事件可以被捕获通过 OnLayoutChangeListener:

将哑视图添加到布局末尾的示例:

 <View 
    android:id="@+id/theDumbViewId"
    android:layout_width="1dp"
    android:layout_height="1dp"
     />

监听事件:

    View dumbView = mainView.findViewById(R.id.theDumbViewId);
    dumbView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            // Your code about size changed
        }
    });

My solution is to add an invisible tiny dumb view at the end of of the layout / fragment (or add it as a background), thus any change on size of the layout will trigger the layout change event for that view which could be catched up by OnLayoutChangeListener:

Example of adding the dumb view to the end of the layout:

 <View 
    android:id="@+id/theDumbViewId"
    android:layout_width="1dp"
    android:layout_height="1dp"
     />

Listen the event:

    View dumbView = mainView.findViewById(R.id.theDumbViewId);
    dumbView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            // Your code about size changed
        }
    });
网名女生简单气质 2024-10-08 19:03:14

其他答案是正确的,但他们没有删除 OnLayoutChangeListener。所以,每次调用这个监听器时,它都会添加一个新的监听器,然后多次调用它。

private fun View.onSizeChange(callback: () -> Unit) {
    addOnLayoutChangeListener(object : OnLayoutChangeListener {
        override fun onLayoutChange(
            view: View?,
            left: Int,
            top: Int,
            right: Int,
            bottom: Int,
            oldLeft: Int,
            oldTop: Int,
            oldRight: Int,
            oldBottom: Int,
        ) {
            view?.removeOnLayoutChangeListener(this)
            if (right - left != oldRight - oldLeft || bottom - top != oldBottom - oldTop) {
                callback()
            }
        }
    })
}

Other answers are correct, but they didn't remove OnLayoutChangeListener. So, every time this listener was called, it added a new listener and then called it many times.

private fun View.onSizeChange(callback: () -> Unit) {
    addOnLayoutChangeListener(object : OnLayoutChangeListener {
        override fun onLayoutChange(
            view: View?,
            left: Int,
            top: Int,
            right: Int,
            bottom: Int,
            oldLeft: Int,
            oldTop: Int,
            oldRight: Int,
            oldBottom: Int,
        ) {
            view?.removeOnLayoutChangeListener(this)
            if (right - left != oldRight - oldLeft || bottom - top != oldBottom - oldTop) {
                callback()
            }
        }
    })
}
内心激荡 2024-10-08 19:03:14

感谢https://stackoverflow.com/users/2402790/michael-allan这是一个很好而且简单的方法如果您不想覆盖您的所有观点。
随着 Api 的发展,我建议改为复制粘贴:

    String TAG="toto";
    ///and last listen for size changed
    YourView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v,
                                   int left, int top, int right, int bottom,
                                   int oldLeft, int oldTop, int oldRight, int oldBottom) {

           boolean widthChanged = (right-left) != (oldRight-oldLeft);
            if( widthChanged )
            {
                // width has changed
                Log.e(TAG,"Width has changed new width is "+(right-left)+"px");
            }
            boolean heightChanged = (bottom-top) != (oldBottom-oldTop);
            if( heightChanged)
            {
                // height has changed
                Log.e(TAG,"height has changed new height is "+(bottom-top)+"px");
            }
        }
    });

Thank to https://stackoverflow.com/users/2402790/michael-allan this is the good and simple way if you don't want to override all your views.
As Api has evolved I would suggest this copy paste instead:

    String TAG="toto";
    ///and last listen for size changed
    YourView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v,
                                   int left, int top, int right, int bottom,
                                   int oldLeft, int oldTop, int oldRight, int oldBottom) {

           boolean widthChanged = (right-left) != (oldRight-oldLeft);
            if( widthChanged )
            {
                // width has changed
                Log.e(TAG,"Width has changed new width is "+(right-left)+"px");
            }
            boolean heightChanged = (bottom-top) != (oldBottom-oldTop);
            if( heightChanged)
            {
                // height has changed
                Log.e(TAG,"height has changed new height is "+(bottom-top)+"px");
            }
        }
    });
烟火散人牵绊 2024-10-08 19:03:14
View.doOnLayout(crossinline action: (view: View) -> Unit): Unit

请参阅文档。

布置此视图时执行给定的操作。如果视图已经布局并且没有请求布局,则该操作将立即执行,否则该操作将在视图下次布局后执行。

该操作只会在下一个布局上调用一次,然后删除。

View.doOnLayout(crossinline action: (view: View) -> Unit): Unit

See docs.

Performs the given action when this view is laid out. If the view has been laid out and it has not requested a layout, the action will be performed straight away, otherwise the action will be performed after the view is next laid out.

The action will only be invoked once on the next layout and then removed.

说谎友 2024-10-08 19:03:14

Kotlin 版本基于 @Michael Allan 的回答来检测布局大小变化

binding.root.addOnLayoutChangeListener { view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
    if(view.height != oldBottom - oldTop) {
        // height changed
    }
    if(view.width != oldRight - oldLeft) {
        // width changed
    }
}

Kotlin version base on @Michael Allan answer to detect layout size change

binding.root.addOnLayoutChangeListener { view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
    if(view.height != oldBottom - oldTop) {
        // height changed
    }
    if(view.width != oldRight - oldLeft) {
        // width changed
    }
}
掀纱窥君容 2024-10-08 19:03:14

如果您像我一样来到此页面,并且

  1. addOnLayoutChangeListener 为您提供了所需的功能,但导致了无限的“requestLayout() 不正确调用....运行第二个布局传递”消息列表来填充您的 logcat 并...
  2. 添加removeOnLayoutChangeListener(尽管修复了重复的 logcat 消息,但这)破坏了您在 1)中拥有的功能,那么这可能会起作用。

它综合了斯莱恩和托尼的建议。

  1. 使用抽象方法创建ResetDetectorCallback接口
    调整大小操作。

  2. 使用 ResetDetectorCallback 的实例(setResetDetectorCallback 方法)创建一个简单的自定义视图 ResetDetector,并重写其 onSizeChange() 方法以调用 resetDetectorCallback.resizeAction()。

  3. 在布局中放置一个
    ResetDetectorView 的实例,其 id、width="match_parent"、height="match_parent",在我的例子中,我还对其进行了约束。

  4. 然后在代码中(我的是一个 Fragment,因为我采用了 Single Activity 方法)让该类实现 ResetDetectorCallback,从该类的布局中设置 ResetDetectorView 以使用“this”,并在该类中实现 resizeAction。

就我而言,它现在终于按照我想要的方式工作了。在桌面模拟器上,我可以根据需要调整该窗口的大小,并且它似乎可以根据我想要的方式准确地调整布局,并且 logcat 不会因“错误调用 requestLayout() .... 运行第二个布局传递”而过载”消息。

可能有更好的方法,也许使用 ViewModels 和 mutableStateObservers,但我对这些还不够熟悉。我希望这对某个地方的人有帮助。感谢上述所有贡献者,即使您的技术对我不起作用。

If your like me and you came to this page and

  1. addOnLayoutChangeListener gave you functionality you needed but caused an infinite list of "requestLayout() improperly called .... running second layout pass" messages to fill your logcat and ...
  2. Adding removingOnLayoutChangeListener(this) broke the functionality you had in 1) despite fixing the repeated logcat message then this might work.

Its a mix of what Slion and Tony suggested.

  1. Create a ResetDetectorCallback interface with abstract method
    resizeAction.

  2. Create a simple custom view ResetDetector with an instance of the ResetDetectorCallback, a setResetDetectorCallback mehtod, and override its onSizeChange() method to invok the resetDectectorCallback.resizeAction().

  3. In the layout place an
    instance of the ResetDetectorView with an id, width="match_parent", height="match_parent", and in my case I also constrained it.

  4. Then in the code (mine was a Fragment since I'm adopting the Single Activity methodology) let that class implement the ResetDetectorCallback, set the resetDetectorView from that class's layout to use "this", and implement the resizeAction in that class.

And in my case its finally working the way I want now. On a Desktop emulator I can resize that Window as much as I want and it appears tobe accurately adjusting the layout accordingly to the way I want and the logcat is not being overloaded with "requestLayout() improperly called .... running second layout pass" messages.

There is probably a better way, perhaps with ViewModels and mutableStateObservers, but I'm not familiar enough with those yet. I hope this helps someone somewhere. And thanks to all the contributors above even if your technique didn't work for me.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文