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

时间:2018-04-20 15:06:09

标签: android animated-gif

背景

我有一个小型动态壁纸应用,我想添加对它的支持以显示GIF动画。

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

然而,对于他们两个,我无法找到如何在其所拥有的空间中很好地拟合GIF动画的内容,这意味着以下任何一个:

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

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

    我发现了什么

    有一个解决方案可以为您提供GifDrawable(here),您可以在ImageView中使用它,但在某些情况下它看起来很慢,我无法弄清楚如何在LiveWallpaper中使用它然后适合它。

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

    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
            }
        }
    }
    

    在视图中处理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()
            }
        }
    }
    

    问题

    1. 鉴于GIF动画,如何在上述每种方式中进行缩放?
    2. 两种情况都可以有单一解决方案吗?
    3. 是否可以使用GifDrawable库(或任何其他可绘制的东西)作为动态壁纸而不是Movie类?如果是这样,怎么样?
    4. 编辑:在找到如何缩放2种之后,我仍然需要知道如何根据第三种类型进行缩放,并且还想知道为什么它在方向改变后不断崩溃,以及它为什么不总是立即显示预览。

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

      项目可用here

3 个答案:

答案 0 :(得分:6)

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

declare @g varchar (50)
 set @g=char(10)
 select isnumeric(@g),@g, isnumeric(replace(replace(@g,char(13),char(10)),char(10),''))

答案 1 :(得分:3)

好的,我想我知道如何缩放内容。不确定为什么有时应用程序仍然会因方向改变而崩溃,以及为什么有时应用程序不会立即显示预览。

项目可用here

对于中部内侧,代码为:

    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())
    }

对于中心作物,代码为:

    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())
    }

对于fit-center,我可以使用它:

                    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
                    ...

答案 2 :(得分:2)

更改影片的宽度和高度:

在movie.draw

之前在onDraw方法中添加此代码
canvas.scale((float)this.getWidth() / (float)movie.width(),(float)this.getHeight() /      (float)movie.height());

canvas.scale(1.9f, 1.21f); //this changes according to screen size

缩放以填充并缩放以适合:

对此已经有了一个很好的答案:

point per inch