2011-09-15 99 views
6

我正在編寫一個應用程序,其目的只是試圖瞭解Android中的視圖層次結構的工作原理。我現在有一些非常嚴酷的問題。我會盡量在我的解釋中簡潔一些。試圖瞭解視圖層次結構

設置:

目前我有三個意見。 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)方法是永遠調用。我已經看到這個說法的一些解決方案,我的觀點沒有任何尺寸(高度/寬度= 0),但是,情況並非如此。當我覆蓋onLayout()時,我得到了我的佈局的尺寸,它們是正確的。另外,getHeight()/ Width()與它們應該完全相同。我也可以覆蓋dispatchDraw()並獲得我的基本觀點來繪製自己。

  2. 我想動畫TestB中的對象。傳統上,我會在調用invalidate()時自己覆蓋onDraw()方法,直到對象完成本應該執行的動畫。但是,在TestB中,當我撥打invalidate()時,該視圖永遠不會重繪。我的印象是,我的父視圖的工作再次調用onDraw()方法,但我的父視圖不再調用dispatchDraw()

我想我的問題是,爲什麼就永遠也不會打電話給我我父視圖onDraw()方法來開始?當我的父視圖中的某個方法在其中一個孩子失效時應該調用什麼方法?父母是否負責確保孩子被吸引,或者Android是否負責照顧?如果Android響應invalidate(),爲什麼我的TestB永遠不會再被繪製?

回答

5

好吧,在經過一番研究和大量嘗試之後,我發現我在問題#2方面做了三件錯誤的事情。很多是簡單的答案,但不是很明顯。

  • 您需要覆蓋每個視圖的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); 
    } 
} 
  • 必須覆蓋onLayout()(它在ViewGroup抽象無論如何,所以它需要的是這樣)。在此方法中,您必須爲每個孩子撥打layout。即使做了前兩件事,我的觀點也不會失效。一旦我做到了這一點,一切工作都完美無缺。

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

你可以在你的博客中分享這個Viewgroup的演示項目嗎?或者它會是非常有用的地方..謝謝你的夥伴.. – 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