полноэкранный предварительный просмотр android textureview с правильным соотношением сторон

Я работал с демонстрацией API камеры2 от Google, и, к сожалению, образец приложения создан для отображения предварительный просмотр текстуры примерно на 70% высоты экрана, осмотревшись, я смог определить, что это было вызвано тем, что AutoFitTextureView переопределяет метод onMeasure(), как показано ниже:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    if (0 == mRatioWidth || 0 == mRatioHeight) {
        setMeasuredDimension(width, height);
    } else {
        if (width < height * mRatioWidth / mRatioHeight) {
            setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
        } else {
            setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
        }
    }
}

Я попытался исправить это, установив правильную высоту и ширину в setMeasuredDimension(width, height);, это устранило проблему с высотой и дало мне полноэкранный предварительный просмотр из просмотра текстуры, однако соотношение сторон полностью нарушено и искажено на каждом устройстве, что является стандартным способом исправить это? Я вижу, что многие приложения в магазине игр нашли способ решить эту проблему, но так и не смогли найти решение, любая помощь будет иметь большое значение, спасибо.


person Edmund Rojas    schedule 22.07.2016    source источник
comment
Эй, ты нашел какое-нибудь решение? столкнулся с той же проблемой .. помогите пожалуйста.   -  person iMDroid    schedule 17.08.2017
comment
Ответ обновлен ниже   -  person Edmund Rojas    schedule 17.08.2017


Ответы (5)


Мне удалось решить проблему, переключив setMeasuredDimension ();

    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    if (0 == mRatioWidth || 0 == mRatioHeight) {
        setMeasuredDimension(width, height);
    } else {
        if (width < height * mRatioWidth / mRatioHeight) {
            setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
        } else {
            setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
        }
    }
person Edmund Rojas    schedule 25.07.2016
comment
Привет, Эдмунд, что вы имеете в виду, переключая setMeasuredDimension ()? У меня такая же проблема с полноэкранным режимом с camera2. Не могли бы вы предоставить код, который вы используете в методе onMeasure, чтобы получить полноэкранный режим работы без каких-либо искажений? - person J.M.; 11.08.2016
comment
Извините за поздний ответ, я обновил свой ответ - person Edmund Rojas; 17.08.2017
comment
Спасибо за обновленный ответ, но это решение расширяет предварительный просмотр. - person iMDroid; 18.08.2017
comment
Спасибо за решение. Вы должны отметить это как ответ. - person E J Chathuranga; 17.11.2017
comment
спасибо, это работает ... должен быть отмечен как правильный ответ - person FaisalAhmed; 28.11.2017
comment
Переключение действительно работает, но небольшое объяснение того, почему это действительно полезно. Кроме того, это выбивает представление FrameLayout за пределы экрана в коде приложения Camera2Raw. - person Ajay; 05.09.2018
comment
Не забудьте установить высоту и ширину вида wrap_content в xml - person flopshot; 09.04.2019
comment
@flopshot эта штука работает для меня, я был разочарован, потому что я использовал Layout ограничения и установил ширину ограничения высоты, чтобы он растянул мой предварительный просмотр. отмечаемый ответ ... +1 - person Vasudev Vyas; 31.07.2019

Ниже приведен код, который мы использовали для измерения предварительного просмотра, который поддерживает размеры предварительного просмотра 4: 3, 16: 9 и 1: 1. Высота масштабируется, потому что приложение блокируется в портретной ориентации, оно не поворачивается в альбомную ориентацию.

Надеюсь, это поможет вам или кому-то еще с той же проблемой!

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    Log.d(TAG, "[onMeasure] Before transforming: " + width + "x" + height);

    int rotation = ((Activity) getContext()).getWindowManager().getDefaultDisplay().getRotation();
    boolean isInHorizontal = Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation;

    int newWidth;
    int newHeight;

    Log.d(TAG, "[onMeasure] Get measured dimensions: " + getMeasuredWidth() + "x" + getMeasuredHeight());

    if (isInHorizontal) {
        newHeight = getMeasuredHeight();
        if (mAspectRatioOneOne) newWidth = getMeasuredHeight();
        else newWidth = (int) (newHeight * mAspectRatio);
    } else {
        newWidth = getMeasuredWidth();
        if (mAspectRatioOneOne) newHeight = getMeasuredWidth();
        else newHeight = (int) (newWidth * mAspectRatio);
    }

    setMeasuredDimension(newWidth, newHeight);
    Log.d(TAG, "[onMeasure] After transforming: " + getMeasuredWidth() + "x" + getMeasuredHeight());

}
person Francisco Durdin Garcia    schedule 28.07.2016

