La gestion de la logique des lignes / sections similaire à UITableView d'iOS n'est pas aussi simple dans Android que dans iOS, cependant, lorsque vous utilisez RecyclerView, la flexibilité de ce que vous pouvez faire est bien plus grande.
En fin de compte, tout dépend de la façon dont vous déterminez le type de vue que vous affichez dans l'adaptateur. Une fois que vous avez compris cela, la navigation devrait être facile (pas vraiment, mais au moins vous l'aurez triée).
L'adaptateur expose deux méthodes que vous devez remplacer:
getItemViewType(int position)
L'implémentation par défaut de cette méthode retournera toujours 0, indiquant qu'il n'y a qu'un seul type de vue. Dans votre cas, ce n'est pas le cas, et vous devrez donc trouver un moyen d'affirmer quelle ligne correspond à quel type de vue. Contrairement à iOS, qui gère cela pour vous avec des lignes et des sections, vous n'aurez ici qu'un seul index sur lequel vous appuyer, et vous devrez utiliser vos compétences de développeur pour savoir quand une position est en corrélation avec un en-tête de section, et quand elle correspond à une ligne normale.
createViewHolder(ViewGroup parent, int viewType)
Vous devez quand même remplacer cette méthode, mais généralement, les gens ignorent simplement le paramètre viewType. En fonction du type de vue, vous devrez gonfler la bonne ressource de mise en page et créer votre support de vue en conséquence. Le RecyclerView gérera le recyclage de différents types de vues de manière à éviter les conflits entre différents types de vues.
Si vous prévoyez d'utiliser un LayoutManager par défaut, tel que LinearLayoutManager
, vous devriez être prêt à partir. Si vous envisagez de créer votre propre implémentation de LayoutManager, vous devrez travailler un peu plus dur. La seule API avec laquelle vous devez vraiment travailler est celle findViewByPosition(int position)
qui donne une vue donnée à une certaine position. Étant donné que vous voudrez probablement le présenter différemment en fonction du type de cette vue, vous avez quelques options:
Généralement, lorsque vous utilisez le modèle ViewHolder, vous définissez la balise de la vue avec le support de vue. Vous pouvez l'utiliser pendant l'exécution dans le gestionnaire de mise en page pour savoir de quel type est la vue en ajoutant un champ dans le support de vue qui l'exprime.
Puisque vous aurez besoin d'une fonction qui détermine quelle position correspond à quel type de vue, vous pouvez aussi bien rendre cette méthode globalement accessible d'une manière ou d'une autre (peut-être une classe singleton qui gère les données?), Et ensuite vous pouvez simplement interroger la même méthode en fonction de la position.
Voici un exemple de code:
// in this sample, I use an object array to simulate the data of the list.
// I assume that if the object is a String, it means I should display a header with a basic title.
// If not, I assume it's a custom model object I created which I will use to bind my normal rows.
private Object[] myData;
public static final int ITEM_TYPE_NORMAL = 0;
public static final int ITEM_TYPE_HEADER = 1;
public class MyAdapter extends Adapter<ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE_NORMAL) {
View normalView = LayoutInflater.from(getContext()).inflate(R.layout.my_normal_row, null);
return new MyNormalViewHolder(normalView); // view holder for normal items
} else if (viewType == ITEM_TYPE_HEADER) {
View headerRow = LayoutInflater.from(getContext()).inflate(R.layout.my_header_row, null);
return new MyHeaderViewHolder(headerRow); // view holder for header items
}
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
final int itemType = getItemViewType(position);
if (itemType == ITEM_TYPE_NORMAL) {
((MyNormalViewHolder)holder).bindData((MyModel)myData[position]);
} else if (itemType == ITEM_TYPE_HEADER) {
((MyHeaderViewHolder)holder).setHeaderText((String)myData[position]);
}
}
@Override
public int getItemViewType(int position) {
if (myData[position] instanceof String) {
return ITEM_TYPE_HEADER;
} else {
return ITEM_TYPE_NORMAL;
}
}
@Override
public int getItemCount() {
return myData.length;
}
}
Voici un exemple de l'apparence de ces détenteurs de vue:
public MyHeaderViewHolder extends ViewHolder {
private TextView headerLabel;
public MyHeaderViewHolder(View view) {
super(view);
headerLabel = (TextView)view.findViewById(R.id.headerLabel);
}
public void setHeaderText(String text) {
headerLabel.setText(text);
}
}
public MyNormalViewHolder extends ViewHolder {
private TextView titleLabel;
private TextView descriptionLabel;
public MyNormalViewHolder(View view) {
super(view);
titleLabel = (TextView)view.findViewById(R.id.titleLabel);
descriptionLabel = (TextView)view.findViewById(R.id.descriptionLabel);
}
public void bindData(MyModel model) {
titleLabel.setText(model.getTitle());
descriptionLabel.setText(model.getDescription());
}
}
Bien sûr, cet exemple suppose que vous avez construit votre source de données (myData) d'une manière qui facilite l'implémentation d'un adaptateur de cette manière. À titre d'exemple, je vais vous montrer comment je construirais une source de données qui montre une liste de noms et un en-tête pour chaque fois que la 1ère lettre du nom change (en supposant que la liste est alphabétisée) - similaire à la façon dont un contacts la liste ressemblerait à:
// Assume names & descriptions are non-null and have the same length.
// Assume names are alphabetized
private void processDataSource(String[] names, String[] descriptions) {
String nextFirstLetter = "";
String currentFirstLetter;
List<Object> data = new ArrayList<Object>();
for (int i = 0; i < names.length; i++) {
currentFirstLetter = names[i].substring(0, 1); // get the 1st letter of the name
// if the first letter of this name is different from the last one, add a header row
if (!currentFirstLetter.equals(nextFirstLetter)) {
nextFirstLetter = currentFirstLetter;
data.add(nextFirstLetter);
}
data.add(new MyModel(names[i], descriptions[i]));
}
myData = data.toArray();
}
Cet exemple vient résoudre un problème assez spécifique, mais j'espère que cela vous donne un bon aperçu de la façon de gérer différents types de lignes dans un recycleur et vous permet de faire les adaptations nécessaires dans votre propre code pour répondre à vos besoins.