android - 为什么 onLayout 和 onSizeChanged 在方向改变时被调用两次?

标签 android android-layout android-orientation android-configchanges

我注意到在处理来自 Activity 的配置更改时,onLayout 和 onSizeChanged 在方向更改后立即连续调用 两次,从横向->纵向或从纵向->横向.此外,第一个 onLayout/onSizeChanged 包含尺寸(旋转之前),而第二个 onLayout/onSizeChanged 包含(正确)尺寸。

有谁知道为什么,和/或如何解决这个问题?似乎屏幕尺寸的变化可能在配置更改后的相当长一段时间内发生 - 即,当 onConfigurationChanged 被调用时,尺寸在配置更改后立即不正确?

下面是代码的调试输出,显示了从纵向到横向旋转后的 onLayout/onSizeChanged 调用(请注意,设备是 540x960,因此横向宽度应为 960,而纵向宽度应为 540):

03-13 17:36:21.140: DEBUG/RotateTest(27765): onConfigurationChanged: LANDSCAPE
03-13 17:36:21.169: DEBUG/RotateTest(27765): onSizeChanged:540,884,0,0
03-13 17:36:21.189: DEBUG/RotateTest(27765): onLayout:true-0,0,540,884
03-13 17:36:21.239: DEBUG/RotateTest(27765): onSizeChanged:960,464,540,884
03-13 17:36:21.259: DEBUG/RotateTest(27765): onLayout:true-0,0,960,464

另请注意,第一个 onSizeChanged 的​​ oldwidth 和 oldheight 为 0,表示我们刚刚添加到 View 层次结构中 - 但横向尺寸错误!

下面是说明此行为的代码:

MyActivity.java

package com.example;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.widget.FrameLayout;

public class MyActivity extends Activity
{
    private static String TAG = "RotateTest";

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        Log.d(TAG, "onConfigurationChanged: " + (newConfig.orientation == 1 ? "PORTRAIT" : "LANDSCAPE"));
        super.onConfigurationChanged(newConfig);
        _setView();
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        Log.d(TAG, "onCreate");
        super.onCreate(savedInstanceState);
        _setView();
    }

    private void _setView() {
        MyHorizontalScrollView scrollView = new MyHorizontalScrollView(this, null);
        setContentView(scrollView);
    }
}

MyHorizo​​ntalScrollView.java

package com.example;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.HorizontalScrollView;

public class MyHorizontalScrollView extends HorizontalScrollView {

    private static String TAG = "RotateTest";

    public MyHorizontalScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        Log.d(TAG, "onLayout:" + String.format("%s-%d,%d,%d,%d", changed, l, t, r, b));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d(TAG, "onSizeChanged:" + String.format("%d,%d,%d,%d", w, h, oldw, oldh));
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example"
      android:versionCode="1"
      android:versionName="1.0"
        >

    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="9"/>

    <application android:label="@string/app_name"
            >
        <activity android:name="MyActivity"
                  android:label="@string/app_name"
                  android:configChanges="orientation"
                >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest> 

最佳答案

很长一段时间以来,我自己一直在想这个问题。

我回答这个问题的方式——因为我相信答案是,这取决于——是在 requestLayout 中添加一个 try/catch 或 logging 语句方法。这使您可以查看何时提出重新测量和重新布局的请求,如果是 try/catch,由谁提出。

Android 中的布局方式是使用 requestLayout 将 View 标记为脏布局。始终在某个时间间隔内在 UI 线程上运行的 Android 循环程序将在未来某个不确定的时间点重新测量和重新布局树中已标记为脏的 View 。

我冒昧地猜测 onConfigurationChanged,您会收到多个 requestLayout 调用,而循环程序会在它们中间的某个地方调用 onMeasure

这就是我的日志记录:

11-07 15:39:13.624: W/YARIAN(30006): requestLayout
11-07 15:39:13.632: W/YARIAN(30006): requestLayout
11-07 15:39:13.640: W/YARIAN(30006): requestLayout
11-07 15:39:13.647: W/YARIAN(30006): requestLayout
11-07 15:39:13.686: W/YARIAN(30006): requestLayout
11-07 15:39:13.718: W/YARIAN(30006): requestLayout
11-07 15:39:13.827: W/YARIAN(30006): requestLayout
11-07 15:39:14.108: W/YARIAN(30006): onLayout
11-07 15:39:14.155: W/YARIAN(30006): requestLayout
11-07 15:39:14.272: W/YARIAN(30006): onLayout

The Android documentation has more information关于测量和布局,但遗憾的是我上面描述的细节不足。

Event Handling and Threading

The basic cycle of a view is as follows:

  1. An event comes in and is dispatched to the appropriate view. The view handles the event and notifies any listeners.
  2. If in the course of processing the event, the view's bounds may need to be changed, the view will call requestLayout().
  3. Similarly, if in the course of processing the event the view's appearance may need to be changed, the view will call invalidate().
  4. If either requestLayout() or invalidate() were called, the framework will take care of measuring, laying out, and drawing the tree as appropriate.

Note: The entire view tree is single threaded. You must always be on the UI thread when calling any method on any view. If you are doing work on other threads and want to update the state of a view from that thread, you should use a Handler.

关于android - 为什么 onLayout 和 onSizeChanged 在方向改变时被调用两次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9696118/

相关文章:

android,如何在edittext中绘制虚线

android - 更改主题时仅更改颜色

android - 如何以编程方式打开和关闭 layout_behavior 行为?

android - 在 Activity 中添加两个 fragment ,一个包含可滚动 ListView ,另一个包含静态内容

java - 我的 XML 文件不显示任何内容

android - 自定义属性 - 方向变化

android - 锁定屏幕方向

android - 适用于 Android 应用程序的 Django channel API

Android动态RelativeLayout相互重叠

android - 使用蓝牙 socket 处理方向变化的最佳方法