web-dev-qa-db-fra.com

Comment lier une vue dans RecyclerView.ViewHolder avec kotlin

Ce qui me rend perplexe, c'est comment lier la vue dans Recycleler.ViewHolder. Ceci est mon adaptateur simple et comment le convertir en kotlin utilise kotlin-Android-extensions sans ButterKnife?

public class RoomAdapter extends RecyclerView.Adapter<ViewHolder> {

  private OnItemClickListener mListener;
  private List<LocationBean> mRooms;

  static class ViewHolder extends RecyclerView.ViewHolder {

  @BindView(R.id.tv_title)
  TextView tvTitle;

  public ViewHolder(View itemView) {
   super(itemView);
   ButterKnife.bind(this, itemView);
   }
  }

  public void setData(List<LocationBean> rooms) {
   mRooms = rooms;
   notifyDataSetChanged();
  }

  @Override
  public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext())
      .inflate(R.layout.item_first_select, parent, false);
    return new ViewHolder(view);
  }

  @Override
  public void onBindViewHolder(final ViewHolder holder, int position) {
  holder.tvTitle.setText(mRooms.get(position).getLocation());

  holder.itemView.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
      mListener.onItemClickListener(v, holder.getLayoutPosition());
     }
    });
  }

  @Override
  public int getItemCount() {
    return mRooms == null ? 0 : mRooms.size();
  }

  public void setOnItemClickListener(OnItemClickListener listener) {
    mListener = listener;
  }

  public interface OnItemClickListener {
    void onItemClickListener(View v, int pos);
  }

}
22
shiguiyou

La solution affichée fonctionne, mais j'aimerais ajouter quelque chose. Le modèle de détenteur de vues a pour but de ne faire que les appels coûteux findViewById une fois pour chaque vue, puis de conserver ces références dans la ViewHolder et d'accéder aux vues à partir de là chaque fois que vous en avez besoin.

Cependant, l'appel de holder.itemView.tv_title.text dans la méthode onBindViewHolder provoquera un appel findViewById pour rechercher la View ayant l'id tv_title dans la itemView chaque fois que le détenteur de la vue est lié. Cela élimine fondamentalement les gains de performances et l'idée de mise en cache que les visionneuses sont pour.

Vous pouvez utiliser le modèle de détenteur de vues et les extensions Kotlin Android simultanément en ajoutant une propriété à votre ViewHolder et en l'initialisant avec un appel effectué avec des extensions, comme suit:

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val title = itemView.tv_title
}

Ensuite, vous pouvez accéder à la View via cette propriété:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.title.text = mRooms!!.get(position).getLocation()

    holder.itemView.setOnClickListener { v ->
        mListener?.onItemClickListener(v, holder.layoutPosition)
    }
}

Enfin, je suggérerais de supprimer l’opérateur !! et d’effectuer plutôt une vérification nulle:

mRooms?.let { rooms ->
    holder.title.text = rooms[position].getLocation()
}
38
zsmb13

Assurez-vous simplement que vous avez apply plugin: 'kotlin-Android-extensions' dans votre fichier de dégradé d'application (pas votre gradle racine), puis utilisez simplement le raccourci Convert in Java file to Kotlin file. Supprimez le code Butterknife et faites directement référence à itemView.tv_title dans votre objet ViewHolder comme suit. 

Edit : J'ai modifié le code pour tirer parti de la mise en cache de vues synthétiques de Kotlin. Vous n'avez pas besoin de définir une variable égale à une vue telle que val title = itemView.tv_title pour bénéficier de ces avantages.

import kotlinx.Android.synthetic.main.item_first_select.view.*

class RoomAdapter : RecyclerView.Adapter<RoomAdapter.ViewHolder>() {

    private var listener: OnItemClickListener? = null
    private var rooms: List<LocationBean>? = null

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bind(room: Room) {
            itemView.tv_title.text = room.getLocation()
        }

    }

    fun setData(rooms: List<LocationBean>) {
        this.rooms = rooms
        notifyDataSetChanged()
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_first_select, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        rooms?.get(position)?.let { room ->
            holder.bind(room)
        }

        holder.itemView.setOnClickListener { v ->
            listener?.onItemClickListener(v, holder.layoutPosition)
        }
    }

    override fun getItemCount(): Int {
        return rooms?.size ?: 0
    }

    fun setOnItemClickListener(listener: OnItemClickListener) {
        this.listener = listener
    }

    interface OnItemClickListener {
        fun onItemClickListener(v: View, pos: Int)
    }

}
7
Nathan Reline

D'autres ont très bien répondu à la question. Je voulais juste ajouter que dans class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), il ne devrait pas y avoir d'opérateur nullable ('?') après itemView pour que la liaison de kotlin fonctionne. 

Android Studio ajoute automatiquement cet opérateur nullable lors de l'utilisation de la saisie semi-automatique pour l'ajout de paramètres de constructeur à partir de ViewHolder(itemView).

1
Ali Nem