2014-11-14 15 views
5

ho bisogno di implementare un animazione su un ImageView, simile alla diapositiva per rispondere di animazione che esiste in molti dispositivi Android. I requiremnts:slitta Android per rispondere come ImageView animazione

  1. livello di supporto API> = 8 (se non è possibile quindi 9), in modo conveniente drag listener è fuori questione
  2. mossa ImageView destra o sinistra quando trascinandolo, solo orizzontale è necessario il trascinamento. All'inizio l'immagine è centrata orizzontalmente.
  3. Ridimensiona lo ImageView durante il trascinamento: più vicino alla fine dello schermo, minore sarà l'immagine.
  4. quando si rilascia il trascinamento, l'immagine necessità di animare di nuovo al centro dello schermo, e la scala alla sua dimensione di origine (anche animate)

ho trovato molti esempi di codice e cercato di attuare da sola ma la combinazione di tutti i requisiti ha reso molto difficile e non ho potuto ottenere un risultato decente, quindi per favore non mettere i link alla prima pagina di una ricerca su Google poiché ho passato molti giorni a provare a implementare questi esempi , Mi piacerebbe un esempio di codice funzionante + layout xml (se necessario)

risposta

6

Puoi farlo senza troppe complicazioni combinando:

  • A View.OnTouchListener (per rilevare la sequenza di trascinamento, in pratica ACTION_DOWN, ACTION_MOVE, ..., ACTION_UP). eccellente NineOldAndroids library biblioteca
  • Jake Wharton, per supportare le animazioni vista proprietà prima livello di API 11.

In primo luogo, ottenere l'oggetto ImageView e collegare il View.OnTouchListener ad esso.

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

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

In secondo luogo, il programma OnTouchListener per catturare l'evento ACTION_DOWN e memorizzare la coordinata iniziale tocco posizione X. Quindi, per ogni ACTION_MOVE calcolare il delta (per la conversione e il ridimensionamento) e per ACTION_UP restituire ImageView allo stato iniziale.

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

Il animateTo() sarebbe calcolato come segue. Il flag immediate indica se la conversione e il ridimensionamento devono essere, beh, immediati (è vero quando si risponde a ciascun evento di spostamento e falso quando si ritorna alla sua posizione iniziale e scala).

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

Si consiglia di armeggiare con il ridimensionamento. Come scritto, significa che l'immagine sarà al 50% della sua dimensione originale quando viene trascinata fino al bordo dello schermo.

Questa soluzione dovrebbe funzionare senza problemi in livello API 8 (sebbene non l'abbia provata). L'essenza completa è available here se lo desideri.

+0

funziona come il fascino :) grazie – Orr

4

La prima cosa che mi è venuta in mente è stata animare lo LayoutParams, tramite un Handler. Non sono sicuro che soddisferà i tuoi requisiti e questo probabilmente richiede un po 'più di test.

In ogni caso è stato abbastanza divertente ricordare la matematica ^^ Quindi, ecco il mio andare a esso, utilizzando solo gli strumenti nativo Android:

Codice:

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

} 

Disposizione:

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

Anteprima:

enter image description here

Problemi correlati