Android-カスタム(複合)コンポーネントの作成


132

現在開発中のAndroidアプリのメインアクティビティは非常に大きくなっています。これは主に、TabWidgetタブが3つあるです。各タブにはかなりの数のコンポーネントがあります。アクティビティは、これらすべてのコンポーネントを一度に制御する必要があります。このアクティビティには20のフィールド(ほとんどすべてのコンポーネントのフィールド)があると想像できると思います。また、多くのロジック(クリックリスナー、リストを満たすためのロジックなど)も含まれています。

コンポーネントベースのフレームワークで私が通常行うことは、すべてをカスタムコンポーネントに分割することです。その場合、各カスタムコンポーネントには明確な責任があります。独自のコンポーネントセットと、そのコンポーネントに関連する他のすべてのロジックが含まれます。

私はこれをどのように行うことができるかを理解しようとしました、そして、彼らが「複合コントロール」と呼ぶのが好きであるものを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がルート要素として宣言されているという事実を除いて、これは機能します。その結果、膨らんだLinearLayout子供は、MyCompoundComponentそれ自体がすでにLinearLayout!! これで、冗長なLinearLayoutとその間MyCompoundComponentに実際に必要なビューができました。

誰かがこれにアプローチするより良い方法を私に提供して、冗長なLinearLayoutインスタンス化を避けることはできますか?


14
私は何かから学ぶ質問が大好きです。ありがとう。
ジェレミーローガン

5
私は最近これについてブログエントリを書きました:blog.jteam.nl/2009/10/08/exploring-the-world-of-android-part-3
Tom van Zummeren

回答:


101

使用マージあなたのXMLのルートとしてタグを

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

この記事を確認してください。


10
どうもありがとうございました!それがまさに私が探していたものです。こんなに長い質問がこんなに短い答えになるのは驚くべきことです。優れた!
トムヴァンズメレン

このマージレイアウトを横向きに含めるとどうなりますか?
Kostadin 2012

2
@Timmmm haha​​hahaビジュアルエディターが存在するずっと前からこの質問をしました:)
Tom van Zummeren

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)、ネストされたレイアウトの問題を回避する方法がないように思われるため、これを行うことはできません。したがって、これは実際には完全な解決策ではありません。次のようなメソッドを追加しMyViewて、少し見栄えを良くすることができます。

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

免責事項:私は完全に一斉に話しているかもしれません。


ありがとう!この答えも正しいと思います:)しかし、3年前に私がこの質問をしたとき、「マージ」もトリックを行いました!
トムファンズメレン、

8
そして、誰かがやって来て、あなたのカスタムビューをどこかでレイアウトで使用しよう<com.example.views.MyView />とするsetDataと、あなたのonFinishInflate呼び出しがNPEをスローし始めます、そしてあなたは理由がわかりません。
クリストファーペリー

ここでの秘訣は、カスタムビューを使用してから、コンストラクターで、マージタグをルートとして使用するレイアウトをインフレートすることです。これで、XMLで使用することも、新しくするだけで使用することもできます。すべてのベースがカバーされます。これは、質問/受け入れられた回答が一緒に行うこととまったく同じです。ただし、レイアウトを直接参照することはできません。これは、カスタムコントロールによって「所有」され、コンストラクター内でのみ使用する必要があります。しかし、このアプローチを使用している場合、なぜとにかくそれを他の場所で使用する必要があるのでしょうか。あなたはしません。
マークA.ドノホー2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.