Как сделать подобный механизм центра обрезки на PlayerView ExoPlayer, но не в центре?

Альтернативой является предоставление вашим текущим пользователям уникального кода, например, David123 или HenryABC. Они делятся своим кодом (который легко запомнить, поскольку он личный). В вашем приложении вы предоставляете реферальный блок, в который новый пользователь вводит код, чтобы разблокировать дополнительные функции. Таким образом, вы можете отслеживать и не беспокоиться о отпечатке пальца. Большая проблема с методом отпечатка пальца - iOS недостаточно уникальна, а мобильные сети используют IP-адреса. Так что кто-то с iPhone 6s в Лондоне на EE, вероятно, будет иметь тот же отпечаток пальца, что и 100 других людей.

18
задан android developer 6 February 2019 в 12:31
поделиться

1 ответ

Вопрос в том, как манипулировать изображением, подобным ImageView.ScaleType.CENTER_CROP , но сместить фокус из центра в другое место, которое находится на 20% от верхней части изображения. Сначала давайте посмотрим, что делает CENTER_CROP:

Из документации :

CENTER_CROP

Scale изображение равномерно (сохраняйте пропорции изображения), так что оба размера (ширина и высота) изображения будут равны или больше соответствующего размера вида (минус отступ). Затем изображение центрируется на виде. Из XML используйте этот синтаксис: android:scaleType="centerCrop".

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

Еще один способ думать об этом состоит в том, что центр изображения «прикреплен» к центру изображения. Затем изображение масштабируется в соответствии с указанными выше критериями.

В следующем видео белые линии отмечают центр изображения; красные линии отмечают центр зрения. Тип шкалы - CENTER_CROP. Обратите внимание, как центральные точки изображения и вида совпадают. При изменении размера представления эти две точки продолжают перекрываться и всегда отображаются в центре вида независимо от размера представления.

enter image description here

Итак, что значит иметь подобный центру поведение в другом месте, например, на 20% сверху? Как и в центре обрезки, мы можем указать, что точка, которая находится в 20% от верхней части изображения, и точка, в которой 20% от верхней части вида будут «закреплены», как точка 50% «закреплена» в центре обрезки. Горизонтальное расположение этой точки остается на уровне 50% изображения и вида. Теперь изображение можно масштабировать для соответствия другим условиям обрезки по центру, которые указывают, что ширина и / или высота изображения будут соответствовать виду без зазоров. (Под размером представления понимается размер представления без заполнения).

