kopia lustrzana https://github.com/ryukoposting/Signal-Android
Fix media viewer rail items jumping around while paging.
rodzic
1e2f7f0775
commit
0fe6538ce4
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue