11

アクティビティからの構成変更を処理するときに、onLayout と onSizeChanged が横向きから縦向きまたは縦向きから横向きに変更された直後に2 回連続して呼び出されることに気付きました。さらに、最初の onLayout/onSizeChanged には古い寸法 (回転前) が含まれ、2 番目の 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 であることにも注意してください。これは、ビュー階層に追加されたばかりであることを示していますが、ランドスケープの寸法が正しくありません!

この動作を示すコードは次のとおりです。

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> 
4

1 に答える 1

8

私自身、これは長い間疑問に思っていました。

私が質問に答えた方法は、答えが依存していると信じているためtry/catch、メソッドに or logging ステートメントを 追加することrequestLayoutです。これにより、再測定や再レイアウトのリクエストがいつ、try/catch,誰によって行われたかを確認できます。

Android でのレイアウトのしくみは、ビューをダーティ レイアウトとしてマークすることですrequestLayout。一定の間隔で常に UI スレッドで実行される Android ルーパーは、将来の不確定な時点でダーティとしてマークされたツリー内のビューを再測定および再レイアウトします。

私は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

Android のドキュメントには、測定とレイアウトに関する詳細情報が記載されていますが、上記の詳細については残念ながら不足しています。

イベント処理とスレッド化

ビューの基本的なサイクルは次のとおりです。

  1. イベントが発生し、適切なビューにディスパッチされます。ビューはイベントを処理し、リスナーに通知します。
  2. イベントの処理中にビューの境界を変更する必要がある場合、ビューは requestLayout() を呼び出します。
  3. 同様に、イベントの処理中にビューの外観を変更する必要がある場合、ビューは invalidate() を呼び出します。
  4. requestLayout() または invalidate() が呼び出された場合、フレームワークはツリーの測定、レイアウト、および描画を適切に処理します。

注: ビュー ツリー全体がシングル スレッドです。ビューでメソッドを呼び出すときは、常に UI スレッド上にいる必要があります。他のスレッドで作業を行っていて、そのスレッドからビューの状態を更新したい場合は、ハンドラーを使用する必要があります。

于 2012-11-07T23:50:21.387 に答える