У меня была аналогичная проблема с реализацией Camera2. Я использовал реализацию TextureView AutoFitTextureView в качестве предварительного просмотра с камеры и хочу настроить правильное соотношение. Моя проблема связана с различными реализациями методов и распространяется на onSurfaceTextureSizeChanged. [1] com.google.android.cameraview.CameraView # onMeasure (..) -> [2] com.google.android.cameraview.AutoFitTextureView # onMeasure (..) -> [3] onSurfaceTextureSizeChanged (..)

Проблема, где в другом if [1], чем в [2]. Я заменил в [1]:

if (height < width * ratio.getY() / ratio.getX()) {

to

if (!(height < width * ratio.getY() / ratio.getX())) {

потому что [2] имел if на основе ширины, а не высоты

 if (width < height * mRatioWidth / mRatioHeight) {

Убедитесь, что методы onMeasure (..) реализованы правильно.

Или вы могли бы сделать как Эдмунд Рохас - выше

person Damian JK    schedule 10.09.2019

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

Однако, если размер вашего экрана не является стандартным (в основном 16: 9 или 4: 3), особенно после вычитания программных кнопок и панели уведомлений (при условии, что вы не полностью полноэкранный режим), тогда единственный вариант для того, чтобы предварительный просмотр заполнял экран, - это обрезать его часть.

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

person Eddy Talvala    schedule 25.07.2016
comment
Эй, как бы вы пошли и увеличили масштаб, чтобы сохранить соотношение сторон, но заполнить весь экран превью камеры? Вы как бы описали это в своем последнем абзаце, но я не понимаю, как правильно изменить матрицу преобразования. - person Csharpest; 08.03.2018
comment
Матрица преобразования - это стандартная графическая матрица 4x4 в однородных координатах. Чтобы заполнить экран, вам просто нужно равномерно масштабировать изображение, пока черные полосы не исчезнут (обратите внимание, что это приведет к удалению некоторых данных изображения за пределы экрана). Это из памяти, но масштабная матрица просто [s 0 0 0; 0 с 0 0; 0 0 0 0; 0 0 0 1], где s - коэффициент увеличения (поэтому s = 2 для двойного размера). Умножьте эту матрицу на матрицу преобразования, которую вы получаете из getTransform (умножение матриц, а не поэлементно), и установите ее как преобразование с помощью setTransform. - person Eddy Talvala; 08.03.2018
comment
Хорошо, я понял метод matrix.setScale (x, y). Я попробую эту матрицу умножить на матрицу преобразования ...- часть завтра и посмотрю, получу ли я ее работать. Я не уверен на 100%, что вы имеете в виду. Я могу снова позвонить тебе, пока огромное спасибо! - person Csharpest; 09.03.2018
comment
Как мне рассчитать масштаб X и Y матрицы преобразования, чтобы он подходил для любой ориентации экрана (альбомная / портретная)? У меня есть ширина экрана + высота и ширина + высота предварительного просмотра. Мне как-то нужно было бы получить значения для X и Y (в зависимости от ориентации), где один из них равен 1, а другой немного больше, в большинстве случаев около 1,2. Я устанавливаю преобразование, когда срабатывает обратный вызов изменения поверхности. - person Csharpest; 09.03.2018
comment
Мммм, подождите, это либо одно значение 1, может быть неправильным - person Csharpest; 09.03.2018
comment
Спасибо, сэр, я добился этого, у меня даже был открытый вопрос по этому поводу, который можно найти здесь: stackoverflow.com/questions/49171585/ - person Csharpest; 09.03.2018

AutoFitTextureView в макете следует использовать wrap_content

    <AutoFitTextureView
        android:id="@+id/texture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

/**
 * A [TextureView] that can be adjusted to a specified aspect ratio.
 */
class AutoFitTextureView : TextureView {

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : super(context, attrs, defStyle)

    private var ratioWidth = 0
    private var ratioHeight = 0

    /**
     * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
     * calculated from the parameters. Note that the actual sizes of parameters don't matter, that
     * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
     *
     * @param width  Relative horizontal size
     * @param height Relative vertical size
     */
    fun setAspectRatio(width: Int, height: Int) {
        require(!(width < 0 || height < 0)) { "Size cannot be negative." }
        ratioWidth = width
        ratioHeight = height
        requestLayout()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        if (ratioWidth == 0 || ratioHeight == 0) {
            setMeasuredDimension(width, height)
        } else {
            if (width < height * ratioWidth / ratioHeight) {
                setMeasuredDimension(width, width * ratioHeight / ratioWidth)
            } else {
                setMeasuredDimension(height * ratioWidth / ratioHeight, height)
            }
        }
    }

}
person weilin    schedule 27.04.2020