web-dev-qa-db-ja.com

GIFアニメーションのコンテンツをビューとライブ壁紙に合わせる方法は?

バックグラウンド

小さなライブ壁紙アプリがあり、GIFアニメーションを表示するためのサポートを追加したいと思います。

このために、私はさまざまな解決策を見つけました。ビューにGIFアニメーションを表示するソリューションがあり( here )、それを表示するためのソリューションもありますライブ壁紙で( here )。

ただし、どちらの場合も、GIFアニメーションのコンテンツをスペースにうまく収める方法が見つかりません。つまり、次のいずれかです。

  1. center-crop-コンテナ(この場合は画面)の100%に収まり、必要に応じて側面(上と下または左と右)でトリミングします。何も伸ばさない。これは、コンテンツは問題ないように見えますが、すべてが表示されるとは限らないことを意味します。
  2. fit-center-幅/高さに合うようにストレッチ
  3. center-inside-大きすぎる場合にのみ、幅/高さに合うように元のサイズ、中央、ストレッチとして設定します。

問題

これらはいずれも実際にはImageViewに関するものではないため、scaleType属性を使用することはできません。

私が見つけたもの

ImageViewで使用できるGifDrawable( here )を提供するソリューションがありますが、それはきれいなようです場合によっては遅く、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. Movieクラスの代わりに、ライブ壁紙にGifDrawableライブラリ(またはその他のドローアブル)を使用することはできますか?もしそうなら、どのように?

編集:2種類のスケーリング方法を見つけた後でも、3番目のタイプに応じてスケーリングする方法を知る必要があります。また、向きを変更した後もクラッシュし続ける理由と、プレビューがすぐに表示されない理由も知りたいです。 。

また、ここでGIFアニメーションを表示するための最良の方法を知りたいのですが、現在、ファイルの内容を考慮せずに、キャンバスを最大60 fps(2フレームごとに1000/60待機)更新するだけです。

プロジェクトが利用可能です ここ

30

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

フィットセンターの場合、これを使用できます。

                    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

プロジェクトに Glide がある場合、ImageViewに描画GIFを提供し、多くのスケーリングオプション(中心や特定の幅など)をサポートしているため、Gifを簡単に読み込むことができます。

Glide.with(context)
    .load(imageUrl or resourceId)
    .asGif()
    .fitCenter() //or other scaling options as you like
    .into(imageView);
6
Adib Faramarzi

ムービーの幅と高さを変更します:

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

塗りつぶすスケールとフィットするスケール:

それについてはすでに良い答えがあります:

https://stackoverflow.com/a/38898699/5675325

2