如何在视图和动态壁纸中适合 GIF 动画的内容?
Posted
技术标签:
【中文标题】如何在视图和动态壁纸中适合 GIF 动画的内容?【英文标题】:How to fit content of GIF animation in View and in live wallpaper? 【发布时间】:2018-10-01 08:22:59 【问题描述】:背景
我有一个小型动态壁纸应用程序,我想添加对它的支持以显示 GIF 动画。
为此,我找到了各种解决方案。有在视图中显示 GIF 动画的解决方案 (here),甚至还有在动态壁纸中显示的解决方案 (here)。
但是,对于他们两个,我都找不到如何将 GIF 动画的内容很好地适应它所拥有的空间,这意味着以下任何一种:
-
center-crop - 适合 100% 的容器(在本例中为屏幕),在需要时在侧面(顶部和底部或左侧和右侧)进行裁剪。不拉伸任何东西。这意味着内容看起来不错,但可能不会全部显示。
fit-center - 拉伸以适应宽度/高度
center-inside - 设置为原始大小,居中,只有在太大时才会拉伸以适应宽度/高度。
问题
这些都不是关于 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()
问题
-
给定一个 GIF 动画,如何以上述方式对其进行缩放?
是否可以为这两种情况提供一个解决方案?
是否可以将 GifDrawable 库(或任何其他可绘制对象)用于动态壁纸,而不是使用 Movie 类?如果有,怎么做?
编辑:找到如何缩放 2 种后,我仍然需要知道如何根据第三种类型进行缩放,并且还想知道为什么它在方向更改后一直崩溃,以及为什么它并不总是显示立即预览。
我还想知道在这里显示 GIF 动画的最佳方式是什么,因为目前我只是刷新画布 ~60fps(每 2 帧之间等待 1000/60),而不考虑文件中的内容。
项目可用here。
【问题讨论】:
【参考方案1】:如果您的项目中有Glide,您可以轻松加载 Gif,因为它为您的 ImageView 提供绘图 GIF,并且支持许多缩放选项(如中心或给定宽度和...)。
Glide.with(context)
.load(imageUrl or resourceId)
.asGif()
.fitCenter() //or other scaling options as you like
.into(imageView);
【讨论】:
我可以使用 Glide 制作 GIF 动画吗?此外,您的代码没有意义,因为这里没有 imageView (甚至没有布局)。这是一个动态壁纸。请在动态壁纸中展示如何做到这一点 是的,您可以将 Glide 用于 GIF 以及静态图像。对于壁纸部分,您可以使用回调来检索 GIF 并使用 this answer 设置壁纸,而不是为into
部分使用 imageView。
我看不出 Glide 如何适合这个链接。
Glide 可以加载和调整 GIF 的大小并在它的回调中(使用.into(Target);
,您可以使用我提供的链接将 GIF 传递给 WallpaperManager。
请显示完整的示例代码。我记得我试过这个,它对我不起作用。另外,您确定使用 Glide 可以很好地处理 GIF 吗?即使文件很大也没有内存问题?【参考方案2】:
好的,我想我知道如何缩放内容了。不知道为什么应用程序有时在方向更改时仍然崩溃,以及为什么应用程序有时不立即显示预览。
项目可用here。
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())
对于center-crop,代码为:
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
...
【讨论】:
【参考方案3】:改变影片的宽度和高度:
在onDraw方法中movie.draw之前添加这段代码
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
缩放以填充和缩放以适应:
已经有一个很好的答案:
https://***.com/a/38898699/5675325
【讨论】:
这是哪种缩放类型?你能告诉我其他的怎么做吗? 再一次,我提到的缩放类型中的哪一种?对我来说,您似乎选择了拉伸它,这意味着没有保存纵横比,这意味着我所写的内容都不是我希望实现的。我不应该也让画布移动吗?关于链接,他们称之为“缩放以适应”的链接看起来像是中心,但它把内容放在了错误的位置(底部而不是纵向中心)。称为“缩放填充”的是中心裁剪,但我什至看不到它被绘制(在肖像上)。请尝试一下。以上是关于如何在视图和动态壁纸中适合 GIF 动画的内容?的主要内容,如果未能解决你的问题,请参考以下文章