Android слайд, чтобы ответить, как анимация ImageView

Мне нужно реализовать анимацию на ImageView , похожую на слайд, чтобы ответить на анимацию, которая существует на многих устройствах Android. Требования:

  1. Уровень API поддержки> = 8 (если это невозможно, тогда 9), поэтому удобное прослушивание перетаскивания не может быть и речи
  2. Перемещайте ImageView вправо или влево при перетаскивании, требуется только горизонтальное перетаскивание. Сначала изображение центрируется по горизонтали.
  3. Масштабируйте ImageView при перетаскивании – как только он приближается к концу экрана, тем меньше будет изображение.
  4. При отпускании перетаскивания изображение необходимо оживить обратно в центр экрана и масштабировать его до исходного размера (также анимированный)

Я нашел много примеров кода и попытался реализовать его самостоятельно, но сочетание всех требований сделало его очень сложным, и я не смог получить достойный результат, поэтому, пожалуйста, не ставьте ссылки на вещи с первой страницы Google Поиск, поскольку я провел много дней, пытаясь реализовать эти примеры, я был бы признателен за пример рабочего кода + xml layout (при необходимости)

Вы можете сделать это без особых сложностей, объединив:

  • A View.OnTouchListener (для обнаружения последовательности перетаскивания, в основном ACTION_DOWN , ACTION_MOVE , …, ACTION_UP ).
  • Отличная библиотека библиотеки NineOldAndroids от Jake Wharton, поддерживающая анимацию свойств изображения до уровня API 11.

Сначала возьмите объект ImageView и присоедините к нему View.OnTouchListener .

 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mImage = findViewById(...); mImage.setOnTouchListener(mTouchListener); } 

