6

Android のビュー階層がどのように機能するかを理解しようとすることのみを目的としてアプリを作成しています。私は今、いくつかの本当に厳しい問題を抱えています。ここでの説明は簡潔にしようと思います。

設定:

現在、私は3つのビューを持っています。2 はViewGroupsで、1 はただのView. それらがこの順序であるとしましょう:

    TestA extends ViewGroup
    TestB extends ViewGroup
    TestC extends View
    TestA->TestB->TestC
    Where TestC is in TestB and TestB is in TestA.

Activityの場合、次のようにビューを表示するだけです。

TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(myView);

問題:

  1. onDraw(Canvas canvas)TestAのメソッドは呼び出されません。ビューに寸法がない (高さ/幅 = 0) というこれに対するいくつかの解決策を見てきましたが、そうではありません。をオーバーライドするonLayout()と、レイアウトの寸法が正しく表示されます。また、getHeight()/Width() は本来あるべき姿です。ベース ビューをオーバーライドdispatchDraw()して描画することもできます。

  2. でオブジェクトをアニメートしたいTestB。伝統的に、オブジェクトが本来のアニメーションを終了するまで、onDraw()呼び出し時にメソッドをオーバーライドしていました。invalidate()ただし、 では、ビューTestBを呼び出しても再描画されません。invalidate()メソッドを再度呼び出すのは親ビューの仕事であるという印象を受けていますonDraw()が、親ビューは再度呼び出しませんdispatchDraw()

私の質問は、onDraw()親ビューのメソッドが最初から呼び出されないのはなぜですか? 親ビューの子の 1 つがそれ自体を無効にするときに、親ビューのどのメソッドを呼び出す必要がありますか? 親は子供が描かれることを保証する責任がありますか、それともAndroidがそれを処理しますか? Android が に応答する場合、二度と描画されないinvalidate()のはなぜですか?TestB

4

2 に答える 2

5

わかりました、いくつかの調査と多くの試みの後、問題 2 に関して 3 つの間違ったことをしていたことがわかりました。その多くは単純な答えでしたが、あまり明白ではありませんでした。

  • onMeasure()ビューごとにオーバーライドする必要があります。内で、子が必要とする MeasureSpecの受け渡しに含まれるすべての子を呼び出すonMeasure()必要があります。measure()ViewGroup

  • 含めたいすべてaddView()の子供を呼び出す必要があります。もともと、私は単純にビュー オブジェクトを作成し、それを直接使用していました。これにより、一度描画することができましたが、ビューはビューツリーに含まれていなかったため、呼び出したときにビューツリーを無効にしたり、再描画したりしていませんでした。例えば:invalidate()

テストAでは:

 TestB childView;
 TestA(Context context){
   ****** setup code *******
   LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
   childView = new TestB(context);
   childView.setLayoutParams(params);
 }

 @Override
 protected void dispatchDraw(Canvas canvas){
   childView.draw(canvas);
 }

これにより、子ビューが一度描画されます。ただし、そのビューをアニメーションなどのために更新する必要がある場合は、それだけです。addView(childView)ビュー ツリーに追加するために、TestA のコンストラクターを挿入しました。最終的なコードは次のとおりです。

 TestB childView;
 TestA(Context context){
   ****** setup code *******
   LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
   childView = new TestB(context);
   childView.setLayoutParams(params);
   addView(childView);
 }

 @Override
 protected void dispatchDraw(Canvas canvas){
   childView.draw(canvas);
 }

または、描画する子がさらに多くある場合は、そのようにオーバーライドできますdispatchDraw(Canvas canvas)が、各描画の間にグリッド線などのカスタム要素が必要です。

@Override
protectd void dispatchDraw(Canvas canvas){
   int childCount = getChildCount();
   for(int i = 0; i < size; i++)
   {
       drawCustomElement();
       getChildAt(i).draw(canvas);
   }
}
  • オーバーライドする必要がありますonLayout()(とにかく抽象的でViewGroupあるため、とにかく必要です)。このメソッド内で、layoutすべての子を呼び出す必要があります。最初の 2 つのことを行った後でも、私の見解は無効にはなりません。これを行うとすぐに、すべてが完璧に機能しました。

更新: 問題 #1 は解決されました。別の非常に単純ですが、それほど明白ではない解決策です。

のインスタンスを作成するときはTestA、呼び出すsetWillNotDraw(false)必要があります。そうしないと、Android は最適化のためにインスタンスを描画しません。したがって、完全なセットアップは次のとおりです。

TestA myView = new TestA(context);
myView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
myView.setWillNotDraw(false);
setContentView(myView);
于 2011-09-16T14:57:08.463 に答える
2

これは直接的な答えではありませんが、ここにカスタム ビューを描画する方法に関する素晴らしいチュートリアルがあり、ビューをアニメーション化する方法について素晴らしいクラッシュ コースが提供されます。コードも非常にシンプルでクリーンです。

お役に立てれば!

于 2011-09-15T21:36:41.223 に答える