Ошибка с методом Android 4.3 ImageView getImageMatrix()

Недавно я обновился до Android 4.4, и некоторые функции моего приложения неожиданно перестали работать.

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

private void initAtZoomLevel(float zoomLevel){
    ....
    Matrix transformMatrix = new Matrix();
    transformMatrix.setScale(initialZoomLevel, initialZoomLevel);
    float yTransCenter = (screenHeight - mapHeight)/2.0f;
    setImageMatrix(transformMatrix);
}

protected void onDraw(Canvas canvas){

    super.onDraw(canvas);
    float[] values = new float[9];
    getImageMatrix().getValues(values);
    scaleFactor = values[0];
    ....
}

ЭТО РАБОТАЕТ НА УСТРОЙСТВАХ ANDROID 4.1.2 и 4.2.2, которые у меня есть

Но на Android 4.4/4.3 getImageMatrix().getValues(values) перестал работать! Он возвращает матрицу идентичности вместо матрицы преобразования, которую я ожидаю при запуске приложения!

ОТЛАДОЧНАЯ РАСПЕЧАТКА:

4.1.2: @setImageMatrix(transformMatrix): transformMatrix = Matrix{[0.025122833, 0.0, 0.0][0.0, 0.025122833, 566.5][0.0, 0.0, 1.0]}

@getImageMatrix().getValues(values): transformMatrix = Matrix{[0.025122833, 0.0, 0.0][0.0, 0.025122833, 566.5][0.0, 0.0, 1.0]}

4.4: @setImageMatrix(transformMatrix): transformMatrix = Matrix{[0.025122833, 0.0, 0.0][0.0, 0.025122833, 553.0][0.0, 0.0, 1.0]}

@getImageMatrix().getValues(values): transformMatrix = Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}

Я огляделся, и я не могу найти никакой документации по этому поводу. Каким-то образом матрица изображения для моего представления сбрасывается; Android 4.4 изменил то, как мы должны это делать? Кто-нибудь еще столкнулся с этой проблемой?

примечание: похоже, проблема возникла на Android 4.3 – такая же проблема возникает и на эмуляторе

ОБНОВЛЕНИЕ: я проверил журнал изменений с 4.2 на 4.3., но я ничего не вижу над классом Matrix или чем-либо, относящимся к классу View.

ОБНОВЛЕНИЕ 2: у меня тоже не работает масштабирование, в котором используется тот же метод setImageMatrix(), и он явно не работает, потому что в getImageMatrix().getValues() ничего не происходит.


person kburbach    schedule 06.12.2013    source источник
comment
Попробуйте изменить фактический исходный код getImageMatrix между версиями Android.   -  person Jon Willis    schedule 06.12.2013
comment
@GenericHolidayName, если присмотреться, на самом деле это не сохраняет/не возвращает одну и ту же матрицу (имена полей различаются между getImageMatrix и setImageMatrix)   -  person kburbach    schedule 09.12.2013
comment
stackoverflow.com/questions/30446099/ Кто-нибудь, кто знает проблему для этого?   -  person maya    schedule 26.05.2015


Ответы (2)


Я нашел то, что я считаю проблемой. Я взглянул на исходный код для ImageView и обнаружил, что setImageMatrix(Matrix matrix) сохраняет матрицу в другом поле, чем возвращает getImageMatrix()...

Android 4.4 ImageView

public void setImageMatrix(Matrix matrix) {
    // collaps null and identity to just null
    if (matrix != null && matrix.isIdentity()) {
        matrix = null;
    }

    // don't invalidate unless we're actually changing our matrix
    if (matrix == null && !mMatrix.isIdentity() ||
            matrix != null && !mMatrix.equals(matrix)) {
        mMatrix.set(matrix);
        configureBounds();
        invalidate();
    }
}

Здесь матрица хранится в поле mMatrix

public Matrix getImageMatrix() {
    if (mDrawMatrix == null) { //<-- should be mMatrix == null
        return new Matrix(Matrix.IDENTITY_MATRIX);
    }
    return mDrawMatrix; //<-- NOT THE RIGHT FIELD TO RETURN
}

Пока getImageMatrix() возвращает mDrawMatrix...

Android 4.1.2 ImageView

public Matrix getImageMatrix() {
    return mMatrix;
}

public void setImageMatrix(Matrix matrix) {
    // collaps null and identity to just null
    if (matrix != null && matrix.isIdentity()) {
        matrix = null;
    }

    // don't invalidate unless we're actually changing our matrix
    if (matrix == null && !mMatrix.isIdentity() ||
            matrix != null && !mMatrix.equals(matrix)) {
        mMatrix.set(matrix);
        configureBounds();
        invalidate();
    }
}

оба метода используют одно и то же полеmMatrix

