2009-09-25 66 views
129

我目前正在開發的Android應用程序的主要活動已增長得相當大。這主要是因爲它包含一個帶有3個選項卡的TabWidget。每個選項卡都有相當多的組件。活動必須一次控制所有這些組件。所以我想你可以想象這個活動有20個字段(幾乎每個組件都有一個字段)。它還包含很多邏輯(點擊監聽器,填充列表的邏輯等)。Android - 編寫自定義(複合)組件

我通常在基於組件的框架中做的事情是將所有東西都拆分成自定義組件。每個自定義組件都會有明確的責任。它將包含它自己的一套組件以及與該組件相關的所有其他邏輯。

我試圖弄清楚如何做到這一點,並且我在Android文檔中發現了一些他們喜歡稱之爲「複合控制」的東西。 (請參閱http://developer.android.com/guide/topics/ui/custom-components.html#compound並滾動到「複合控件」部分)我想根據定義視圖結構的XML文件創建這樣的組件。

在本文檔中,它說:

注意,只是一個活動一樣, 您可以使用聲明 (基於XML的)方法來創建 包含的組件,或者你可以嵌套 他們編程從您的代碼。

嗯,那是個好消息!基於XML的方法正是我想要的!但它沒有說明如何去做,除了它像「一個活動」一樣......但是我在一個活動中所做的是呼叫setContentView(...)來從XML擴充視圖。如果您的子類爲LinearLayout,則此方法不可用。

所以我試圖手動充氣,這樣的XML:

public class MyCompoundComponent extends LinearLayout { 

    public MyCompoundComponent(Context context, AttributeSet attributeSet) { 
     super(context, attributeSet); 
     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     inflater.inflate(R.layout.my_layout, this); 
    } 
} 

這個工程,除了一個事實,即XML我載有聲明爲根元素LinearLayout。這導致膨脹的LinearLayoutMyCompoundComponent的孩子,其本身已經是LinearLayout !!所以現在我們在MyCompoundComponent和它實際需要的視圖之間有一個冗餘的LinearLayout。

有人可以請我提供一個更好的方法來解決這個問題,避免有一個多餘的LinearLayout實例嗎?

+13

我愛從我學到的東西的問題。謝謝。 – 2009-09-26 02:16:59

+4

我最近寫了一篇關於此的博客文章: http://blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3/ – 2009-10-12 09:04:09

回答

98

使用合併標記爲您的XML根

<merge xmlns:android="http://schemas.android.com/apk/res/android"> 
<!-- Your Layout --> 
</merge> 

Check this article.

+10

非常感謝!這正是我所期待的。令人驚訝的是,如此長的問題能有如此簡短的答案。優秀! – 2009-09-25 11:27:33

+0

如何在景觀中包含此合併佈局? – Kostadin 2012-07-10 07:02:56

+0

但是這意味着你不能使用可視化編輯器...... – Timmmm 2012-10-22 15:35:57

0

我認爲你應該做的方式,就是用你的類名作爲XML根元素:

<com.example.views.MyView xmlns:.... 
     android:orientation="vertical" etc.> 
    <TextView android:id="@+id/text" ... /> 
</com.example.views.MyView> 

然後讓您的課程從您想要使用的佈局派生。請注意,如果您正在使用此方法,則請勿使用此處使用佈局充氣器。

public class MyView extends LinearLayout 
{ 
    public ConversationListItem(Context context, AttributeSet attrs) 
    { 
     super(context, attrs); 
    } 
    public ConversationListItem(Context context, AttributeSet attrs, int defStyle) 
    { 
     super(context, attrs, defStyle); 
    } 


    public void setData(String text) 
    { 
     mTextView.setText(text); 
    } 

    private TextView mTextView; 

    @Override 
    protected void onFinishInflate() 
    { 
     super.onFinishInflate(); 

     mTextView = (TextView)findViewById(R.id.text); 
    } 
} 

然後,您可以像平常一樣在XML佈局中使用您的視圖。如果你想使視圖程序你必須自己誇大它:

MyView v = (MyView)inflater.inflate(R.layout.my_view, parent, false); 

不幸的是,這並不讓你做v = new MyView(context),因爲似乎沒有被周圍的嵌套佈局問題的一種方式,所以這個ISN真的不是一個完整的解決方案。你可以像這樣添加一個方法來MyView,使其更好一點:

public static MyView create(Context context) 
{ 
    return (MyView)LayoutInflater.fromContext(context).inflate(R.layout.my_view, null, false); 
} 

聲明:我可以講完全胡說八道。

+0

謝謝!我認爲這個答案也是正確的:)但三年前,當我問這個問題時,「合併」也做到了! – 2012-12-21 12:52:35

+8

然後有人過來試着在佈局的某個地方使用你的自定義視圖,只用''''和你的'setData'和'onFinishInflate'調用開始拋出NPE,你不知道爲什麼。 – 2013-03-09 01:37:13

+0

這裏的訣竅是使用您的自定義視圖,然後在構造函數中將使用合併標記的佈局充氣爲根。現在你可以在XML中使用它,或者只是通過新的方式來使用它。所有基地都被覆蓋,這正是問題/被接受的答案一起做的事情。然而,你不能做的是直接參考佈局。它現在由自定義控件「擁有」,只應在構造函數中使用它。但是如果你使用這種方法,你爲什麼還需要在其他任何地方使用它?你不會的。 – MarqueIV 2017-07-21 14:35:08