Как разместить содержимое анимации GIF в режиме просмотра и в живых обоях?

Фон

У меня есть небольшое приложение для живых обоев, которое я хочу добавить для него, чтобы показать анимацию GIF.

Для этого я нашел различные решения. Существует решение показать анимацию GIF в представлении ( здесь), и есть даже решение для показа его в живых обоях ( здесь).

Тем не менее, для обоих из них я не могу найти, как хорошо подходить к содержанию анимации GIF в пространстве, которое у него есть, что означает любое из следующего:

  1. center-crop - подходит для 100% контейнера (в этом случае экран), обрезание по сторонам (сверху и снизу или слева и справа) при необходимости. Ничего не растягивает. Это означает, что контент кажется прекрасным, но не все это может быть показано.
  2. fit-center - растяжка, чтобы соответствовать ширине/высоте
  3. 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()
        }
    }
}

Вопросы

  1. Учитывая анимацию GIF, как я могу масштабировать ее по каждому из указанных способов?
  2. Возможно ли иметь одно решение для обоих случаев?
  3. Можно ли использовать библиотеку 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/...