如何在视图和动态壁纸中适应 GIF 动画的内容? [英] How to fit content of GIF animation in View and in live wallpaper?

查看:29
本文介绍了如何在视图和动态壁纸中适应 GIF 动画的内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a small live wallpaper app, that I want to add support for it to show GIF animations.

为此,我找到了各种解决方案.有在视图中显示 GIF 动画的解决方案(此处),并且有甚至是在动态壁纸中显示它的解决方案(此处).

For this, I've found various solutions. There is the solution of showing a GIF animation in a view (here), and there is even a solution for showing it in a live wallpaper (here).

但是,对于他们两个,我都找不到如何在其拥有的空间中很好地适应 GIF 动画的内容,这意味着以下任何一项:

However, for both of them, I can't find how to fit the content of the GIF animation nicely in the space it has, meaning any of the following:

  1. center-crop - 适合 100% 的容器(在本例中为屏幕),在需要时在侧面(顶部和底部或左侧和右侧)进行裁剪.不会拉伸任何东西.这意味着内容看起来不错,但可能无法显示所有内容.
  2. fit-center - 拉伸以适应宽度/高度
  3. center-inside - 设置为原始大小,居中,并仅在太大时拉伸以适应宽度/高度.

问题

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

The problem

None of those is actually about ImageView, so I can't just use the scaleType attribute.

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

There is a solution that gives you a GifDrawable (here), which you can use in ImageView, but it seems it's pretty slow in some cases, and I can't figure out how to use it in LiveWallpaper and then fit it.

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

The main code of the LiveWallpaper GIF handling is as such (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 动画的主要代码如下:

The main code for handling GIF animation in a view is as such:

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 类?如果是这样,怎么办?

<小时>

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


after finding how to scale for 2 kinds, I still need to know how to scale according to the third type, and also want to know why it keeps crashing after orientation changes, and why it doesn't always show the preview right away.

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

I'd also like to know what's the best way to show the GIF animation here, because currently I just refresh the canvas ~60fps (1000/60 waiting between each 2 frames), without consideration of what's in the file.

项目在此处可用.

推荐答案

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

OK I think I got how to scale the content. Not sure though why the app still crashes upon orientation change sometimes, and why the app doesn't show the preview right away sometimes.

项目在此处可用.

对于center-inside,代码为:

For center-inside, the code is:

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

对于center-crop,代码是:

For center-crop, the code is:

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

对于适合中心,我可以使用这个:

for fit-center, I can use this:

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

这篇关于如何在视图和动态壁纸中适应 GIF 动画的内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