Как разместить содержимое анимации GIF в режиме просмотра и в живых обоях?
Фон
У меня есть небольшое приложение для живых обоев, которое я хочу добавить для него, чтобы показать анимацию GIF.
Для этого я нашел различные решения. Существует решение показать анимацию GIF в представлении ( здесь), и есть даже решение для показа его в живых обоях ( здесь).
Тем не менее, для обоих из них я не могу найти, как хорошо подходить к содержанию анимации GIF в пространстве, которое у него есть, что означает любое из следующего:
- center-crop - подходит для 100% контейнера (в этом случае экран), обрезание по сторонам (сверху и снизу или слева и справа) при необходимости. Ничего не растягивает. Это означает, что контент кажется прекрасным, но не все это может быть показано.
- fit-center - растяжка, чтобы соответствовать ширине/высоте
- center-inside - устанавливается как оригинальный размер, центрируется и растягивается, чтобы соответствовать ширине/высоте, только если он слишком большой.
Эта проблема
Ни один из них не относится к ImageView, поэтому я не могу просто использовать атрибут scaleType.
Что я нашел
Существует решение, которое дает вам GifDrawable ( здесь), который вы можете использовать в ImageView, но в некоторых случаях кажется довольно медленным, и я не могу понять, как его использовать в LiveWallpaper, а затем подгонять.
Основной код обработки GIF LiveWallpaper как таковой ( здесь):
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? Если да, то как?
EDIT: узнав, как масштабироваться для 2-х видов, мне все равно нужно знать, как масштабироваться в соответствии с третьим типом, а также знать, почему он продолжает сбой после изменений ориентации и почему он не всегда показывает предварительный просмотр сразу,
Я также хотел бы знать, какой лучший способ показать анимацию GIF здесь, потому что в настоящее время я просто обновляю холст ~ 60 кадров в секунду (1000/60 ожидания между каждыми 2 кадрами) без учета того, что в файле.
Проект доступен здесь.
Ответы
Ответ 1
OK Я думаю, что я получил, как масштабировать контент. Не уверен, хотя почему приложение по-прежнему периодически падает при изменении ориентации и почему приложение не отображает предварительный просмотр сразу.
Проект доступен здесь.
Для центра внутри код:
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
Если у вас есть Glide в вашем проекте, вы можете легко загрузить Gifs, так как он предоставляет графические GIF файлы для вашего ImageViews и поддерживает множество параметров масштабирования (например, центра или заданной ширины и...).
Glide.with(context)
.load(imageUrl or resourceId)
.asGif()
.fitCenter() //or other scaling options as you like
.into(imageView);
Ответ 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
Масштаб для заполнения и масштабирования:
Там уже есть хороший ответ:
fooobar.com/info/12350685/...