Мой внешний RecyclerView
аварийно завершает работу с
IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:false isAttached:true...
или
IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
Как видно из названия, у меня есть RecyclerView
в макете элемента списка первого RecyclerView
.Этот макет используется для отображения сообщений, а внутренний RecyclerView
для отображения вложений, которые идут с сообщением.Внутренняя RecyclerViews
видимость установлена на GONE
или VISIBLE
в зависимости от того, есть ли в сообщении какие-либо вложения или нет.Упрощенная внешняя компоновка элементов списка выглядит следующим образом
ConstraintLayout
TextView
TextView
TextView
RecyclerView
А часть адаптера, которая обрабатывает внутренний RecyclerView
, выглядит следующим образом
private fun bindFiles(message: Message?) = with(itemView) {
if (message != null && message.attachments.isNotEmpty())
{
sent_message_attachments.setAsVisible()
sent_message_attachments.layoutManager = GridLayoutManager(this.context,Math.min(message.attachments.size,3))
sent_message_attachments.adapter = AttachmentAdapter(message.attachments)
sent_message_attachments.itemAnimator = null
sent_message_attachments.setHasFixedSize(true)
}
else{
sent_message_attachments.setAsGone()
sent_message_attachments.adapter = null
sent_message_attachments.layoutManager = null
}
}
Ошибка связана сспособ, которым я получаю вложения во внутреннем адаптере, поскольку, как только я отключаю часть, которая запускает процесс загрузки, все в порядке.При загрузке изображений с устройства проблем нет, но как только я запускаю процесс загрузки, все идет к черту.Это та часть, которая обрабатывает изображения и запускает процесс загрузки во внутреннем адаптере.У меня есть функции для видео и для других типов файлов, которые в значительной степени совпадают, но используют немного другой макет.
private fun bindImage(item: HFile?) = with(itemView) {
if (item != null)
{
if (item.isOnDevice && !item.path.isNullOrEmpty())
{
if (item.isGif)
{
attachment_image.displayGif(File(item.path))
}
else
{
attachment_image.displayImage(File(item.path))
}
}
else
{
//TODO: Add option to load images manually
FileHandler(item.id).downloadFileAsObservable(false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ progress ->
//TODO: Show download process
},
{ error ->
error.printStackTrace()
//TODO: Enable manual retry
},
{ notifyItemChanged(adapterPosition)} //onComplete
)
}
}
}
Я использую ту же структуру, что и выше, в моих DiscussionListAdapter
для загрузки портретов для обсуждения (изображения профиля и т. д.) и у него нет той же проблемы.
Это функции расширений, используемые для надувания viewHolders и для отображения изображений
fun ViewGroup.inflate(layoutRes: Int): View
{
return LayoutInflater.from(context).inflate(layoutRes, this, false)
}
fun ImageView.displayGif(file:File){
GlideApp.with(context).asGif().load(file).transforms(CenterCrop(), RoundedCorners(30)).into(this)
}
fun ImageView.displayImage(file:File){
GlideApp.with(context).load(file).transforms(CenterCrop(), RoundedCorners(30)).into(this)
}
Я был на этомв течение прошлых нескольких дней и просто не могу обдумать это.Любая помощь в любом направлении очень ценится.Я знаю, что мои объяснения могут быть немного повсюду, поэтому просто попросите разъяснений, когда это необходимо:)
ОБНОВЛЕНИЕ
Теперь я смог произвести это с GridLayout
, а такжес RecyclerView
.Можно с уверенностью предположить, что вложенные RecyclerViews
не были виновником здесь.Я даже пытался отказаться от Rx-компонента, который обрабатывал загрузку изображений и создал IntentService
для процесса, но такие же сбои все еще происходят.
С GridLayout
Я имею в виду, что вместо того, чтобы иметь другой адаптер длязаполнение вложенного RecyclerView
Я использую только один адаптер для заполнения сообщения, а также для надувания и заполнения представлений для вложений, а также для присоединения этих представлений к вложенному GridLayout
.
Сбой происходит при запускечтобы загрузить файл и затем прокрутить представление, которое должно показать загруженный файл, из экрана.Это представление должно быть переработано, но по какой-то причине процесс загрузки (который в моих тестовых примерах занимает всего около 100-400 мс) заставляет приложение выдавать одну из двух ошибок, упомянутых в исходном вопросе.Возможно, стоит отметить, что я использую Realm
, а адаптер получает список RealmResults<Message>
в качестве набора данных.Мой докладчик ищет изменения в списке, а затем уведомляет адаптер при необходимости (измененный из-за реализации IntentService
).
Вот так я могу воспроизводить это снова и снова:
- Открыть дискуссию, в которой есть сообщения с вложениями
- Начать прокрутку вверх для просмотра дополнительных сообщений
- Передать сообщение с вложением и вывести его из экрана, пока он еще загружается
- Crash
Не произойдет сбоя, если я остановлюсь и буду ждать завершения загрузки, и все будет работать, как задумано.Изображение / видео / файл обновляются с соответствующим уменьшенным изображением, и приложение не будет зависать, если я прокручиваю его из поля зрения.
ОБНОВЛЕНИЕ 2
Я попытался заменить вложенный ViewGroup
на одинImageView
просто чтобы увидеть, есть проблема во вложенности.И вот!Это все еще падает.Теперь я действительно сбит с толку, так как DiscussionListAdapter
, о котором я упоминал ранее, содержит в себе ту же самую вещь, и она работает как шарм ... Мой поиск продолжается.Надеюсь, кто-нибудь когда-нибудь получит пользу от моей агонии.
ОБНОВЛЕНИЕ 3
Я начал регистрировать родителя каждого ViewHolder
в функции onBindViewHolder()
.Как и ожидалось, я получил nulls
после nulls
после nulls
, до того, как приложение вылетело и извергло это.
04-26 21:54:50.718 27075-27075/com.hailer.hailer.dev D/MsgAdapter: Parent of ViewHolder: android.view.ViewOverlay$OverlayViewGroup{82a9fbc V.E...... .......D 0,0-1440,2168}
В конце концов, в моем безумии есть метод! Но это только ставит больше вопросов. Почему ViewOverlay
используется здесь? Как часть RecyclerView или как часть темных магов планирует лишить меня моего здравомыслия?
Sidenote
Я начал копаться в RecyclerViews
коде, чтобы проверить, могу ли я найти причину для ViewOverlay
тайны. Я обнаружил, что RecyclerView
вызывает функцию адаптеров onCreateViewHolder()
только дважды. Оба раза предоставили себя в качестве аргумента parent
для функции. Так что не повезло ... Что, черт возьми, может заставить представление элемента иметь ViewOverlay
в качестве родителя? Родитель является неизменным значением, поэтому единственный способ для ViewOverlay
быть установленным в качестве родителя - это создать что-то новое ViewHolder
и предоставить ViewOverlay
в качестве родительского объекта.
ОБНОВЛЕНИЕ 4
Иногда я поражаюсь своей собственной глупости. ViewOverlay
используется, потому что элементы анимируются. Я даже не рассматривал это как вариант, поскольку я установил itemAnimator
для RecyclerView
как null
, но по какой-то странной причине это не работает. Предметы все еще оживляются, и это вызывает всю эту шараду. Так что может быть причиной этого? (Как я решил игнорировать движущиеся элементы, я не знаю, но анимация стала очень ясной, когда я заставлял приложение загружать одно и то же изображение снова и снова, и весь список становился беспорядочным.)
My DiscussionInstanceFragment
содержит рассматриваемый RecyclerView и вложенный ConstraintLayout, который, в свою очередь, содержит EditText
для ввода пользователя и кнопку отправки.
val v = inflater.inflate(R.layout.fragment_discussion_instance, container, false)
val lm = LinearLayoutManager(context)
lm.reverseLayout = true
v.disc_instance_messages_list.layoutManager = lm
v.disc_instance_messages_list.itemAnimator = null
v.disc_instance_messages_list.adapter = mPresenter.messageAdapter
Это часть, которая обрабатывает инициализацию RecyclerView
. Я определенно устанавливаю itemAnimator
как null
, но анимация просто не остановится! Я попытался установить атрибут animateLayoutChanges
xml в корне ConstraintLayout
и RecyclerView
, но ни один из них не сработал. Стоит отметить, что я также проверил, имел ли RecyclerView
itemAnimator
в разных состояниях программы, и каждый раз, когда я проверял аниматор, он был нулевым. Так что же оживляет мою RecyclerView
?!