Change cover placeholder (#6756)

This commit is contained in:
Ivan Iskandar 2022-03-10 05:26:55 +07:00 committed by GitHub
parent b9fd01315b
commit 869424cd16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 20 additions and 118 deletions

View File

@ -2,14 +2,11 @@ package eu.kanade.tachiyomi.ui.browse.source.browse
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.dispose import coil.dispose
import coil.imageLoader
import coil.request.ImageRequest
import coil.transition.CrossfadeTransition
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding import eu.kanade.tachiyomi.databinding.SourceComfortableGridItemBinding
import eu.kanade.tachiyomi.widget.StateImageViewTarget import eu.kanade.tachiyomi.util.view.loadAutoPause
/** /**
* Class used to hold the displayed data of a manga in the catalogue, like the cover or the title. * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title.
@ -49,16 +46,8 @@ class SourceComfortableGridHolder(
override fun setImage(manga: Manga) { override fun setImage(manga: Manga) {
binding.thumbnail.dispose() binding.thumbnail.dispose()
if (!manga.thumbnail_url.isNullOrEmpty()) { binding.thumbnail.loadAutoPause(manga) {
val crossfadeDuration = binding.root.context.imageLoader.defaults.transitionFactory.let { setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
if (it is CrossfadeTransition.Factory) it.durationMillis else 0
}
val request = ImageRequest.Builder(binding.root.context)
.data(manga)
.setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
.target(StateImageViewTarget(binding.thumbnail, binding.progress, crossfadeDuration))
.build()
itemView.context.imageLoader.enqueue(request)
} }
} }
} }

View File

@ -2,14 +2,11 @@ package eu.kanade.tachiyomi.ui.browse.source.browse
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.dispose import coil.dispose
import coil.imageLoader
import coil.request.ImageRequest
import coil.transition.CrossfadeTransition
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.SourceCompactGridItemBinding import eu.kanade.tachiyomi.databinding.SourceCompactGridItemBinding
import eu.kanade.tachiyomi.widget.StateImageViewTarget import eu.kanade.tachiyomi.util.view.loadAutoPause
/** /**
* Class used to hold the displayed data of a manga in the catalogue, like the cover or the title. * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title.
@ -49,16 +46,8 @@ class SourceCompactGridHolder(
override fun setImage(manga: Manga) { override fun setImage(manga: Manga) {
binding.thumbnail.dispose() binding.thumbnail.dispose()
if (!manga.thumbnail_url.isNullOrEmpty()) { binding.thumbnail.loadAutoPause(manga) {
val crossfadeDuration = binding.root.context.imageLoader.defaults.transitionFactory.let { setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
if (it is CrossfadeTransition.Factory) it.durationMillis else 0
}
val request = ImageRequest.Builder(binding.root.context)
.data(manga)
.setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
.target(StateImageViewTarget(binding.thumbnail, binding.progress, crossfadeDuration))
.build()
itemView.context.imageLoader.enqueue(request)
} }
} }
} }

View File

@ -3,14 +3,11 @@ package eu.kanade.tachiyomi.ui.browse.source.globalsearch
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import coil.dispose import coil.dispose
import coil.imageLoader
import coil.request.ImageRequest
import coil.transition.CrossfadeTransition
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardItemBinding import eu.kanade.tachiyomi.databinding.GlobalSearchControllerCardItemBinding
import eu.kanade.tachiyomi.widget.StateImageViewTarget import eu.kanade.tachiyomi.util.view.loadAutoPause
class GlobalSearchCardHolder(view: View, adapter: GlobalSearchCardAdapter) : class GlobalSearchCardHolder(view: View, adapter: GlobalSearchCardAdapter) :
FlexibleViewHolder(view, adapter) { FlexibleViewHolder(view, adapter) {
@ -54,16 +51,8 @@ class GlobalSearchCardHolder(view: View, adapter: GlobalSearchCardAdapter) :
fun setImage(manga: Manga) { fun setImage(manga: Manga) {
binding.cover.dispose() binding.cover.dispose()
if (!manga.thumbnail_url.isNullOrEmpty()) { binding.cover.loadAutoPause(manga) {
val crossfadeDuration = itemView.context.imageLoader.defaults.transitionFactory.let { setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
if (it is CrossfadeTransition.Factory) it.durationMillis else 0
}
val request = ImageRequest.Builder(itemView.context)
.data(manga)
.setParameter(MangaCoverFetcher.USE_CUSTOM_COVER, false)
.target(StateImageViewTarget(binding.cover, binding.progress, crossfadeDuration))
.build()
itemView.context.imageLoader.enqueue(request)
} }
} }
} }

View File

@ -1,11 +1,14 @@
package eu.kanade.tachiyomi.util.view package eu.kanade.tachiyomi.util.view
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
import android.graphics.drawable.ColorDrawable
import android.widget.ImageView import android.widget.ImageView
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.ColorUtils
import coil.ImageLoader import coil.ImageLoader
import coil.imageLoader import coil.imageLoader
import coil.load import coil.load
@ -38,20 +41,22 @@ fun ImageView.loadAutoPause(
loader: ImageLoader = context.imageLoader, loader: ImageLoader = context.imageLoader,
builder: ImageRequest.Builder.() -> Unit = {} builder: ImageRequest.Builder.() -> Unit = {}
) { ) {
// Build the original request so we can add on our success listener
load(data, loader) { load(data, loader) {
val placeholderColor = ColorUtils.setAlphaComponent(Color.GRAY, 0x1F) // 12% gray
placeholder(ColorDrawable(placeholderColor))
// Build the original request so we can add on our success listener // Build the original request so we can add on our success listener
val originalBuild = apply(builder).build() val originalListener = apply(builder).build().listener
listener( listener(
onSuccess = { request, metadata -> onSuccess = { request, metadata ->
(request.target as? ImageViewTarget)?.drawable.let { (request.target as? ImageViewTarget)?.drawable.let {
if (it is Animatable && context.animatorDurationScale == 0f) it.stop() if (it is Animatable && context.animatorDurationScale == 0f) it.stop()
} }
originalBuild.listener?.onSuccess(request, metadata) originalListener?.onSuccess(request, metadata)
}, },
onStart = { request -> originalBuild.listener?.onStart(request) }, onStart = { request -> originalListener?.onStart(request) },
onCancel = { request -> originalBuild.listener?.onCancel(request) }, onCancel = { request -> originalListener?.onCancel(request) },
onError = { request, throwable -> originalBuild.listener?.onError(request, throwable) } onError = { request, throwable -> originalListener?.onError(request, throwable) }
) )
} }
} }

View File

@ -1,43 +0,0 @@
package eu.kanade.tachiyomi.widget
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import androidx.core.view.isVisible
import coil.drawable.CrossfadeDrawable
import coil.target.ImageViewTarget
/**
* A Coil target to display an image with an optional view to show while loading.
*
* @param target the view where the image will be loaded
* @param progress the view to show when the image is loading.
* @param crossfadeDuration duration in millisecond to crossfade the result drawable
*/
class StateImageViewTarget(
private val target: ImageView,
private val progress: View,
private val crossfadeDuration: Int = 0
) : ImageViewTarget(target) {
override fun onStart(placeholder: Drawable?) {
progress.isVisible = true
}
override fun onSuccess(result: Drawable) {
progress.isVisible = false
if (crossfadeDuration > 0) {
val crossfadeResult = CrossfadeDrawable(target.drawable, result, durationMillis = crossfadeDuration)
target.setImageDrawable(crossfadeResult)
crossfadeResult.start()
} else {
target.setImageDrawable(result)
}
}
override fun onError(error: Drawable?) {
progress.isVisible = false
if (error != null) {
target.setImageDrawable(error)
}
}
}

View File

@ -9,19 +9,6 @@
android:foreground="@drawable/library_item_selector_overlay" android:foreground="@drawable/library_item_selector_overlay"
android:padding="4dp"> android:padding="4dp">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progress"
style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/thumbnail"
app:layout_constraintEnd_toEndOf="@+id/thumbnail"
app:layout_constraintStart_toStartOf="@+id/thumbnail"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<com.google.android.material.imageview.ShapeableImageView <com.google.android.material.imageview.ShapeableImageView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
android:layout_width="0dp" android:layout_width="0dp"

View File

@ -55,18 +55,4 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
tools:text="Sample name" /> tools:text="Sample name" />
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progress"
style="@style/Widget.Tachiyomi.CircularProgressIndicator.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>