Vue arborescente RecyclerView (sans bibliothèques tierces et tableaux enfants)

Bonjour, chers lecteurs.

Dans mon article, je souhaite partager une implémentation d'arborescence avec RecyclerView. Sans utiliser de bibliothèques supplémentaires et sans utiliser de tableau enfant.
Peu importe, s'il vous plaît, sous le chat. J'essaierai de décrire autant que possible quoi et comment.



Le principe de la formation d'une liste d'éléments est que les éléments enfants seront affichés ou masqués.

Bien que j'ai dit que l'implémentation se fera sans bibliothèques supplémentaires, cependant, les bibliothèques standard doivent toujours être connectées.

Bibliothèques connectées
dependencies { implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:design:26.1.0' implementation 'com.android.support:recyclerview-v7:26.1.0' } 


Le balisage sera très minime - uniquement la liste RecyclerView.

Marquage
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/recycler_list"> </android.support.v7.widget.RecyclerView> </RelativeLayout> 


De plus, nous avons besoin d'une classe distincte avec laquelle nous allons stocker les valeurs de la liste.

Classe pour les données Data.java
 public final class Data { private String valueText = ""; //  private int valueId = 0; //  private boolean itemParent = false; //    private int parentId = -1; //id ,    private boolean childVisibility = false; //   //     public boolean isItemParent() { return itemParent; } //    public void setItemParent(boolean newItemParent) { itemParent = newItemParent; } //    public boolean isChildVisibility() { return childVisibility; } //     public void setChildVisibility(boolean newChildVisibility) { childVisibility = newChildVisibility; } //    public int getParentId() { return parentId; } //    public void setParentId(int newParentId) { parentId = newParentId; } //   public String getValueText() { return valueText; } //   public void setValueText(String newValueText) { valueText = newValueText; } //   public int getValueId() { return valueId; } //   public void setValueId(int newValueId) { valueId = newValueId; } } 


Les commentaires doivent être clairs, mais je vais vous expliquer. Pour chaque élément de la liste, nous allons stocker son identifiant valueId , son nom valueText , l'identifiant de l'élément parent parentId , une étiquette indiquant que l'élément est un parent itemParent et la valeur de visibilité pour les éléments enfants de childVisibility .

L'étape préparatoire suivante consiste à créer un balisage pour l'élément de liste lui-même.

Balisage pour item.xml
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/item" android:layout_width="match_parent" android:layout_height="wrap_content"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!--     --> <android.support.v7.widget.AppCompatImageView android:id="@+id/icon_tree" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/icon_hide" android:visibility="gone" app:backgroundTint="@color/colorPrimary" android:layout_centerVertical="true"/> <LinearLayout android:id="@+id/block_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_toRightOf="@+id/icon_tree"> <!--  --> <TextView android:id="@+id/value_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?android:attr/selectableItemBackground" android:text="sdfdsf"/> </LinearLayout> <!--  --> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="@color/colorPrimary" android:layout_below="@+id/block_text" android:layout_marginTop="4dp" android:layout_marginLeft="4dp" android:layout_marginRight="4dp"/> </RelativeLayout> </LinearLayout> 


AppCompatImageView est nécessaire pour afficher l'état de l'élément parent. TextView - pour afficher la valeur d'un élément. La vue est juste pour le partage.

La dernière étape préparatoire consiste à créer une classe pour gérer l'adaptateur de liste.

Adaptateur pour liste RecyclerViewAdapter.java
 public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> { private View vv; private List<Data> allRecords; //   public RecyclerViewAdapter(List<Data> records) { allRecords = records; } @Override public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false); return new RecyclerViewAdapter.ViewHolder(v); } @Override public void onBindViewHolder(final RecyclerViewAdapter.ViewHolder viewHolder, int i) { Data record = allRecords.get(i); String value = record.getValueText(); int id = record.getValueId(); int parentId = record.getParentId(); final int position = i; final String text = "#" + id + ": " + value + " (id  : " + parentId + ")"; //   ,    if (parentId >= 0) { //      setVisibility(viewHolder.item, allRecords.get(parentId).isChildVisibility(), parentId); } else { //  ,   setVisibility(viewHolder.item, true, parentId); } //      if (record.isItemParent()) { viewHolder.iconTree.setVisibility(View.VISIBLE); //   if (record.isChildVisibility()) //   viewHolder.iconTree.setBackgroundResource(R.drawable.icon_show); else //   viewHolder.iconTree.setBackgroundResource(R.drawable.icon_hide); } else //   viewHolder.iconTree.setVisibility(View.GONE); //   if (!TextUtils.isEmpty(value)) { viewHolder.valueText.setText(value); } //     viewHolder.valueText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Data dataItem = allRecords.get(position); if (dataItem.isItemParent()) { //   ,     dataItem.setChildVisibility(!dataItem.isChildVisibility()); notifyDataSetChanged(); } else { //   ,    Snackbar snackbar = Snackbar.make(vv, text, Snackbar.LENGTH_LONG); snackbar.show(); } } }); } //   private void setVisibility(View curV, boolean visible, int parentId) { // ,      LinearLayout vPadding = curV.findViewById(R.id.block_text); LinearLayout.LayoutParams params; if (visible) { params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); if (vPadding != null) { if (parentId >= 0) { //  ,   vPadding.setPadding(80, 0, 0, 0); } else { vPadding.setPadding(0, 0, 0, 0); } } } else params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); curV.setLayoutParams(params); } @Override public int getItemCount() { return allRecords.size(); } class ViewHolder extends RecyclerView.ViewHolder { private LinearLayout item; private TextView valueText; private AppCompatImageView iconTree; public ViewHolder(View itemView) { super(itemView); vv = itemView; item = vv.findViewById(R.id.id_item); valueText = vv.findViewById(R.id.value_name); iconTree = vv.findViewById(R.id.icon_tree); } } } 


Le traitement principal a lieu dans la procédure onBindViewHolder . Pour chaque élément de liste, son identifiant, sa valeur et les paramètres de la valeur parent sont obtenus. Affichage ou masquage des éléments enfants, ainsi qu'une icône d'état pour l'élément parent. Eh bien, le traitement des clics sur la liste est bloqué. Ensuite, tout le monde décide comment il doit traiter la liste. L'exemple montre simplement un message avec id et valeur d'élément.
Dans la procédure d'affichage ou de masquage de l'enfant setVisibility , le texte est en retrait pour l'élément enfant à 80 pixels.

Il ne reste plus qu'à remplir la liste au bon endroit.

Création de liste
 List<Data> records = new ArrayList<Data>(); //  Data record; RecyclerViewAdapter adapter; int parentId; RecyclerView recyclerView = findViewById(R.id.recycler_list); record = new Data(); record.setValueId(1); record.setValueText("  1"); record.setItemParent(true); //  records.add(record); parentId = records.size() -1; for (int ind = 1; ind <= 3; ind ++) { record = new Data(); record.setValueId(ind); record.setValueText(" " + ind); record.setParentId(parentId); records.add(record); } record = new Data(); record.setValueId(1); record.setValueText("  "); record.setItemParent(true); //  records.add(record); parentId = records.size() -1; for (int ind = 4; ind <= 7; ind ++) { record = new Data(); record.setValueId(ind); record.setValueText("  " + ind); record.setParentId(parentId); records.add(record); } record = new Data(); record.setValueId(1); record.setValueText("  "); record.setItemParent(true); //  records.add(record); parentId = records.size() -1; for (int ind = 8; ind <= 12; ind ++) { record = new Data(); record.setValueId(ind); record.setValueText(" " + ind); record.setParentId(parentId); records.add(record); } for (int ind = 13; ind <= 18; ind ++) { record = new Data(); record.setValueId(ind); record.setValueText("  " + ind); records.add(record); } for (int ind = 19; ind <= 21; ind ++) { record = new Data(); record.setValueId(ind); record.setValueText("   " + ind); records.add(record); } record = new Data(); record.setValueId(1); record.setValueText("  "); record.setItemParent(true); //  records.add(record); parentId = records.size() -1; for (int ind = 22; ind <= 30; ind ++) { record = new Data(); record.setValueId(ind); record.setValueText(": " + ind); record.setParentId(parentId); records.add(record); } for (int ind = 31; ind <= 45; ind ++) { record = new Data(); record.setValueId(ind); record.setValueText("   " + ind); records.add(record); } adapter = new RecyclerViewAdapter(records); RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator(); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager(layoutManager); recyclerView.setItemAnimator(itemAnimator); 


Le résultat est une liste si simple avec le support des éléments enfants. Cette implémentation vous permet de remplir plusieurs éléments imbriqués. Mais vous devez affiner légèrement l'indentation des éléments enfants si le niveau d'imbrication est supérieur à 1.

Merci à tous pour votre attention et vos projets réussis.

Source: https://habr.com/ru/post/fr431718/


All Articles