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) },
|
{ media -> jumpViewPagerToMedia(media) },
|
||||||
object : ImageLoadingListener() {
|
object : ImageLoadingListener() {
|
||||||
override fun onAllRequestsFinished() {
|
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) {
|
private fun bindAlbumRail(albumThumbnailMedia: List<Media>, currentItem: MediaTable.MediaRecord) {
|
||||||
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
|
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
|
||||||
if (albumThumbnailMedia.size > 1) {
|
if (albumThumbnailMedia.size > 1) {
|
||||||
val albumPosition = albumThumbnailMedia.indexOfFirst { it.uri == currentItem.attachment?.uri }
|
|
||||||
if (albumRail.visibility == GONE) {
|
if (albumRail.visibility == GONE) {
|
||||||
albumRail.visibility = View.INVISIBLE
|
albumRail.visibility = View.INVISIBLE
|
||||||
}
|
}
|
||||||
|
val railItems = albumThumbnailMedia.map { MediaRailAdapter.MediaRailItem(it, it.uri == currentItem.attachment?.uri) }
|
||||||
albumRailAdapter.currentItemPosition = albumPosition
|
albumRailAdapter.submitList(railItems) { albumRail.post { scrollAlbumRailToCurrentAdapterPosition() } }
|
||||||
albumRailAdapter.submitList(albumThumbnailMedia)
|
|
||||||
scrollAlbumRailToCurrentAdapterPosition()
|
|
||||||
} else {
|
} else {
|
||||||
albumRail.visibility = View.GONE
|
albumRail.visibility = View.GONE
|
||||||
albumRailAdapter.submitList(emptyList())
|
albumRailAdapter.submitList(emptyList())
|
||||||
albumRailAdapter.imageLoadingListener.reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scrollAlbumRailToCurrentAdapterPosition() {
|
private fun scrollAlbumRailToCurrentAdapterPosition() {
|
||||||
val currentItemPosition = albumRailAdapter.currentItemPosition
|
val currentItemPosition = albumRailAdapter.findSelectedItemPosition()
|
||||||
val currentList = albumRailAdapter.currentList
|
|
||||||
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
|
val albumRail: RecyclerView = binding.mediaPreviewPlaybackControls.recyclerView
|
||||||
albumRail.scrollToPosition(currentItemPosition)
|
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 offsetFromStart = (albumRail.width - individualItemWidth) / 2
|
||||||
val smoothScroller = OffsetSmoothScroller(requireContext(), offsetFromStart)
|
val smoothScroller = OffsetSmoothScroller(requireContext(), offsetFromStart)
|
||||||
smoothScroller.targetPosition = currentItemPosition
|
smoothScroller.targetPosition = currentItemPosition
|
||||||
|
@ -336,8 +328,8 @@ class MediaPreviewV2Fragment : LoggingFragment(R.layout.fragment_media_preview_v
|
||||||
layoutManager.startSmoothScroll(smoothScroller)
|
layoutManager.startSmoothScroll(smoothScroller)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun crossfadeViewIn(view: View, duration: Long = 200) {
|
private fun crossfadeViewIn(view: View, duration: Long = 200): Boolean {
|
||||||
if (!view.isVisible && !fullscreenHelper.isSystemUiVisible) {
|
return if (!view.isVisible && !fullscreenHelper.isSystemUiVisible) {
|
||||||
val viewPropertyAnimator = view.animate()
|
val viewPropertyAnimator = view.animate()
|
||||||
.alpha(1f)
|
.alpha(1f)
|
||||||
.setDuration(duration)
|
.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.interpolator = PathInterpolator(0.17f, 0.17f, 0f, 1f)
|
||||||
}
|
}
|
||||||
viewPropertyAnimator.start()
|
viewPropertyAnimator.start()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
package org.thoughtcrime.securesms.mediapreview.mediarail
|
package org.thoughtcrime.securesms.mediapreview.mediarail
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.load.DataSource
|
import com.bumptech.glide.load.DataSource
|
||||||
import com.bumptech.glide.load.engine.GlideException
|
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.components.ThumbnailView
|
||||||
import org.thoughtcrime.securesms.mediasend.Media
|
import org.thoughtcrime.securesms.mediasend.Media
|
||||||
import org.thoughtcrime.securesms.mms.GlideRequests
|
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 org.thoughtcrime.securesms.util.visible
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the RecyclerView.Adapter for the row of thumbnails present in the media viewer screen.
|
* 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()) {
|
class MediaRailAdapter(
|
||||||
val imageLoadingListener: ImageLoadingListener
|
private val glideRequests: GlideRequests,
|
||||||
|
private val onRailItemSelected: (Media) -> Unit,
|
||||||
var currentItemPosition: Int = -1
|
private val imageLoadingListener: ImageLoadingListener
|
||||||
|
) : MappingAdapter() {
|
||||||
private val listener: RailItemListener
|
|
||||||
private val stableIdGenerator: StableIdGenerator<Media>
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.listener = listener
|
registerFactory(MediaRailItem::class.java, ::MediaRailViewHolder, R.layout.mediarail_media_item)
|
||||||
stableIdGenerator = StableIdGenerator()
|
|
||||||
this.imageLoadingListener = imageLoadingListener
|
|
||||||
setHasStableIds(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(viewGroup: ViewGroup, type: Int): MediaRailViewHolder {
|
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||||
return MediaRailViewHolder(LayoutInflater.from(viewGroup.context).inflate(R.layout.mediarail_media_item, viewGroup, false))
|
super.onAttachedToRecyclerView(recyclerView)
|
||||||
|
recyclerView.itemAnimator = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(viewHolder: MediaRailViewHolder, i: Int) {
|
override fun submitList(list: List<MappingModel<*>>?) {
|
||||||
viewHolder.bind(getItem(i), i == currentItemPosition, glideRequests, listener, imageLoadingListener)
|
super.submitList(list)
|
||||||
|
if (list?.isEmpty() == true) {
|
||||||
|
imageLoadingListener.reset()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewRecycled(holder: MediaRailViewHolder) {
|
override fun submitList(list: List<MappingModel<*>>?, commitCallback: Runnable?) {
|
||||||
holder.recycle()
|
super.submitList(list, commitCallback)
|
||||||
|
if (list?.isEmpty() == true) {
|
||||||
|
imageLoadingListener.reset()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemId(position: Int): Long {
|
fun findSelectedItemPosition(): Int {
|
||||||
return stableIdGenerator.getId(getItem(position))
|
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 image: ThumbnailView
|
||||||
private val outline: View
|
private val outline: View
|
||||||
private val captionIndicator: 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)
|
overlay = itemView.findViewById(R.id.rail_item_overlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(
|
override fun bind(model: MediaRailItem) {
|
||||||
media: Media,
|
imageLoadingListener.onRequest()
|
||||||
isCurrentlySelected: Boolean,
|
image.setImageResource(glideRequests, model.media.uri, 0, 0, false, imageLoadingListener)
|
||||||
glideRequests: GlideRequests,
|
image.setOnClickListener { onRailItemSelected(model.media) }
|
||||||
railItemListener: RailItemListener,
|
captionIndicator.visibility = if (model.media.caption.isPresent) View.VISIBLE else View.GONE
|
||||||
listener: ImageLoadingListener
|
|
||||||
) {
|
outline.visible = model.isSelected
|
||||||
listener.onRequest()
|
overlay.setImageResource(if (model.isSelected) R.drawable.mediapreview_rail_item_overlay_selected else R.drawable.mediapreview_rail_item_overlay_unselected)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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?> {
|
abstract class ImageLoadingListener : RequestListener<Drawable?> {
|
||||||
|
@ -125,14 +116,4 @@ class MediaRailAdapter(private val glideRequests: GlideRequests, listener: RailI
|
||||||
|
|
||||||
abstract fun onAllRequestsFinished()
|
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