web-dev-qa-db-fra.com

Android - Écriture d'un composant personnalisé (composé)

L'application Android que je suis en train de développer a une activité principale qui a pris de l'ampleur. C'est principalement parce qu'elle contient un TabWidget avec 3 onglets. Chaque onglet en a plusieurs composants. L'activité doit contrôler tous ces composants à la fois. Je pense donc que vous pouvez imaginer que cette activité a comme 20 champs (un champ pour presque tous les composants). Elle contient également beaucoup de logique (écouteurs de clic, logique à remplir listes, etc.).

Ce que je fais normalement dans les frameworks basés sur les composants, c'est de tout diviser en composants personnalisés. Chaque composant personnalisé aurait alors une responsabilité claire. Il contiendrait son propre ensemble de composants et toutes les autres logiques liées à ce composant.

J'ai essayé de comprendre comment cela peut être fait, et j'ai trouvé quelque chose dans la documentation Android ce qu'ils aiment appeler un "contrôle composé". (Voir http: // développeur .Android.com/guide/topics/ui/custom-components.html # compound et faites défiler jusqu'à la section "Contrôles composés") Je voudrais créer un tel composant basé sur un fichier XML définissant la structure de la vue.

Dans la documentation, il est écrit:

Notez que, tout comme avec une activité, vous pouvez utiliser l'approche déclarative (basée sur XML) pour créer les composants contenus, ou vous pouvez les imbriquer par programme à partir de votre code.

Eh bien, c'est une bonne nouvelle! L'approche basée sur XML est exactement ce que je veux! Mais il ne dit pas comment le faire, sauf que c'est "comme avec une activité" ... Mais ce que je fais dans une activité, c'est appeler setContentView(...) pour gonfler les vues depuis XML. Cette méthode n'est pas disponible si vous par exemple sous-classe LinearLayout.

J'ai donc essayé de gonfler le XML manuellement comme ceci:

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);
    }
}

Cela fonctionne, sauf que le XML que je charge a déclaré LinearLayout comme élément racine. Il en résulte que le LinearLayout gonflé est un enfant de MyCompoundComponent qui lui-même est déjà un LinearLayout !! Nous avons donc maintenant un LinearLayout redondant entre MyCompoundComponent et les vues dont il a réellement besoin.

Quelqu'un peut-il me fournir une meilleure façon d'aborder cela, en évitant d'avoir instancié un LinearLayout redondant?

131
Tom van Zummeren

Utilisez merge tag comme racine XML

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

Consultez cet article.

100
bhatt4982

Je pense que la façon dont vous êtes censé le faire est d'utiliser votre nom de classe comme élément racine XML:

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

Et ensuite, dérivez votre classe de la disposition que vous souhaitez utiliser. Notez que si vous utilisez cette méthode, vous ne pas utilisez le gonfleur de mise en page ici.

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);
    }
}

Et vous pouvez ensuite utiliser votre vue dans les mises en page XML comme d'habitude. Si vous souhaitez créer la vue par programme, vous devez la gonfler vous-même:

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

Malheureusement, cela ne vous permet pas de faire v = new MyView(context) car il ne semble pas y avoir moyen de contourner le problème des dispositions imbriquées, donc ce n'est pas vraiment une solution complète. Vous pouvez ajouter une méthode comme celle-ci à MyView pour la rendre un peu plus agréable:

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

Avertissement: je parle peut-être de conneries complètes.

0
Timmmm