Lors de la création d’applications complexes, vous souhaiterez souvent réutiliser le même groupe de vues à différents endroits de l’application. Une façon de résoudre ce problème consiste à créer une vue qui encapsule la logique et la disposition d’un groupe de vues afin que vous puissiez les réutiliser sans dupliquer le code à divers endroits du projet. Dans ce didacticiel, vous apprendrez à utiliser des vues composées pour créer des vues personnalisées facilement réutilisables.
1. introduction
Sur Android, une vue composée d’un groupe de vues est appelée vue composée ou composant composé. Dans ce didacticiel, vous allez créer un contrôle pour sélectionner une valeur dans une liste qui défile d’un côté à l’autre. Nous nommerons le composé un spinner latéral puisque la vue par défaut du SDK Android pour choisir une valeur dans une liste est appelée un spinner. La capture d’écran suivante illustre ce que nous allons créer dans ce didacticiel.


2. Configuration du projet
Pour commencer, vous devez créer un nouveau projet Android avec Android 4.0 comme niveau SDK minimum requis. Ce projet ne doit contenir qu’une activité vide appelée Activité principale. le Activity
ne fait rien de plus que d’initialiser la mise en page comme vous pouvez le voir dans l’extrait de code suivant.
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
La mise en page pour MainActivity
est situé dans le /res/layout/activity_main.xml fichier et il ne doit contenir qu’un fichier vide RelativeLayout
dans laquelle la vue composée sera affichée ultérieurement.
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> </RelativeLayout>
3. Créer une vue composée
Pour créer une vue composée, vous devez créer une nouvelle classe qui gère les vues dans la vue composée. Pour le spinner latéral, vous avez besoin de deux Button
vues pour les flèches et un TextView
view pour afficher la valeur sélectionnée.
Pour commencer, créez le /res/layout/sidespinner_view.xml fichier de disposition que nous utiliserons pour la classe de spinner latéral, en veillant à envelopper les trois vues dans un <merge>
marque.
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:id="@+id/sidespinner_view_previous" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toLeftOf="@+id/sidespinner_view_value"/> <TextView android:id="@+id/sidespinner_view_current_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="24sp" /> <Button android:id="@+id/sidespinner_view_next" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </merge>
Ensuite, nous devons créer le SideSpinner
classe qui gonfle cette disposition et définit les flèches comme images d’arrière-plan des boutons. À ce stade, la vue composée ne fait rien car il n’y a encore rien à afficher.
public class SideSpinner extends LinearLayout { private Button mPreviousButton; private Button mNextButton; public SideSpinner(Context context) { super(context); initializeViews(context); } public SideSpinner(Context context, AttributeSet attrs) { super(context, attrs); initializeViews(context); } public SideSpinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initializeViews(context); } /** * Inflates the views in the layout. * * @param context * the current context for the view. */ private void initializeViews(Context context) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.sidespinner_view, this); } @Override protected void onFinishInflate() { super.onFinishInflate(); // Sets the images for the previous and next buttons. Uses // built-in images so you don't need to add images, but in // a real application your images should be in the // application package so they are always available. mPreviousButton = (Button) this .findViewById(R.id.sidespinner_view_previous); mPreviousButton .setBackgroundResource(android.R.drawable.ic_media_previous); mNextButton = (Button)this .findViewById(R.id.sidespinner_view_next); mNextButton .setBackgroundResource(android.R.drawable.ic_media_next); } }
Vous remarquerez que la vue composée étend la LinearLayout
voir le groupe. Cela signifie que toute mise en page utilisant la vue composée a accès aux attributs de la mise en page linéaire. En conséquence, la disposition de la vue composée est un peu différente de celle habituelle, la balise racine est un <merge>
au lieu de la balise pour un groupe de vues comme <LinearLayout>
ou <RelativeLayout>
.
Lorsque vous ajoutez la vue composée à la présentation de MainActivity
, la balise de la vue composée agira comme un <LinearLayout>
marque. Une classe de vue composée peut dériver de n’importe quelle classe dérivant de ViewGroup
, mais dans ce cas, la disposition linéaire est la plus appropriée puisque les vues sont disposées horizontalement.
4. Ajouter la vue composée à une mise en page
À ce stade, le projet se compile mais rien n’est visible car la vue composée n’est pas dans la disposition de MainActivity
. La vue fléchée latérale doit être ajoutée à la mise en page de l’activité comme toute autre vue. Le nom de la balise est le nom complet du SideSpinner
classe, y compris l’espace de noms.
Pour ajouter la double flèche latérale à MainActivity
, ajoutez ce qui suit à la disposition relative dans le /res/layout/activity_main.xml fichier.
<com.cindypotvin.sidespinnerexample.SideSpinner android:id="@+id/sidespinner_fruits" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center"/>
Les attributs disponibles dans le <SideSpinner>
balise sont des attributs de la disposition linéaire puisque le SideSpinner
la classe que nous avons créée étend la LinearLayout
classe. Si vous lancez le projet, la double flèche latérale doit être visible, mais elle ne contient pas encore de valeurs.
5. Ajouter des méthodes à la vue composée
Il manque encore quelques éléments si nous voulons réellement utiliser le spinner latéral. Nous devrions être en mesure d’ajouter de nouvelles valeurs au spinner, sélectionner une valeur et obtenir la valeur sélectionnée.
Le moyen le plus simple d’ajouter de nouveaux comportements à une vue composée consiste à ajouter de nouvelles méthodes publiques au SideSpinner
classe. Ces méthodes peuvent être utilisées par tout Activity
qui a une référence à la vue.
private CharSequence[] mSpinnerValues = null; private int mSelectedIndex = -1; /** * Sets the list of value in the spinner, selecting the first value * by default. * * @param values * the values to set in the spinner. */ public void setValues(CharSequence[] values) { mSpinnerValues = values; // Select the first item of the string array by default since // the list of value has changed. setSelectedIndex(0); } /** * Sets the selected index of the spinner. * * @param index * the index of the value to select. */ public void setSelectedIndex(int index) { // If no values are set for the spinner, do nothing. if (mSpinnerValues == null || mSpinnerValues.length == 0) return; // If the index value is invalid, do nothing. if (index < 0 || index >= mSpinnerValues.length) return; // Set the current index and display the value. mSelectedIndex = index; TextView currentValue; currentValue = (TextView)this .findViewById(R.id.sidespinner_view_current_value); currentValue.setText(mSpinnerValues[index]); // If the first value is shown, hide the previous button. if (mSelectedIndex == 0) mPreviousButton.setVisibility(INVISIBLE); else mPreviousButton.setVisibility(VISIBLE); // If the last value is shown, hide the next button. if (mSelectedIndex == mSpinnerValues.length - 1) mNextButton.setVisibility(INVISIBLE); else mNextButton.setVisibility(VISIBLE); } /** * Gets the selected value of the spinner, or null if no valid * selected index is set yet. * * @return the selected value of the spinner. */ public CharSequence getSelectedValue() { // If no values are set for the spinner, return an empty string. if (mSpinnerValues == null || mSpinnerValues.length == 0) return ""; // If the current index is invalid, return an empty string. if (mSelectedIndex < 0 || mSelectedIndex >= mSpinnerValues.length) return ""; return mSpinnerValues[mSelectedIndex]; } /** * Gets the selected index of the spinner. * * @return the selected index of the spinner. */ public int getSelectedIndex() { return mSelectedIndex; }
le onFinishInflate
La méthode de la vue composée est appelée lorsque toutes les vues de la présentation sont gonflées et prêtes à être utilisées. C’est l’endroit où ajouter votre code si vous devez modifier les vues dans la vue composée.
Avec les méthodes que vous venez d’ajouter au SideSpinner
classe, le comportement des boutons sélectionnant la valeur précédente et suivante peut maintenant être ajouté. Remplacez le code existant dans le onFinishInflate
méthode avec ce qui suit:
@Override protected void onFinishInflate() { // When the controls in the layout are doing being inflated, set // the callbacks for the side arrows. super.onFinishInflate(); // When the previous button is pressed, select the previous value // in the list. mPreviousButton = (Button) this .findViewById(R.id.sidespinner_view_previous); mPreviousButton .setBackgroundResource(android.R.drawable.ic_media_previous); mPreviousButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { if (mSelectedIndex > 0) { int newSelectedIndex = mSelectedIndex - 1; setSelectedIndex(newSelectedIndex); } } }); // When the next button is pressed, select the next item in the // list. mNextButton = (Button)this .findViewById(R.id.sidespinner_view_next); mNextButton .setBackgroundResource(android.R.drawable.ic_media_next); mNextButton.setOnClickListener(new OnClickListener() { public void onClick(View view) { if (mSpinnerValues != null && mSelectedIndex < mSpinnerValues.length - 1) { int newSelectedIndex = mSelectedIndex + 1; setSelectedIndex(newSelectedIndex); } } }); // Select the first value by default. setSelectedIndex(0); }
Avec le nouvellement créé setValues
et setSelectedIndex
méthodes, nous pouvons maintenant initialiser le spinner latéral à partir de notre code. Comme pour toute autre vue, vous devez trouver la vue fléchée latérale dans la mise en page avec le findViewById
méthode. Nous pouvons alors appeler n’importe quelle méthode publique sur la vue à partir de l’objet retourné, y compris celles que nous venons de créer.
L’extrait de code suivant montre comment mettre à jour le onCreate
méthode de la MainActivity
classe pour afficher une liste de valeurs dans la double flèche latérale, à l’aide du setValues
méthode. Nous pouvons également sélectionner la deuxième valeur de la liste par défaut en invoquant le setSelectedIndex
méthode.
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Initializes the side spinner from code. SideSpinner fruitsSpinner; fruitsSpinner = (SideSpinner)this .findViewById(R.id.sidespinner_fruits); CharSequence fruitList[] = { "Apple", "Orange", "Pear", "Grapes" }; fruitsSpinner.setValues(fruitList); fruitsSpinner.setSelectedIndex(1); } }
Si vous lancez l’application, le spinner latéral devrait fonctionner comme prévu. La liste des valeurs s’affiche et la valeur Orange est sélectionné par défaut.
6. Ajouter des attributs de disposition à la vue composée
Les vues disponibles dans le SDK Android peuvent être modifiées via le code, mais certains attributs peuvent également être définis directement dans la mise en page correspondante. Ajoutons un attribut au spinner latéral qui définit les valeurs que le spinner latéral doit afficher.
Pour créer un attribut personnalisé pour la vue composée, nous devons d’abord définir l’attribut dans le /res/values/attr.xml fichier. Chaque attribut de la vue composée doit être regroupé dans un style avec un <declare-styleable>
marque. Pour la double flèche latérale, le nom de la classe est utilisé comme indiqué ci-dessous.
<resources> <declare-styleable name="SideSpinner"> <attr name="values" format="reference" /> </declare-styleable> </resources>
dans le <attr>
tag, le name
l’attribut contient l’identifiant utilisé pour faire référence au nouvel attribut dans la mise en page et le format
attribut contient le type du nouvel attribut.
Pour la liste des valeurs, le reference
type est utilisé car l’attribut fera référence à une liste de chaînes définies comme une ressource. Les types de valeur normalement utilisés dans les mises en page peuvent être utilisés pour vos attributs personnalisés, notamment boolean
, color
, dimension
, enum
, integer
, float
et string
.
Voici comment définir la ressource pour une liste de chaînes que le values
l’attribut de la double flèche latérale se référera à. Il doit être ajouté au /res/values/strings.xml fichier comme indiqué ci-dessous.
<resources> <string-array name="vegetable_array"> <item>Cucumber</item> <item>Potato</item> <item>Tomato</item> <item>Onion</item> <item>Squash</item> </string-array> </resources>
Pour tester le nouveau values
attribut, créez une vue fléchée latérale dans le MainActivity
mise en page sous le spinner latéral existant. L’attribut doit être précédé d’un espace de noms ajouté au RelativeLayout
, tel que xmlns:sidespinner="http://schemas.android.com/apk/res-auto"
. C’est ce que la mise en page finale /res/layout/activity_main.xml devrait ressembler à.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:sidespinner="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.cindypotvin.sidespinnerexample.SideSpinner android:id="@+id/sidespinner_fruits" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center"/> <com.cindypotvin.sidespinnerexample.SideSpinner android:id="@+id/sidespinner_vegetables" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center" android:layout_below="@id/sidespinner_fruits" sidespinner:values="@array/vegetable_array" /> </RelativeLayout>
Finalement, le SideSpinner
la classe doit être modifiée pour lire le values
attribut. La valeur de chaque attribut de la vue est disponible dans le AttributeSet
objet qui est passé en tant que paramètre du constructeur de la vue.
Pour obtenir la valeur de votre personnalisé values
attribut, nous appelons d’abord le obtainStyledAttributes
méthode de la AttributeSet
objet avec le nom du stylable contenant l’attribut. Cela renvoie la liste des attributs pour ce stylable sous forme de TypedArray
objet.
Nous appelons ensuite la méthode getter du TypedArray
objet qui a le bon type pour l’attribut souhaité, en passant l’identifiant de l’attribut en tant que paramètre. Le bloc de code suivant montre comment modifier le constructeur du spinner latéral pour obtenir la liste de valeurs et les définir dans le spinner latéral.
public SideSpinner(Context context) { super(context); initializeViews(context); } public SideSpinner(Context context, AttributeSet attrs) { super(context, attrs); TypedArray typedArray; typedArray = context .obtainStyledAttributes(attrs, R.styleable.SideSpinner); mSpinnerValues = typedArray .getTextArray(R.styleable.SideSpinner_values); typedArray.recycle(); initializeViews(context); } public SideSpinner(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray typedArray; typedArray = context .obtainStyledAttributes(attrs, R.styleable.SideSpinner); mSpinnerValues = typedArray .getTextArray(R.styleable.SideSpinner_values); typedArray.recycle(); initializeViews(context); }
Si vous lancez l’application, vous devriez voir deux flèches latérales qui fonctionnent indépendamment l’une de l’autre.
sept. Enregistrer et restaurer l’état
La dernière étape que nous devons effectuer est l’enregistrement et la restauration de l’état de la vue composée. Lorsqu’une activité est détruite et recréée, par exemple, lors de la rotation de l’appareil, les valeurs des vues natives avec un identifiant unique sont automatiquement enregistrées et restaurées. Ce n’est actuellement pas le cas pour le spinner latéral.
L’état des vues n’est pas enregistré. Les identifiants des vues dans le SideSpinner
class ne sont pas uniques car elles peuvent être réutilisées plusieurs fois. Cela signifie que nous sommes responsables de l’enregistrement et de la restauration des valeurs des vues dans la vue composée. Nous faisons cela en implémentant le onSaveInstanceState
, onRestoreInstanceState
, et dispatchSaveInstanceState
méthodes. Le bloc de code suivant montre comment procéder pour la double flèche latérale.
/** * Identifier for the state to save the selected index of * the side spinner. */ private static String STATE_SELECTED_INDEX = "SelectedIndex"; /** * Identifier for the state of the super class. */ private static String STATE_SUPER_CLASS = "SuperClass"; @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putParcelable(STATE_SUPER_CLASS, super.onSaveInstanceState()); bundle.putInt(STATE_SELECTED_INDEX, mSelectedIndex); return bundle; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle)state; super.onRestoreInstanceState(bundle .getParcelable(STATE_SUPER_CLASS)); setSelectedIndex(bundle.getInt(STATE_SELECTED_INDEX)); } else super.onRestoreInstanceState(state); } @Override protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) { // Makes sure that the state of the child views in the side // spinner are not saved since we handle the state in the // onSaveInstanceState. super.dispatchFreezeSelfOnly(container); } @Override protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { // Makes sure that the state of the child views in the side // spinner are not restored since we handle the state in the // onSaveInstanceState. super.dispatchThawSelfOnly(container); }
Conclusion
Le spinner latéral est maintenant terminé. Les deux flèches latérales fonctionnent comme prévu et leurs valeurs sont restaurées si l’activité est détruite et recréée. Vous pouvez désormais appliquer ce que vous avez appris pour réutiliser n’importe quel groupe de vues dans une application Android à l’aide de vues composées.
Laisser un commentaire