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 une vue composée ou un 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 plus tard.
<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 mise en page que nous utiliserons pour la classe de spinner latéral, en veillant à envelopper les trois vues dans un <merge> étiqueter.
<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> balise au lieu de la balise pour un groupe de vues comme <LinearLayout> ou <RelativeLayout>.
Lorsque vous ajoutez la vue composée à la mise en page de MainActivity, la balise de la vue composée agira comme un <LinearLayout> étiqueter. 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 mise en page 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 déposer.
<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 mise en page linéaire puisque le SideSpinner la classe que nous avons créée étend la LinearLayout classer. 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 pouvoir 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 classer. 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 des vues dans la vue composée.
Avec les méthodes que vous venez d’ajouter à la 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, en utilisant le 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 déposer. Chaque attribut de la vue composée doit être regroupé dans un style avec un <declare-styleable> étiqueter. 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 qui sont normalement utilisés dans les mises en page peuvent être utilisés pour vos attributs personnalisés, y compris 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 coutume 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 de la double flèche latérale pour obtenir la liste des valeurs et les définir dans la double flèche latérale.
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.
7. 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.
Soutenez notre effort ❤️
Si vous avez apprécié cet article, pensez à laisser un pourboire pour nous aider à continuer à publier du contenu de qualité.





















