Contexte
J'ai une petite application de fond d'écran en direct, et je veux ajouter un support pour qu'elle puisse afficher des animations GIF.
Pour cela, j'ai trouvé différentes solutions. Il y a la solution de montrer une animation GIF dans une vue ( aquí ), et il existe même une solution pour l'afficher dans un fond d'écran en direct ( aquí ).
Cependant, dans les deux cas, je n'arrive pas à trouver comment faire tenir le contenu de l'animation GIF dans l'espace disponible, c'est-à-dire dans l'une des situations suivantes :
- center-crop - s'adapte à 100% du conteneur (l'écran dans ce cas), en recadrant sur les côtés (haut et bas ou gauche et droite) si nécessaire. N'étire rien. Cela signifie que le contenu semble correct, mais qu'il ne sera peut-être pas entièrement affiché.
- fit-center - s'étirer pour s'adapter à la largeur/hauteur
- center-inside - défini comme la taille originale, centré, et étiré pour s'adapter à la largeur/hauteur uniquement s'il est trop grand.
Le problème
Aucun d'entre eux ne concerne ImageView, je ne peux donc pas simplement utiliser l'attribut scaleType.
Ce que j'ai trouvé
Il existe une solution qui vous donne un GifDrawable ( aquí ), que vous pouvez utiliser dans ImageView, mais il semble que ce soit assez lent dans certains cas, et je n'arrive pas à trouver comment l'utiliser dans LiveWallpaper et ensuite l'adapter.
Le code principal de la manipulation des GIF de LiveWallpaper est le suivant ( aquí ) :
class GIFWallpaperService : WallpaperService() {
override fun onCreateEngine(): WallpaperService.Engine {
val movie = Movie.decodeStream(resources.openRawResource(R.raw.cinemagraphs))
return GIFWallpaperEngine(movie)
}
private inner class GIFWallpaperEngine(private val movie: Movie) : WallpaperService.Engine() {
private val frameDuration = 20
private var holder: SurfaceHolder? = null
private var visible: Boolean = false
private val handler: Handler = Handler()
private val drawGIF = Runnable { draw() }
private fun draw() {
if (visible) {
val canvas = holder!!.lockCanvas()
canvas.save()
movie.draw(canvas, 0f, 0f)
canvas.restore()
holder!!.unlockCanvasAndPost(canvas)
movie.setTime((System.currentTimeMillis() % movie.duration()).toInt())
handler.removeCallbacks(drawGIF)
handler.postDelayed(drawGIF, frameDuration.toLong())
}
}
override fun onVisibilityChanged(visible: Boolean) {
this.visible = visible
if (visible)
handler.post(drawGIF)
else
handler.removeCallbacks(drawGIF)
}
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacks(drawGIF)
}
override fun onCreate(surfaceHolder: SurfaceHolder) {
super.onCreate(surfaceHolder)
this.holder = surfaceHolder
}
}
}
Le code principal pour gérer l'animation GIF dans une vue est le suivant :
class CustomGifView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {
private var gifMovie: Movie? = null
var movieWidth: Int = 0
var movieHeight: Int = 0
var movieDuration: Long = 0
var mMovieStart: Long = 0
init {
isFocusable = true
val gifInputStream = context.resources.openRawResource(R.raw.test)
gifMovie = Movie.decodeStream(gifInputStream)
movieWidth = gifMovie!!.width()
movieHeight = gifMovie!!.height()
movieDuration = gifMovie!!.duration().toLong()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
setMeasuredDimension(movieWidth, movieHeight)
}
override fun onDraw(canvas: Canvas) {
val now = android.os.SystemClock.uptimeMillis()
if (mMovieStart == 0L) { // first time
mMovieStart = now
}
if (gifMovie != null) {
var dur = gifMovie!!.duration()
if (dur == 0) {
dur = 1000
}
val relTime = ((now - mMovieStart) % dur).toInt()
gifMovie!!.setTime(relTime)
gifMovie!!.draw(canvas, 0f, 0f)
invalidate()
}
}
}
Les questions
- Étant donné une animation GIF, comment puis-je la mettre à l'échelle de chacune des manières ci-dessus ?
- Est-il possible d'avoir une solution unique pour les deux cas ?
- Est-il possible d'utiliser la bibliothèque GifDrawable (ou tout autre dessinable d'ailleurs) pour le fond d'écran vivant, au lieu de la classe Movie ? Si oui, comment ?
EDIT : après avoir trouvé comment mettre à l'échelle pour 2 types, j'ai encore besoin de savoir comment mettre à l'échelle selon le troisième type, et je veux aussi savoir pourquoi il continue à planter après les changements d'orientation, et pourquoi il ne montre pas toujours l'aperçu tout de suite.
J'aimerais aussi savoir quelle est la meilleure façon d'afficher l'animation GIF ici, parce qu'actuellement je rafraîchis simplement la toile ~60fps (1000/60 d'attente entre chaque 2 images), sans tenir compte de ce qu'il y a dans le fichier.
Le projet est disponible aquí .