OnClickListener не работает внутри моего класса адаптера

#android #kotlin #android-recyclerview #firebaseui

# #Android #kotlin #android-recyclerview #firebaseui

Вопрос:

Я просмотрел множество решений, размещенных в Интернете, но они не смогли решить мою проблему. Вероятно, позиция адаптера возвращает -1, но почему?

     java.lang.ArrayIndexOutOfBoundsException: length=10; index=-1
        at java.util.ArrayList.get(ArrayList.java:439)
        at 
    com.firebase.ui.common.BaseObservableSnapshotArray.getSnapshot(BaseObservableSnapshotArray.java:70)
        at com.example.twitterclone.adapters.MyAdapter$TweetViewHolder.<init>(MyAdapter.kt:36)
        at com.example.twitterclone.adapters.MyAdapter.onCreateViewHolder(MyAdapter.kt:50)
        at com.example.twitterclone.adapters.MyAdapter.onCreateViewHolder(MyAdapter.kt:21)
        at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7078)
        at 
 

RecyclerViewAdapterCode:

     class MyAdapter(
    options: FirestoreRecyclerOptions<Tweet>,
    private val clickInterface: ClickInterface
    ):FirestoreRecyclerAdapter<Tweet, MyAdapter.TweetViewHolder>(options) {

    inner class TweetViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val profile =
            view.findViewById<de.hdodenhof.circleimageview.CircleImageView>(R.id.userProfile)
        val tweet = view.findViewById<TextView>(R.id.tweet)
        val like = view.findViewById<TextView>(R.id.totalLikes)
        val thumbsUp = view.findViewById<ImageView>(R.id.thumbsUp)
        val name=view.findViewById<TextView>(R.id.tweetUserName)
        init {


                val tweetId=snapshots.getSnapshot(adapterPosition).get("tweetId")
                thumbsUp.setOnClickListener {
                    clickInterface.clickLike(tweetId.toString())
                }

        }


    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): TweetViewHolder {
        val viewHolder = TweetViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.post_tweets_item, parent, false)
        )



        return viewHolder
    }

    override fun onBindViewHolder(
        holder: TweetViewHolder,
        position: Int,
        model: Tweet
    ) {
        holder.tweet.text = model.content.toString()
        holder.like.text = model.likes.toString()
        UserDao().getUser(model.uid!!).get().addOnSuccessListener {
            holder.name.text = it.get("name").toString()
            Glide.with(holder.profile.context).load(it.get("profileUrl").toString())
                .into(holder.profile)
        }


    }
     }
 

LayoutCode

     <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_margin="10dp"
    android:layout_height="wrap_content">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="2dp"
        app:cardCornerRadius="5dp"
        app:cardElevation="2dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">

                <de.hdodenhof.circleimageview.CircleImageView
                    android:id="@ id/userProfile"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:layout_marginTop="8dp"
                    android:layout_marginLeft="8dp" />

                <TextView
                    android:id="@ id/tweetUserName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="10dp"
                    android:textColor="@android:color/black"
                    android:layout_marginTop="8dp" />
            </LinearLayout>

            <TextView
                android:id="@ id/tweet"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="13dp"
                android:textColor="@android:color/black" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="80dp"
                    android:orientation="vertical"
                    android:padding="8dp">

                    <ImageView
                        android:layout_width="30dp"
                        android:id="@ id/thumbsUp"
                        android:clickable="true"
                        android:layout_height="30dp"
                        android:src="@drawable/ic_baseline_thumb_up_24" />

                    <TextView
                        android:id="@ id/totalLikes"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center" />
                </LinearLayout>

                <LinearLayout
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="80dp"
                    android:orientation="vertical"
                    android:padding="8dp">

                    <ImageView
                        android:clickable="true"
                        android:layout_width="30dp"
                        android:layout_height="30dp"
                        android:id="@ id/comments"
                        android:src="@drawable/ic_baseline_comment_24" />

                    <TextView
                        android:id="@ id/totalComments"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center" />
                </LinearLayout>
            </LinearLayout>
        </LinearLayout>
    </androidx.cardview.widget.CardView>

</LinearLayout>
 

Комментарии:

1. Попробуйте установить точку останова внутри onClickListener , чтобы увидеть, действительно ли она запускает onClick?

