Почему вызов setScaleX во время жеста масштабирования вызывает мерцание?

Я пытаюсь создать масштабируемый контейнер и ориентируюсь на API 14+.

В моем onScale (я использую ScaleGestureDetector для обнаружения масштабирования) я делаю что-то вроде этого:

public boolean onScale (ScaleGestureDetector detector) {
   float scaleFactor = detector.getScaleFactor();
   setScaleX(getScaleX() * scaleFactor);
   setScaleY(getScaleY() * scaleFactor);

   return true;
};

Работает, но зум не плавный. На самом деле он заметно мерцает.

Я также попробовал это с аппаратным слоем, думая, что масштабирование будет происходить на графическом процессоре после загрузки текстуры и, следовательно, будет очень быстрым. Но это не имело значения - зум не плавный и иногда странно мерцает.

Что я делаю неправильно?


person numan salati    schedule 30.06.2013    source источник
comment
Я не хочу публиковать ответ, потому что я не могу протестировать решение, но вы можете, поэтому посмотрите здесь: stackoverflow.com/questions/5790503/ . После того, как вы попробуете, вернитесь сюда и сообщите нам, решило ли это проблему. Взгляните на последний ответ.   -  person g00dy    schedule 03.07.2013
comment
я знаю об этом решении, но у него есть критические недостатки - оно только масштабирует холст для рисования - ни один из клипов и точек касания не трансформируется. вот почему я использовал setScale, который изменяет матрицу преобразования в контейнере и корректирует прямоугольники клипа и соответствующим образом преобразует сенсорные координаты (без необходимости самостоятельно выполнять математические вычисления матрицы)   -  person numan salati    schedule 03.07.2013
comment
Можете ли вы поделиться немного больше кода? В частности, фактические процедуры рисования. Вы пробовали профилировать, чтобы увидеть, где рисунок борется? Я бы посмотрел на минимизацию (возможно, до нуля) количества распределений объектов в узких циклах рисования. Я знаю, что это общие (возможно, очевидные) моменты, но трудно сказать больше без дополнительной информации.   -  person snowdragon    schedule 09.07.2013
comment
Привет, @numansalati, ты смог решить эту проблему? Я, к сожалению, сталкиваюсь с тем же.   -  person Gautam Mandsorwale    schedule 11.02.2014
comment
@GautamM. извините, пришлось перейти к другим вещам. если вы найдете что-то, поделитесь этим.   -  person numan salati    schedule 11.02.2014


Ответы (3)


Мерцание похоже на переключение вида между увеличенным и неувеличенным изображением? Это вызвано тем, что ScaleGestureDetector обрабатывает события движения из того же представления, которое вы масштабируете. Когда вы setScaleX() изменяете координаты касания, что вызывает новое событие касания, интерпретируемое как изменение масштаба, который вы только что применили.

Поместите содержимое вашего масштабируемого представления в один дочерний элемент FrameLayout и вместо этого установите масштаб для этого представления.

Я разместил рабочий макет масштабирования здесь: https://gist.github.com/anorth/9845602

person Alex North    schedule 29.03.2014
comment
нет ли другого выхода, какова конкретная причина? можем ли мы решить по-другому, не увеличивая иерархию приложения? @Алекс - person Anmol; 28.03.2019

Согласно вашему вопросу, методы setScaleX и setScaleY мерцают, потому что они выполняются внутри контекста «рисование в представлении» (setScaleX и setScaleY принадлежат View). В документации говорится:

Вариант «а» — рисование в виде — лучший выбор, если вы хотите рисовать простую графику, которая не требует динамических изменений и не является частью игры, требующей высокой производительности. Например, вы должны рисовать свою графику в представлении, когда хотите отобразить статическую графику или предопределенную анимацию внутри статического приложения.

С другой стороны, если вы последуете ответу @ g00dy, вы будете выполнять внутри контекста «рисование на холсте» (scale() принадлежит Canvas). В документации говорится:

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

Жест щипка является интенсивным, поэтому его необходимо выполнить в варианте b... поэтому вам следует использовать решение @g00dy или любой другой подобный подход.

Здесь приведена цитируемая документация.

Я надеюсь, что это поможет вам.

person Jorge Gil    schedule 05.07.2013
comment
setScaleX (или alpha, или tranx/y) оптимизированы за счет того, что не создают повторное создание списков отображения, потому что все, что ему нужно сделать, это сделать недействительным список отображения родителя. а во-вторых, используя слои, предполагается масштабирование на GPU. Так что в любом случае теоретически это должно быть быстрее, чем перерисовывать каждый кадр (что эквивалентно воссозданию DL). - person numan salati; 06.07.2013
comment
также вы читали мой комментарий к комментарию GOOD? это решение имитирует его, потому что мне интересно не просто показывать масштабирование (что является тривиальной проблемой), а создавать общий контейнер масштабирования (проблема намного сложнее), где все дети имеют соответствующие клипы и точки касания преобразованы (наряду с отображением масштабирования). Я могу сделать это, полностью контролируя манипулирование матрицей контейнера, но я хотел избежать этого, потому что это то, что свойства scaleX и scaleY существуют в первую очередь (представлено в API 11+) - person numan salati; 06.07.2013
comment
Ну... во-первых, ваш вопрос, почему мерцание, затем у вас есть ответ, на самом деле мне все равно, что вы хотите сделать. И если вам не нравится решение @g00dy, как я уже говорил, вы можете использовать любой другой подобный подход. Во-вторых, нет ничего быстрее, чем рисование на холсте, поэтому компании, занимающиеся видеоиграми, всегда используют холст для динамического обновления своих графических фреймов. - person Jorge Gil; 06.07.2013
comment
вы упускаете из виду суть вопроса и нюансы различных подходов. это не нубский вопрос, но спасибо за попытку. - person numan salati; 06.07.2013

Я столкнулся с той же проблемой. Задача была:

  • создать холст (в подклассе View или SurfaceView, не имеет значения)
  • нарисовать на нем некоторые изображения и графические примитивы (методами drawBitmap(), drawLine()...)
  • сделать холст прокручиваемым и масштабируемым

Я полагаю, нет необходимости показывать здесь весь код класса.

Решение проблемы с мерцанием при масштабировании было очень простым. Просто поместите процедуру масштабирования в метод onScaleEnd().

private class MyScaleGestureListener implements OnScaleGestureListener
{
    public boolean onScale(ScaleGestureDetector detector)
    {   
        scaleFactor *= detector.getScaleFactor();  // class variable of type float
        if (scaleFactor > 5) scaleFactor = 5;      // some limitations
        if (scaleFactor < 1) scaleFactor = 1;
        return true;
    }
    public boolean onScaleBegin(ScaleGestureDetector detector)
    { return true;}

    public void onScaleEnd(ScaleGestureDetector detector) 
     {
       setScaleX(scaleFactor); setScaleY(scaleFactor); 
       invalidate();     // it seems to me - no effect
     }
} 

Проверено на реальном устройстве Samsung GALAXY S III mini.

person vladimir vesely    schedule 27.09.2014
comment
Но что, если я хочу масштабироваться, даже если для улучшения UX требуется onScale? - person Anmol; 28.03.2019