Простой ZoomableImageView своими руками за 2 минуты

15 ИЮНЯ 2022

------------------------------------------------------------------------

Наш телеграм-канал: t.me/@mailsgun_online

Youtube: youtube.com/@mailsgun_online

------------------------------------------------------------------------

Обновить

6 сентября в 17:09

Порой в Android разработке бывают простые проблемы, которые не так просто решить без нужных библиотек или Custom View.

Недавно я столкнулся с проблемой создания вот такого простого эффекта:

Более подробно вы можете посмотреть запись экрана эмулятора

Простыми словами: вы можете уменьшать изображение и увеличивать его. Если вы уменьшаете изображение, а затем отпускаете его, оно возвращается в исходное состояние плавным переходом.

Казалось бы, много различных библиотек, которые могут это сделать.

Но увы, я не нашел ничего более менее подходящего

Код

Приступаем к коду:

public class ZoomableImage extends AppCompatImageView {
    // Отслеживание касаний пальцев
    private ScaleGestureDetector scaleDetector;
    // текущий коэффициент масштабирования изображения
    private float scaleFactor = 1.f;
    // конструкторы
    public ZoomableImage(Context context){
        super(context);
        init();
    }
    public ZoomableImage(Context context, AttributeSet set) {
        super(context, set);
        init();
    }
    public ZoomableImage(Context context, AttributeSet set, int defStyleAttr) {
        super(context, set, defStyleAttr);
        init();
    }
    // начальный коэффициент масштабирования
    private final float normalScaleFactor = 1f;
    // число на которое будет увеличиваться коэффициент масштабирования при анимации
    private final float increasingValue = .02f;
    // специальное число, которое уменьшает скорость масштабирования при уменьшении изображения
    private final double decreasingDivisor = 1.032;
    // минимальный коэффициент масштабирования
    private final float minScaleFactor = 0.05f;
    // максимальный коэффициент масштабирования
    private final float maxScaleFactor = 5.0f;
    // задержка для анимации
    private final long delay = 4L;
    // используем для выполнения анимации
    final Handler handler = new Handler(Looper.getMainLooper());
    // анимация возвращения к нормальному коэффициенту
    // срабатывает, когда мы уменьшаем изображение и отпускаем его
    final Runnable animationRunnable = new Runnable() {
        @Override
        public void run() {
            // пока текущий коэффициент масштабирования меньше нормального
            // увеличиваем его до нормального
            if (scaleFactor <= normalScaleFactor) {
                scaleFactor += increasingValue;
                // постоянно перерисовываем наше View
                invalidate();
                // снова выполняем анимацию, пока условие не станет ложным
                handler.postDelayed(this, delay);
            } else {
                // после завершения анимации, присваем текущему коэффициенту
                // начальное значение и перерисовываем View
                scaleFactor = normalScaleFactor;
                invalidate();
            }
        }
    };
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        // отслеживаем касания пальцев
        scaleDetector.onTouchEvent(ev);
        // запускаем анимацию, если мы уменьшили изображение и отпустили его
        if (ev.getAction() == MotionEvent.ACTION_UP && scaleFactor < normalScaleFactor) {
            // отменяем предыдущую анимацию
            handler.removeCallbacks(animationRunnable);
            // запускаем новую
            handler.postDelayed(animationRunnable, 4L);
        }
        return true;
    }
    private void init() {
        // инициализация нашего ScaleGestureDetector'а
        scaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    }
    @Override
    public void onDraw(Canvas canvas) {
        canvas.save();
        // масштабируем canvas вместе с нашим изображением
        // Обратите внимание, что мы передаем getWidth() / 2 и getHeight() / 2
        // для того, чтобы изображение масштабировалось от центра
        canvas.scale(scaleFactor, scaleFactor, getWidth() / 2, getHeight() / 2);
        super.onDraw(canvas);
        canvas.restore();
    }
    // чувствительность срабатывания, которую я подобрал экспериментально
    private final int sensitivity = 10;
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            // если значения перемещения наших пальцев больше sensitivity то мы считаем,
            // что изображение масштабируется, в противном случае - ложное срабатывание
            if (Math.abs((detector.getCurrentSpan() - detector.getPreviousSpan())) > sensitivity) {
                final float scale = detector.getScaleFactor();
                // при уменьшении я заметил что скорость масштабирования была очень быстрая,
                // поэтому я решил поделить scale на специальный коэффициент decreasingDivisor, который я тоже подобрал
                // экспериментально
                scaleFactor *= scale < normalScaleFactor ? scale / decreasingDivisor : scale;
                // ограничиваем максимальное масштабирование в 5.0 и минимальное в 0.05
                scaleFactor = Math.max(minScaleFactor, Math.min(scaleFactor, maxScaleFactor));
                invalidate();
            }
            return true;
        }
    }
}

Думаю, без вопросов)

Все довольно понятно и просто.

Хорошего кода! 🙂

Всего голосов 7: ↑6 и ↓1+5
  • 2 февраля 2017 в 09:46
    Всего голосов 25: ↑21 и ↓4+17

    Просмотры8.9K

  • 29 июля 2015 в 06:30
    Всего голосов 26: ↑22 и ↓4+18

    Просмотры51K

  • 15 апреля 2015 в 11:49
    Всего голосов 23: ↑16 и ↓7+9

    Просмотры44K

  • вчера в 12:00
    Всего голосов 137: ↑134 и ↓3+131

    Просмотры38K

  • вчера в 12:00
    Всего голосов 47: ↑47 и ↓0+47

    Просмотры4.8K

  • вчера в 19:34
    Всего голосов 38: ↑37 и ↓1+36

    Просмотры8.6K

  • вчера в 11:42
    Всего голосов 40: ↑37 и ↓3+34

    Просмотры22K

  • вчера в 10:01
    Всего голосов 33: ↑32 и ↓1+31

    Просмотры3K

Source: habr1

Ну и напоследок напоминаю вам о нашем новом проекте vkserfing bot.