Ответ №1:

adapterPosition в блоке инициализации ViewHolder возвращается значение -1. Поэтому нам просто нужно вызвать adapterPosition прослушиватель, который решит проблему, поскольку он восстановит позицию, когда держатель представления будет создан и прикреплен к recyclerview . Установка любых прослушивателей в блоке инициализации — хороший подход, поскольку onCreateViewHolder вызывается только один раз для создания держателя представления, но onBindViewHolder вызывается несколько раз для одного и того же держателя представления, поэтому установка прослушивателей в onBindViewHolder будет излишней.

    init {

            thumbsUp.setOnClickListener {
                val tweetId=snapshots.getSnapshot(adapterPosition).get("tweetId")
                clickInterface.clickLike(tweetId.toString())
            }

   }
 

Ответ №2:

Прикрепите ваш OnClickListener в onBindViewHolder

 override fun onBindViewHolder(
            holder: TweetViewHolder,
            position: Int,
            model: Tweet
        ) {
            //your code of attaching data to view
            holder.thumbsUp.setOnClickListener{
              clickInterface.clickLike(model.tweetId.toString())
           }
         }
 

Пересмотрите архитектуру своего приложения. Как запрашивать у DAO данные внутри RecyclerView.Адаптер — довольно странный подход

Комментарии:

1. НЕТ, не делайте этого. Прикрепление слушателя внутри onbindviewholder ужасно, потому что это сильно повлияет на производительность!

2. добавление списка кликов влияет на производительность? от чего?

3. Конечно, в recyclerview. OnBindViewHolder() будет вызываться несколько раз, поэтому слушатель также будет подключен несколько раз. Никогда не следует этого делать и скорее подключать слушателя в OnCreateViewHolder()

4. извините, но хахахаха. Большая проблема с производительностью при вызове один раз setOnClickListener в onBindViewHolder. И в качестве комментария к коду, где автор вызывает DAO для получения данных в onBindViewHolder. В этом конкретном примере еще более неизвестно, что лучше, поскольку мы не знаем, что автор делает с методом snapshots.getSnapshot(adapterPosition).get(«tweetID»). И я думаю, что это большая проблема с производительностью, которая вызывает setOnClickListener при каждой привязке: P

5. Что ж, часть вашего ответа верна. Но только потому, что автор делает ужасные вещи внутри своего onBindViewHolder, вам не нужно делать то же самое: P

Ответ №3:

Да, я ожидаю adapterPosition , что в вашем getSnapshot вызове в блоке ViewHolder инициализации возвращается значение -1. Насколько мне известно, все средства получения позиции адаптера возвращают позицию последнего запрошенного элемента для отображения. Но когда вы впервые создаете ViewHolder s, к адаптеру еще ничего не привязано (все еще находится на этапе настройки), поэтому его значение по умолчанию position , вероятно, равно -1.

В основном способ RecyclerView работы заключается в следующем — вы определяете эти ViewHolder объекты, которые в основном представляют собой объекты, отображающие данные для каждого элемента. Вместо того, чтобы иметь по одному для каждого элемента в списке, адаптер создает несколько из них (достаточно, чтобы заполнить видимую область и пару с обеих сторон), а затем перерабатывает их, перемещая их, когда они вне поля зрения, и обновляет их содержимое для представления другого элемента.

onCreateViewHolder это то, что адаптер вызывает для создания этих объектов контейнера. Это место, где вы раздуваете макет и обычно делаете findViewById это в представлениях, чтобы у вас была ссылка на них, и вы можете просто установить TextView текст вместо того, чтобы находить представление каждый раз, когда вы хотите его обновить.

onBindViewHolder это то, что вызывается, когда вам нужно обновить ViewHolder информацию о другом элементе. Именно здесь вы фактически выполняете все настройки данных, меняете изображения и все остальное, обновляя прослушиватели кликов, если это необходимо.

В принципе, вы не должны использовать adapterPosition в своем ViewHolder init блоке, потому что он вызывается один раз, когда создается объект. Мало того, что у вас нет значимой позиции на данный момент, но разве это не то, что вы захотите часто обновлять по мере изменения позиции? Вот onBindViewHolder для чего! В нем передана позиция и все такое. Это метод, в котором вы устанавливаете состояние