#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
для чего! В нем передана позиция и все такое. Это метод, в котором вы устанавливаете состояние