如何在视图和动态壁纸中拟合GIF动画的内容?

and*_*per 30 android animated-gif

背景

我有一个小型动态壁纸应用程序,我想添加支持它来显示GIF动画.

为此,我找到了各种解决方案.有一种在视图(这里)中显示GIF动画的解决方案,甚至还有一个解决方案,可以在动态壁纸(这里)中显示它.

但是,对于它们两者,我无法找到如何在其所拥有的空间中很好地拟合GIF动画的内容,这意味着以下任何一种情况:

  1. 中心裁剪 - 适合100%的容器(在这种情况下为屏幕),在需要时在侧面(顶部和底部或左侧和右侧)裁剪.不伸展任何东西.这意味着内容似乎很好,但并非所有内容都可能显示出来.
  2. fit-center - 拉伸以适合宽度/高度
  3. center-inside - 设置为原始大小,居中,并且只有在太大时才能拉伸以适合宽度/高度.

问题

这些都不是关于ImageView的,所以我不能只使用scaleType属性.

我发现了什么

有一个解决方案可以为你提供一个GifDrawable(这里),你可以在ImageView中使用它,但在某些情况下它似乎很慢,我无法弄清楚如何在LiveWallpaper中使用它然后适合它.

LiveWallpaper GIF处理的主要代码就是这样(这里):

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在视图中处理GIF动画的主要代码是这样的:

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()
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题

  1. 给定GIF动画,我如何在上述每种方式中进行缩放?
  2. 是否可以为这两种情况提供单一解决方案?
  3. 是否可以使用GifDrawable库(或其他任何可绘制的)来制作动态壁纸而不是Movie类?如果是这样,怎么样?

编辑:在找到如何缩放2种之后,我仍然需要知道如何根据第三种类型进行缩放,并且还想知道为什么它在方向改变后不断崩溃,以及为什么它不总是立即显示预览.

我也想知道在这里展示GIF动画的最佳方式是什么,因为目前我只刷新画布~60fps(每2帧之间等待1000/60),而不考虑文件中的内容.

项目可在此处获得.

Adi*_*rzi 6

如果您的项目中有Glide,您可以轻松加载Gif,因为它为您的ImageView提供绘图GIF并支持许多缩放选项(如中心或给定宽度和......).

Glide.with(context)
    .load(imageUrl or resourceId)
    .asGif()
    .fitCenter() //or other scaling options as you like
    .into(imageView);
Run Code Online (Sandbox Code Playgroud)


and*_*per 3

好吧,我想我知道如何扩展内容了。但不确定为什么应用程序有时在方向改变时仍然崩溃,以及为什么应用程序有时不立即显示预览。

项目可在此处获取。

对于center-inside,代码是:

    private fun draw() {
        if (!isVisible)
            return
        val canvas = holder!!.lockCanvas() ?: return
        canvas.save()
        //center-inside
        val scale = Math.min(canvas.width.toFloat() / movie.width().toFloat(), canvas.height.toFloat() / movie.height().toFloat());
        val x = (canvas.width.toFloat() / 2f) - (movie.width().toFloat() / 2f) * scale;
        val y = (canvas.height.toFloat() / 2f) - (movie.height().toFloat() / 2f) * scale;
        canvas.translate(x, y)
        canvas.scale(scale, scale)
        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())
    }
Run Code Online (Sandbox Code Playgroud)

对于中心裁剪,代码是:

    private fun draw() {
        if (!isVisible)
            return
        val canvas = holder!!.lockCanvas() ?: return
        canvas.save()
        //center crop
        val scale = Math.max(canvas.width.toFloat() / movie.width().toFloat(), canvas.height.toFloat() / movie.height().toFloat());
        val x = (canvas.width.toFloat() / 2f) - (movie.width().toFloat() / 2f) * scale;
        val y = (canvas.height.toFloat() / 2f) - (movie.height().toFloat() / 2f) * scale;
        canvas.translate(x, y)
        canvas.scale(scale, scale)
        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())
    }
Run Code Online (Sandbox Code Playgroud)

对于健身中心,我可以使用这个:

                    val canvasWidth = canvas.width.toFloat()
                    val canvasHeight = canvas.height.toFloat()
                    val bitmapWidth = curBitmap.width.toFloat()
                    val bitmapHeight = curBitmap.height.toFloat()
                    val scaleX = canvasWidth / bitmapWidth
                    val scaleY = canvasHeight / bitmapHeight
                    scale = if (scaleX * curBitmap.height > canvas.height) scaleY else scaleX
                    x = (canvasWidth / 2f) - (bitmapWidth / 2f) * scale
                    y = (canvasHeight / 2f) - (bitmapHeight / 2f) * scale
                    ...
Run Code Online (Sandbox Code Playgroud)