Итак, проблема прямо здесь --- вдруг getImageMatrix() возвращает неправильное поле...

person kburbach    schedule 09.12.2013
comment
Чтобы попытаться решить проблему, убедитесь, что для scaleType установлено значение ScaleType.MATRIX. Похоже, что mMatrix должно быть назначено mDrawMatrix в configureBounds(), если scaleType установлено правильно. Похоже, что на самом деле это исправление, призванное усилить scaleType, а не новую ошибку. - person Geobits; 09.12.2013
comment
Я мог бы поклясться, что где-то это было... пора посмотреть, не в этом ли проблема - person kburbach; 09.12.2013
comment
Вы можете быть правы. Мне так кажется. Раньше не имело значения, присвоено значение mDrawMatrix или нет, потому что оно не возвращалось. - person Geobits; 09.12.2013
comment
есть строка if(ScaleType.MATRIX == mScaleType) {... mDrawMatrix = mMatrix}, НО - первая строка configureBounds() это if (mDrawable == null || !mHaveFrame) { return; }. Так что он сразу вернется и обойдет настройку mDrawMatrix = mMatrix. - person kburbach; 09.12.2013
comment
Можно ли рисовать null? Это определенно может объяснить это. - person Geobits; 09.12.2013
comment
да, я никогда не устанавливал drawable - мне нужно использовать ImageView для рисования сотен маленьких прямоугольников, текста и т. д., которые меняются в зависимости от уровня масштабирования, поэтому, когда я впервые начал проект, у меня сложилось впечатление, что я не могу использовать Drawable.. , это работало до сих пор, но, может быть, я сделал неправильное предположение? - person kburbach; 09.12.2013
comment
Вы можете попробовать использовать фиктивный рисунок, чтобы заставить configureBounds работать. Я могу понять, почему они не утруждают себя установкой границ для нулевого рисунка, поэтому я думаю, что это может быть хорошим обходным путем. - person Geobits; 09.12.2013
comment
Хотя я бы не сказал, что это неверное предположение. Не знать, что теперь он должен пройти через configureBounds, чтобы вернуть правильную матрицу, кажется довольно разумным, поскольку раньше этого не требовалось. - person Geobits; 09.12.2013
comment
установка фиктивного чертежа в одиночку не сработала, но я заставил его работать - сделал отдельный ответ. - person kburbach; 10.12.2013

Хотя мой предыдущий ответ описывает общую проблему, на самом деле это может быть не ошибка. Как указано в Generic Holiday Name, mDrawMatrix должно быть установлено на mMatrix в методе configureBounds(), что устранит эту проблему/ошибку/что угодно.

ОДНАКО мне пришлось добавить несколько строк кода, чтобы configureBounds() действительно заработало:

private void configureBounds() {
    if (mDrawable == null || !mHaveFrame) {
        return;
    }

    int dwidth = mDrawableWidth;
    int dheight = mDrawableHeight;

    ...

    if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
        /* If the drawable has no intrinsic size, or we're told to
            scaletofit, then we just fill our entire view.
        */
        mDrawable.setBounds(0, 0, vwidth, vheight);
        mDrawMatrix = null;
    } else {
             //here's the where mDrawMatrix == mMatrix IF using scaleType.MATRIX
             ...
    }
}

Итак, чтобы заставить это работать так, как я ожидаю, с 4.2 и более ранними версиями, вам необходимо убедиться:

  1. mDrawable != null. Раньше это не было проблемой, но в моем случае я не использовал рисование, поэтому все терпело неудачу (оператор return был выполнен сразу)
  2. 'dwidth >0 && dheight >0. Это не проблема, если у вас есть настоящий рисунок, но, как я уже сказал, у меня его не было.
  3. mHaveFrame = true. Я понятия не имел, что это такое - никогда не использовал его. Единственный способ установить это значение true — вызвать setFrame(int, int, int, int).

Чтобы мой код масштабирования снова заработал, мне пришлось добавить следующее:

 //potentially a fix for the "bug" that appears in Android 4.3+
 //mDrawable cannot be null anymore for getImageMatrix to work---stupid
 //therefore the imageview class MUST set a drawable for matrix scaling to work
 //so here I am using a "empty" drawable to get around this
 ShapeDrawable fakeDrawable = new ShapeDrawable(); //so mDrawable != null
 fakeDrawable.setIntrinsicHeight(1); //so dwidth and dheight are > 0
 fakeDrawable.setIntrinsicWidth(1);

 setImageDrawable(fakeDrawable);

 setFrame(0, 0, viewWidth, viewHeight); //setting a frame so mHaveFrame = true

УРА

person kburbach    schedule 09.12.2013
comment
Кстати, добавленный код был помещен в методы конструктора/инициализации для моего пользовательского изображения. - person kburbach; 10.12.2013