20

私は困惑している問題に遭遇しており、誰かが私にいくつかの指針を与えてくれることを望んでいました.

カスタム ViewGroup (実際には RelativeLayout を含む FrameLayout) を使用してイベント カレンダーをレンダリングするアプリケーションに取り組んでいます。カレンダー内のイベントは、イベントの期間とそれを含むビューのサイズに応じたサイズのビューとして表されます。

含まれている FrameLayout のサイズが変更されたときに発生する問題が発生しています。現在の実装では、イベントを表すすべてのビューを削除し、新しいビューを追加して、FrameLayout の現在のサイズに基づいてサイズを計算しようとします。この作業は、FrameLayout でオーバーライドされる View の onSizeChanged() メソッドを介してトリガーされます。

ビューのサイズが変更されると、このコードが実行され、ビューが更新されますが、実際には画面にレンダリングされるものはありません... FrameLayout に含まれるビューは単に表示されません。Hierarchyviewer ツールでビューを読み込むと、それらはビュー ツリーの一部であり、概観の本来あるべき位置に概説されますが、表示されません。(ビューは FrameLayout の最初のレンダリングで表示されることに注意してください...ビューが消えるのは、サイズ変更後にのみです。)

サイズ変更中のイベントの順序は次のようになります。

onMeasure()
onMeasure()
onSizeChanged()
onLayout()

ビュー(onSizeChanged()内)をリセットした後にrequestLayout()を呼び出しても効果がないようです。ただし、requestLayout() を呼び出す前に遅延を発生させると、ビューが表示されます。スレッドを生成してスリープ状態にするか、単純に requestLayout() を呼び出すダミー ボタンを作成し、サイズ変更後にそれを押すか、または onSizeChanged() の最後に配置されたこの醜いハックでさえ、この遅延を引き起こすことができます。

post(new Runnable() {
    public void run() {
        requestLayout();
    }
});

このハックを使用すると、含まれているビューが表示され、イベントの順序は次のようになります。

onMeasure()
onMeasure()
onSizeChanged()
onLayout()
onMeasure()
onMeasure()
onLayout()

そのため、(ビュー ツリーが変更された後に) 2 回目の測定パスを強制すると、含まれているビューが本来あるべき姿で表示されるようになります。なぜ requestLayout() の呼び出しを遅らせると、これが起こるのかは謎です。

誰かが私が間違っていることについての指針を提供できますか?

一部のコードを見ずにこれを理解するのはやや難しいことを認識しているため、私の問題を示す小さなサンプル アプリケーションを作成し、Github で利用できるようにしました。

https://github.com/MichaelSims/ViewGroupResizeTest

上記のハックは、別のブランチにコミットされています。

https://github.com/MichaelSims/ViewGroupResizeTest/tree/post-runnable-hack

追加情報を提供できる場合は、お知らせください。事前に感謝します。

4

2 に答える 2

26

これはハックではなく、本来の動作方法です。onSizeChanged() はレイアウト パスの一部として呼び出され、レイアウト パス中に requestLayout() を呼び出すことはできません/すべきではありません。レイアウト パス中にビュー階層を変更したことを示すために、イベント キューに requestLayout() をポストするのは正しいことです。

于 2011-05-02T03:58:38.133 に答える
0

私は同じ問題に遭遇します。

私の onMeasure() は次のようなものでした:

@Override    
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ...
        //calculate new width and height here
        ...
        setMeasuredDimension(newMeasureSpecWidth, newMeasureSpecHeight);
    }

私の間違いは、onMeasure() の最初の行で super.onMeasure() を呼び出していたため、変更しようとしているサイズに基づいて ViewGroup の内側の子が計算されていたことです。

だから私の修正は次のようなことをしていました:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
    //calculate new width and height here
    ...
    int newMeasureSpecWidth = MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY);
    int newMeasureSpecHeight = MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY);
    super.onMeasure(newMeasureSpecWidth, newMeasureSpecHeight);
}

そのため、super.onMeasure() への呼び出しを使用して ViewGroup に新しいサイズを設定していましたが、新しいサイズをその子にも伝えています。

覚えておいてください: onMeasure() をオーバーライドするときの契約は、setMeasuredDimension() を呼び出す必要があるということです (メソッド自体を呼び出すか、super.onMeasure() を呼び出すことでこれを実現できます)。

于 2016-04-06T14:17:21.543 に答える