2014-11-14 14 views
5

muszę wdrożyć Animacja na ImageView, podobny do suwaka odpowiedzieć animację, która istnieje w wielu urządzeniach z Androidem. W requiremnts:Android slajdów odpowiedzieć jak animacji ImageView

  1. poziomu wsparcia API> = 8 (jeżeli nie jest to możliwe wtedy 9), a więc wygodny drag listener jest wykluczone
  2. Przesuń ImageView prawo lub w lewo, podczas przeciągania go, tylko poziome przeciąganie jest wymagane. Początkowo obraz jest wyśrodkowany w poziomie.
  3. Skala ImageView podczas przeciągania - im bliżej końca ekranu, tym mniejszy będzie obraz.
  4. po zwolnieniu przeciąganie, obraz musi animować powrotem do środka ekranu, a skala jego wielkości pochodzenia (także animowane)

Znalazłem wiele próbek kodu i próbuje wdrożyć go na własną rękę ale połączenie wszystkich wymagań sprawiło, że było to bardzo trudne i nie mogłem uzyskać przyzwoitego wyniku, więc proszę nie umieszczać linków do rzeczy z pierwszej strony wyszukiwarki Google, ponieważ spędziłem wiele dni próbując wdrożyć te przykłady , Doceniłbym działający przykład kodu + układ xml (jeśli to konieczne)

Odpowiedz

6

Możesz to zrobić bez większych komplikacji, łącząc:

  • A View.OnTouchListener (w celu wykrycia sekwencji przeciągania, w zasadzie ACTION_DOWN, ACTION_MOVE, ..., ACTION_UP).
  • Jake Wharton doskonała NineOldAndroids library biblioteka, wspieranie animacji widok własności przed poziomie API 11.

pierwsze, uzyskać obiektu ImageView i przymocować View.OnTouchListener do niego.

@Override 
protected void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 

    ... 
    mImage = findViewById(...); 
    mImage.setOnTouchListener(mTouchListener); 
} 

drugie zaprogramować OnTouchListener uchwycić zdarzenia ACTION_DOWN i przechowywać współrzędnych początkowa pozycja X dotyku. Następnie dla każdego oblicza się deltę (dla translacji i skalowania), a dla ACTION_UP zwraca ImageView do swojego stanu początkowego.

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; 
    } 
}; 

The animateTo() zostanie wdrożony w następujący sposób. Flaga immediate wskazuje, czy tłumaczenie i skalowanie powinny być, no cóż, natychmiastowe (jest to prawdziwe w przypadku reagowania na każde zdarzenie przenoszenia i fałszywe po powrocie do początkowej pozycji i skali).

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(); 
} 

Możesz chcieć majstrować przy skalowaniu. Zgodnie z zapisami oznacza to, że obraz będzie miał 50% pierwotnego rozmiaru po przeciągnięciu do krawędzi ekranu.

To rozwiązanie powinno działać bez problemów na poziomie API 8 (choć tego nie testowałem). Pełny sens to available here, jeśli chcesz.

+0

działa jak urok :) dzięki – Orr

4

Pierwsza rzecz, która przyszła na myśl, to animacja LayoutParams, poprzez Handler. Nie jestem pewien, czy spełni on twoje wymagania, a to prawdopodobnie wymaga więcej testów.

W każdym razie to było całkiem zabawne pamiętając matematyki ^^ Więc oto moja go na to, używając tylko rodzimych android narzędzia:

Kod:

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); 
      } 
     } 
    } 

} 

Układ:

<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> 

Podgląd:

enter image description here