Fix media viewer rail items jumping around while paging.

main
Cody Henthorne 2022-12-21 16:18:42 -05:00 zatwierdzone przez Greyson Parrelli
rodzic 1e2f7f0775
commit 0fe6538ce4
2 zmienionych plików z 56 dodań i 80 usunięć

Wyświetl plik

@ -165,11 +165,14 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
{ media -> jumpViewPagerToMedia(media) },
object : ImageLoadingListener() {
override fun onAllRequestsFinished() {
crossfadeViewIn(this@apply)
val willAnimateIn = crossfadeViewIn(this@apply)
if (!willAnimateIn) {
visible = true
}
}
}
)
this.adapter = albumRailAdapter
adapter = albumRailAdapter
}
}
@ -303,32 +306,21 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
private fun bindAlbumRail(albumThumbnailMedia: List<Media>, currentItem: MediaTable.MediaRecord) {
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
if (albumThumbnailMedia.size > 1) {
val albumPosition = albumThumbnailMedia.indexOfFirst { it.uri == currentItem.attachment?.uri }
if (albumRail.visibility == GONE) {
albumRail.visibility = View.INVISIBLE
}
albumRailAdapter.currentItemPosition = albumPosition
albumRailAdapter.submitList(albumThumbnailMedia)
scrollAlbumRailToCurrentAdapterPosition()
val railItems = albumThumbnailMedia.map { MediaRailAdapter.MediaRailItem(it, it.uri == currentItem.attachment?.uri) }
albumRailAdapter.submitList(railItems) { albumRail.post { scrollAlbumRailToCurrentAdapterPosition() } }
} else {
albumRail.visibility = View.GONE
albumRailAdapter.submitList(emptyList())
albumRailAdapter.imageLoadingListener.reset()
}
}
private fun scrollAlbumRailToCurrentAdapterPosition() {
val currentItemPosition = albumRailAdapter.currentItemPosition
val currentList = albumRailAdapter.currentList
val currentItemPosition = albumRailAdapter.findSelectedItemPosition()
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
albumRail.scrollToPosition(currentItemPosition)
for (i in currentList.indices) {
val isSelected = i == currentItemPosition
val stableId = albumRailAdapter.getItemId(i)
val viewHolder = albumRail.findViewHolderForItemId(stableId) as? MediaRailAdapter.MediaRailViewHolder
viewHolder?.setSelectedItem(isSelected)
}
val offsetFromStart = (albumRail.width - individualItemWidth) / 2
val smoothScroller = OffsetSmoothScroller(requireContext(), offsetFromStart)
smoothScroller.targetPosition = currentItemPosition
@ -336,8 +328,8 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
layoutManager.startSmoothScroll(smoothScroller)
}
private fun crossfadeViewIn(view: View, duration: Long = 200) {
if (!view.isVisible && !fullscreenHelper.isSystemUiVisible) {
private fun crossfadeViewIn(view: View, duration: Long = 200): Boolean {
return if (!view.isVisible && !fullscreenHelper.isSystemUiVisible) {
val viewPropertyAnimator = view.animate()
.alpha(1f)
.setDuration(duration)
@ -353,6 +345,9 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
viewPropertyAnimator.interpolator = PathInterpolator(0.17f, 0.17f, 0f, 1f)
}
viewPropertyAnimator.start()
true
} else {
false
}
}

Wyświetl plik

@ -1,12 +1,8 @@
package org.thoughtcrime.securesms.mediapreview.mediarail
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
@ -16,45 +12,59 @@ import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.components.ThumbnailView
import org.thoughtcrime.securesms.mediasend.Media
import org.thoughtcrime.securesms.mms.GlideRequests
import org.thoughtcrime.securesms.util.adapter.StableIdGenerator
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
import org.thoughtcrime.securesms.util.adapter.mapping.MappingModel
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
import org.thoughtcrime.securesms.util.visible
import java.util.concurrent.atomic.AtomicInteger
/**
* This is the RecyclerView.Adapter for the row of thumbnails present in the media viewer screen.
*/
class MediaRailAdapter(private val glideRequests: GlideRequests, listener: RailItemListener, imageLoadingListener: ImageLoadingListener) : ListAdapter<Media, MediaRailAdapter.MediaRailViewHolder>(MediaDiffer()) {
val imageLoadingListener: ImageLoadingListener
var currentItemPosition: Int = -1
private val listener: RailItemListener
private val stableIdGenerator: StableIdGenerator<Media>
class MediaRailAdapter(
private val glideRequests: GlideRequests,
private val onRailItemSelected: (Media) -> Unit,
private val imageLoadingListener: ImageLoadingListener
) : MappingAdapter() {
init {
this.listener = listener
stableIdGenerator = StableIdGenerator()
this.imageLoadingListener = imageLoadingListener
setHasStableIds(true)
registerFactory(MediaRailItem::class.java, ::MediaRailViewHolder, R.layout.mediarail_media_item)
}
override fun onCreateViewHolder(viewGroup: ViewGroup, type: Int): MediaRailViewHolder {
return MediaRailViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.mediarail_media_item, viewGroup, false))
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
super.onAttachedToRecyclerView(recyclerView)
recyclerView.itemAnimator = null
}
override fun onBindViewHolder(viewHolder: MediaRailViewHolder, i: Int) {
viewHolder.bind(getItem(i), i == currentItemPosition, glideRequests, listener, imageLoadingListener)
override fun submitList(list: List<MappingModel<*>>?) {
super.submitList(list)
if (list?.isEmpty() == true) {
imageLoadingListener.reset()
}
}
override fun onViewRecycled(holder: MediaRailViewHolder) {
holder.recycle()
override fun submitList(list: List<MappingModel<*>>?, commitCallback: Runnable?) {
super.submitList(list, commitCallback)
if (list?.isEmpty() == true) {
imageLoadingListener.reset()
}
}
override fun getItemId(position: Int): Long {
return stableIdGenerator.getId(getItem(position))
fun findSelectedItemPosition(): Int {
return indexOfFirst(MediaRailItem::class.java) { it.isSelected }.coerceAtLeast(0)
}
class MediaRailViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
data class MediaRailItem(val media: Media, val isSelected: Boolean) : MappingModel<MediaRailItem> {
override fun areItemsTheSame(newItem: MediaRailItem): Boolean {
return media.uri == newItem.media.uri
}
override fun areContentsTheSame(newItem: MediaRailItem): Boolean {
return this == newItem
}
}
private inner class MediaRailViewHolder(itemView: View) : MappingViewHolder<MediaRailItem>(itemView) {
private val image: ThumbnailView
private val outline: View
private val captionIndicator: View
@ -67,34 +77,15 @@ class MediaRailAdapter(private val glideRequests: GlideRequests, listener: RailI
overlay = itemView.findViewById(R.id.rail_item_overlay)
}
fun bind(
media: Media,
isCurrentlySelected: Boolean,
glideRequests: GlideRequests,
railItemListener: RailItemListener,
listener: ImageLoadingListener
) {
listener.onRequest()
image.setImageResource(glideRequests, media.uri, 0, 0, false, listener)
image.setOnClickListener { railItemListener.onRailItemClicked(media) }
captionIndicator.visibility = if (media.caption.isPresent) View.VISIBLE else View.GONE
setSelectedItem(isCurrentlySelected)
override fun bind(model: MediaRailItem) {
imageLoadingListener.onRequest()
image.setImageResource(glideRequests, model.media.uri, 0, 0, false, imageLoadingListener)
image.setOnClickListener { onRailItemSelected(model.media) }
captionIndicator.visibility = if (model.media.caption.isPresent) View.VISIBLE else View.GONE
outline.visible = model.isSelected
overlay.setImageResource(if (model.isSelected) R.drawable.mediapreview_rail_item_overlay_selected else R.drawable.mediapreview_rail_item_overlay_unselected)
}
fun recycle() {
image.setOnClickListener(null)
}
fun setSelectedItem(isActive: Boolean) {
outline.visible = isActive
val resId = if (isActive) R.drawable.mediapreview_rail_item_overlay_selected else R.drawable.mediapreview_rail_item_overlay_unselected
overlay.setImageResource(resId)
}
}
fun interface RailItemListener {
fun onRailItemClicked(media: Media)
}
abstract class ImageLoadingListener : RequestListener<Drawable?> {
@ -125,14 +116,4 @@ class MediaRailAdapter(private val glideRequests: GlideRequests, listener: RailI
abstract fun onAllRequestsFinished()
}
class MediaDiffer : DiffUtil.ItemCallback<Media>() {
override fun areItemsTheSame(oldItem: Media, newItem: Media): Boolean {
return oldItem.uri == newItem.uri
}
override fun areContentsTheSame(oldItem: Media, newItem: Media): Boolean {
return oldItem == newItem
}
}
}