Во-вторых, запрограммируйте OnTouchListener для захвата события ACTION_DOWN и сохраните координату X начального положения касания. Затем для каждого ACTION_MOVE вычисляет дельта (для трансляции и масштабирования), а для ACTION_UP возвращает ImageView в исходное состояние.

 private View.OnTouchListener mTouchListener = new View.OnTouchListener() { private float mStartX; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN : mStartX = event.getRawX(); return true; case MotionEvent.ACTION_MOVE : float currentX = event.getRawX(); animateTo(currentX - mStartX, true); // Snap to drag return true; case MotionEvent.ACTION_UP : case MotionEvent.ACTION_CANCEL : animateTo(0, false); // Ease back return true; } return false; } }; 

Функция animateTo() будет реализована следующим образом. immediate флаг указывает, должен ли быть трансляции и масштабирования, ну, немедленным (это верно при ответе на каждое событие перемещения и false при возврате назад в исходное положение и масштаб).

 private void animateTo(float displacement, boolean immediate) { final int EASE_BACK_DURATION = 300; // ms int duration = (immediate ? 0 : EASE_BACK_DURATION); Display display = getWindowManager().getDefaultDisplay(); int totalWidth = display.getWidth(); float scale = 1.0f - Math.abs(displacement / totalWidth); ViewPropertyAnimator.animate(mImage) .translationX(displacement) .scaleX(scale) .scaleY(scale) .setDuration(duration) .start(); } 

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

Это решение должно работать без проблем на уровне API 8 (хотя я его не тестировал). Полный текст доступен здесь, если вы этого хотите.

Первое, что пришло в голову, – это анимация LayoutParams через Handler . Не уверен, что он будет соответствовать вашим требованиям, и это, вероятно, потребует еще нескольких тестов.

В любом случае было довольно забавно запоминать математику. Итак, вот мой подход к ней, используя только собственные инструменты для Android:

Код:

 package com.example.simon.draggableimageview; import android.os.Handler; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; /** * Created by Simon on 2014 Nov 18. */ public class MainActivity extends ActionBarActivity { private static final String TAG = "MainActivity"; // Avoid small values for the following two or setSize will start lagging behind // The maximum time, animation (from smallest) to default size will take private static final int MAX_DURATION = 500; // Minimum delay (ms) for each loop private static final int MIN_DELAY = 20; // By how many px (at least) each (animation back to default state) loop will shift the image private static final float MIN_X_SHIFT = 3; private ImageView mImage; private int mInitialW, mInitialH, mCenterX; private int mMaxMargin; private AnimateBack mAnimateBack; private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler = new Handler(); mImage = (ImageView) findViewById(R.id.imageView); final RelativeLayout imageHolder = (RelativeLayout) findViewById(R.id.imageHolder); mImage.post(new Runnable() { @Override public void run() { // Image ready, measure it mInitialH = mImage.getHeight(); mInitialW = mImage.getWidth(); imageHolder.post(new Runnable() { @Override public void run() { // Calc other measurements int containerWidth = imageHolder.getWidth(); mCenterX = containerWidth / 2; mMaxMargin = containerWidth - mInitialW; } }); } }); imageHolder.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getAction()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mAnimateBack = new AnimateBack(); mAnimateBack.run(); break; case MotionEvent.ACTION_MOVE: mHandler.removeCallbacks(mAnimateBack); if (motionEvent.getX() > mMaxMargin + mInitialW || motionEvent.getX() < 0) { // Fake Action_Up if out of container bounds motionEvent.setAction(MotionEvent.ACTION_UP); onTouch(view, motionEvent); return true; } setSize(motionEvent.getX() - mCenterX); break; } return true; } }); } private void setSize(float offsetFromCenter) { // Calculate new left margin RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mImage.getLayoutParams(); params.leftMargin = (int) (mMaxMargin * offsetFromCenter / (mCenterX - mInitialW / 2.0)); // Calculate dimensions float ratio = 1 - (Math.abs(offsetFromCenter) / mCenterX); params.width = (int) (mInitialW * ratio); params.height = (int) (mInitialH * ratio); mImage.setLayoutParams(params); // Log.e(TAG, String.format("leftMargin: %d, W: %d, H: %d", // params.leftMargin, params.width, params.height)); } private class AnimateBack implements Runnable { private int loopCount, loopDelay; private float loopBy; public AnimateBack() { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mImage.getLayoutParams(); float offsetFromCenter = (float) params.leftMargin / mMaxMargin * (mCenterX - mInitialW / 2.0f); float totalDuration = (Math.abs(offsetFromCenter) * MAX_DURATION / mCenterX); loopBy = MIN_X_SHIFT; loopCount = (int) Math.abs(offsetFromCenter / loopBy); loopDelay = (int) (totalDuration / loopCount); if (loopDelay < MIN_DELAY) { // Use the minimum delay loopDelay = MIN_DELAY; // Minimum loop count is 1 loopCount = (int) Math.max(totalDuration / loopDelay, 1); // Calculate by how much each loop will inc/dec the margin loopBy = Math.round(Math.abs(offsetFromCenter / loopCount)); } Log.d(TAG, String.format("Animate back will take: %fms. Will start from offset %d. " + "It will advance by %dpx every %dms", totalDuration, (int) offsetFromCenter, (int) loopBy, loopDelay)); } @Override public void run() { --loopCount; RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mImage.getLayoutParams(); // Calculate offsetFromCenter float offsetFromCenter = (float) ((float) params.leftMargin / mMaxMargin * (mCenterX - mInitialW / 2.0)); // Don't pass 0 when looping if (params.leftMargin > 0) { offsetFromCenter = Math.max(offsetFromCenter - loopBy, 0); } else { offsetFromCenter = Math.min(offsetFromCenter + loopBy, 0); } setSize(offsetFromCenter); if (loopCount == 0) { mHandler.removeCallbacks(this); } else { mHandler.postDelayed(this, loopDelay); } } } } 

Планировка:

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/imageHolder" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <ImageView android:id="@+id/imageView" android:layout_width="200dp" android:layout_height="200dp" android:src="@drawable/ic_launcher"/> </RelativeLayout> </RelativeLayout> 

Предварительный просмотр:

Введите описание изображения здесь

Intereting Posts
Как отличить прокрутку от пользователя прокрутки Android от прокрутки программы? Сбой загрузки Youtube при загрузке видео в реальном времени Не удалось открыть файл на стороне клиента, попробовав сервер. Ошибка в Android. Переключение фрагментов слишком быстро вызывает: java.lang.IllegalStateException: нет активности Ошибка Social Auth 4.4 :: Недопустимые области: publish_stream. Android WebView не обновляется после того, как содержимое веб-сайта было изменено с помощью JavaScript Кэш данных в общих предпочтениях Android – карта google перемещает камеру с позиции на другую Проблема с подписью Android Экран активности Lollipop Android поврежден Какая зависимость требуется для API осведомленности? OnItemLongClickListener не работает в пользовательском listview Печать штрих-кода с использованием термопринтера Android Запуск нового действия из полноэкранного режима приводит к тому, что строка состояния отображает панель действий Ndk-gdb с несколькими такими libs