Вот короткое видео об этом поведении кадрирования на 20%. В этом видео белые линии показывают середину изображения, красные линии показывают закрепленную точку на виде, а синяя линия, которая отображается за горизонтальной красной линией, обозначает 20% от верхней части изображения. (Демонстрационный проект находится на GitHub .

enter image description here

Вот результат, показывающий полное изображение, которое было предоставлено, и видео в квадратной рамке, переходящее из неподвижного изображения.

enter image description here

MainActivity.kt
[ 1115] prepareMatrix() - это метод, позволяющий определить, как масштабировать / обрезать изображение. Необходимо выполнить дополнительную работу с видео, так как кажется, что видео сделано в соответствии с TextureView как масштаб введите "FIT_XY", когда он назначен для TextureView. Из-за этого масштабирования размер носителя должен быть восстановлен до того, как prepareMatrix() будет вызвано для видео

class MainActivity : AppCompatActivity() {
    private val imageResId = R.drawable.test
    private val videoResId = R.raw.test
    private var player: SimpleExoPlayer? = null
    private val mFocalPoint = PointF(0.5f, 0.2f)

    override fun onCreate(savedInstanceState: Bundle?) {
        window.setBackgroundDrawable(ColorDrawable(0xff000000.toInt()))
        super.onCreate(savedInstanceState)
        if (cache == null) {
            cache = SimpleCache(File(cacheDir, "media"), LeastRecentlyUsedCacheEvictor(MAX_PREVIEW_CACHE_SIZE_IN_BYTES))
        }
        setContentView(R.layout.activity_main)
        //        imageView.visibility = View.INVISIBLE
        imageView.setImageResource(imageResId)
        imageView.doOnPreDraw {
            imageView.scaleType = ImageView.ScaleType.MATRIX
            val imageWidth: Float = ContextCompat.getDrawable(this, imageResId)!!.intrinsicWidth.toFloat()
            val imageHeight: Float = ContextCompat.getDrawable(this, imageResId)!!.intrinsicHeight.toFloat()
            imageView.imageMatrix = prepareMatrix(imageView, imageWidth, imageHeight, mFocalPoint, Matrix())
            val b = BitmapFactory.decodeResource(resources, imageResId)
            val d = BitmapDrawable(resources, b.copy(Bitmap.Config.ARGB_8888, true))
            val c = Canvas(d.bitmap)
            val p = Paint()
            p.color = resources.getColor(android.R.color.holo_red_dark)
            p.style = Paint.Style.STROKE
            val strokeWidth = 10
            p.strokeWidth = strokeWidth.toFloat()
            // Horizontal line
            c.drawLine(0f, imageHeight * mFocalPoint.y, imageWidth, imageHeight * mFocalPoint.y, p)
            // Vertical line
            c.drawLine(imageWidth * mFocalPoint.x, 0f, imageWidth * mFocalPoint.x, imageHeight, p)
            // Line in horizontal and vertical center
            p.color = resources.getColor(android.R.color.white)
            c.drawLine(imageWidth / 2, 0f, imageWidth / 2, imageHeight, p)
            c.drawLine(0f, imageHeight / 2, imageWidth, imageHeight / 2, p)

            imageView.setImageBitmap(d.bitmap)
            imageViewFull.setImageBitmap(d.bitmap)
        }
    }

    fun startPlay(view: View) {
        playVideo()
    }

    private fun getViewWidth(view: View): Float {
        return (view.width - view.paddingStart - view.paddingEnd).toFloat()
    }

    private fun getViewHeight(view: View): Float {
        return (view.height - view.paddingTop - view.paddingBottom).toFloat()
    }

    private fun prepareMatrix(targetView: View, mediaWidth: Float, mediaHeight: Float,
                              focalPoint: PointF, matrix: Matrix): Matrix {
        if (targetView.visibility != View.VISIBLE) {
            return matrix
        }
        val viewHeight = getViewHeight(targetView)
        val viewWidth = getViewWidth(targetView)
        val scaleFactorY = viewHeight / mediaHeight
        val scaleFactor: Float
        val px: Float
        val py: Float
        if (mediaWidth * scaleFactorY >= viewWidth) {
            // Fit height
            scaleFactor = scaleFactorY
            px = -(mediaWidth * scaleFactor - viewWidth) * focalPoint.x / (1 - scaleFactor)
            py = 0f
        } else {
            // Fit width
            scaleFactor = viewWidth / mediaWidth
            px = 0f
            py = -(mediaHeight * scaleFactor - viewHeight) * focalPoint.y / (1 - scaleFactor)
        }
        matrix.postScale(scaleFactor, scaleFactor, px, py)
        return matrix
    }

    private fun playVideo() {
        player = ExoPlayerFactory.newSimpleInstance(this@MainActivity, DefaultTrackSelector())
        player!!.setVideoTextureView(textureView)
        player!!.addVideoListener(object : VideoListener {
            override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) {
                super.onVideoSizeChanged(width, height, unappliedRotationDegrees, pixelWidthHeightRatio)
                val matrix = Matrix()
                // Restore true media size for further manipulation.
                matrix.setScale(width / getViewWidth(textureView), height / getViewHeight(textureView))
                textureView.setTransform(prepareMatrix(textureView, width.toFloat(), height.toFloat(), mFocalPoint, matrix))
            }

            override fun onRenderedFirstFrame() {
                Log.d("AppLog", "onRenderedFirstFrame")
                player!!.removeVideoListener(this)
                imageView.animate().alpha(0f).setDuration(2000).start()
                imageView.visibility = View.INVISIBLE
            }
        })
        player!!.volume = 0f
        player!!.repeatMode = Player.REPEAT_MODE_ALL
        player!!.playRawVideo(this, videoResId)
        player!!.playWhenReady = true
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/240/big_buck_bunny_240p_20mb.mkv", cache!!)
        //        player!!.playVideoFromUrl(this, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv", cache!!)
        //        player!!.playVideoFromUrl(this@MainActivity, "https://sample-videos.com/video123/mkv/720/big_buck_bunny_720p_1mb.mkv")
    }

    override fun onStop() {
        super.onStop()
        if (player != null) {
            player!!.setVideoTextureView(null)
            //        playerView.player = null
            player!!.release()
            player = null
        }
    }

    companion object {
        const val MAX_PREVIEW_CACHE_SIZE_IN_BYTES = 20L * 1024L * 1024L
        var cache: com.google.android.exoplayer2.upstream.cache.Cache? = null

        @JvmStatic
        fun getUserAgent(context: Context): String {
            val packageManager = context.packageManager
            val info = packageManager.getPackageInfo(context.packageName, 0)
            val appName = info.applicationInfo.loadLabel(packageManager).toString()
            return Util.getUserAgent(context, appName)
        }
    }

    fun SimpleExoPlayer.playRawVideo(context: Context, @RawRes rawVideoRes: Int) {
        val dataSpec = DataSpec(RawResourceDataSource.buildRawResourceUri(rawVideoRes))
        val rawResourceDataSource = RawResourceDataSource(context)
        rawResourceDataSource.open(dataSpec)
        val factory: DataSource.Factory = DataSource.Factory { rawResourceDataSource }
        prepare(LoopingMediaSource(ExtractorMediaSource.Factory(factory).createMediaSource(rawResourceDataSource.uri)))
    }

    fun SimpleExoPlayer.playVideoFromUrl(context: Context, url: String, cache: Cache? = null) = playVideoFromUri(context, Uri.parse(url), cache)

    fun SimpleExoPlayer.playVideoFile(context: Context, file: File) = playVideoFromUri(context, Uri.fromFile(file))

    fun SimpleExoPlayer.playVideoFromUri(context: Context, uri: Uri, cache: Cache? = null) {
        val factory = if (cache != null)
            CacheDataSourceFactory(cache, DefaultHttpDataSourceFactory(getUserAgent(context)))
        else
            DefaultDataSourceFactory(context, MainActivity.getUserAgent(context))
        val mediaSource = ExtractorMediaSource.Factory(factory).createMediaSource(uri)
        prepare(mediaSource)
    }
}
0
ответ дан Cheticamp 6 February 2019 в 12:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: