2011-09-15 19 views
6

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

セットアップ:

現在、私は3つのビューを持っています。 2はViewGroupsであり、1はちょうどViewである。私は単純にそうようなビューを表示私のActivity

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

::のは、彼らがこの順にしているとしましょう

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

問題:種皮の

  1. onDraw(Canvas canvas)方法は決してありませんと呼ばれる。私は、私のビューには次元(高さ/幅= 0)がないと言っているが、これは当てはまりません。 onLayout()を無効にすると、私のレイアウトの寸法が得られ、正しいものになります。また、getHeight()/ Width()は、そうであるように正確です。私はdispatchDraw()をオーバーライドして、自分自身を描くために自分のベースビューを取得することもできます。

  2. TestBでオブジェクトをアニメートしたいと思います。伝統的には、私はメソッドを呼び出すときにinvalidate()そのオブジェクトをオーバーライドします。しかし、TestBでは、invalidate()と呼ぶと、ビューは決して再描画されません。 onDraw()メソッドをもう一度呼び出すのは私の親ビューの仕事ですが、私の親ビューはdispatchDraw()を再度呼び出さないという印象を受けています。

私は私の質問は、なぜ私の親ビューの私のonDraw()方法で始まるために呼び出されることはありません飽きないだろうしている?推測私の親ビューのどのメソッドが、子どもの1人が無効になったときに呼び出されるはずですか?親はそれが子供が引き出されることを確実にする責任があるか、それともAndroidがそれを世話するのでしょうか? Androidがinvalidate()に応答すると、なぜ私のTestBは再び描画されませんか?

答えて

5

[OK]を、いくつかの研究と多くの試行の後、私は問題#2に関して3つのことを間違っていたことがわかった。その多くは簡単な回答でしたが、あまり明らかではありませんでした。

  • ビューごとにonMeasure()を上書きする必要があります。 onMeasure()の範囲内での場合はmeasure()とし、ViewGroupに含まれるすべての子がの場合、子が必要とするMeasureSpecを渡す必要があります。

  • の場合は、の子供に含めたい場合はaddView()に電話する必要があります。もともと、私は単にビュー・オブジェクトを作成し、それを直接使用しました。これにより、一度描画することができましたが、ビューツリーにビューが含まれていなかったので、invalidate()と呼んだときにビューツリーが無効になっていて、再描画されていませんでした。種皮に

:たとえば

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); 
    } 
} 
  • あなたは(それがとにかく必須ですので、それは、とにかくViewGroupに抽象的です)onLayout()をオーバーライドする必要があります。この方法では、すべての子に対してlayoutを呼び出す必要があります。最初の2つのことをしても、私の意見は無効にならないでしょう。私がこれをやろうとすると、すべてが完璧に機能しました。

UPDATE: 問題#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); 
+0

あなたのブログのビューグループのこのデモプロジェクトを共有できますか、それは非常に参考になります..おめでとうございます。 – user1201239

+0

私はXMLで指定してもchildViewを追加する必要がありますか? – user1201239

+0

すみません。私はブログやアップロードするものがありません。私は可能な限り役立つように努力します。あなたの最初の質問に答えるには、いいえ。 xmlを展開すると、その子は 'ViewGroup'に含まれます。最も早く検索できるのは 'onFinishInflate()'です。その後、あなたは選択肢があります。 'getChildAt(int index)'メソッドを介して子供を取得するか、 'findViewById(int id)'を使用してください。インデックスは、子がxmlに配置されている順序になります。私はどのようにインデックス作成が子供の中の子供と一緒に働くかわかりません。 – DeeV

2

これは直接的な回答ではありませんが、カスタムビューを描画する方法についてはhere is a fantastic tutorialで、ビューをアニメーション化する方法については大きなクラッシュがあります。コードは非常にシンプルでクリーンでもあります。

希望すると便利です。

+0

ありがとうございます。これは私の問題を解決することはできませんでしたが、それは解決につながるのに役立ちました。 – DeeV

関連する問題