2014-11-14 2 views
5

Ich muss eine Animation auf einem ImageView implementieren, ähnlich wie die Folie, um die Animation zu beantworten, die in vielen Android-Geräten existiert. Die requiremnts:Android-Folie zu beantworten wie ImageView Animation

  1. Unterstützung API-Level> = 8 (falls nicht möglich, dann 9), so dass die praktische drag listener ist indiskutabel
  2. Umzug der Image rechts oder links, wenn sie ziehen, nur horizontale Ziehen ist erforderlich. Zuerst wird das Bild horizontal zentriert.
  3. Skalieren Sie die ImageView beim Ziehen - je näher Sie dem Ende des Bildschirms kommen, desto kleiner wird das Bild.
  4. wenn das Ziehen Loslassen, müssen das Bild in die Mitte des Bildschirms animieren zurück, und das Ausmaß seiner Herkunft Größe (auch animiert)

ich viele Code-Beispiele gefunden und versucht, es auf eigene Faust zu implementieren aber die Kombination aller Anforderungen machte es sehr schwierig und ich konnte kein anständiges Ergebnis erzielen, also bitte keine Links von der ersten Seite einer Google-Suche einfügen, da ich viele Tage damit verbracht habe, diese Beispiele zu implementieren Ich würde ein funktionierendes Codebeispiel + XML-Layout (falls erforderlich) zu schätzen wissen.

Antwort

6

Sie können dies ohne viel Komplikation tun, indem Sie kombinieren:

  • A View.OnTouchListener (um die Schleppsequenz zu erkennen, im Grunde ACTION_DOWN, ACTION_MOVE, ..., ACTION_UP).
  • Jake Wharton ausgezeichnete NineOldAndroids library Bibliothek Ansicht Eigenschaft Animationen vor API-Ebene 11.

Zunächst erhalten die ImageView Objekt und fügen Sie die View.OnTouchListener auf, sie zu unterstützen.

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

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

Zweitens Programm der OnTouchListener das ACTION_DOWN Ereignis erfassen und speichern die anfängliche Berührungsposition X-Koordinate. Dann für jede ACTION_MOVE berechnen Sie das Delta (für die Übersetzung und Skalierung) und für ACTION_UP die ImageView in den Ausgangszustand zurück.

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

Die animateTo() würde wie folgt umgesetzt werden. Das immediate-Flag gibt an, ob die Übersetzung und die Skalierung, nun, unmittelbar (es ist wahr, wenn auf jedes Bewegungsereignis reagiert, und falsch, wenn es zurück in seine ursprüngliche Position und seine Skalierung erleichtert).

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

Sie können mit der Skalierung basteln wollen. Wie geschrieben, bedeutet dies, dass das Bild 50% seiner ursprünglichen Größe hat, wenn es bis zum Rand des Bildschirms gezogen wird.

Diese Lösung sollte ohne Probleme in API Level 8 funktionieren (obwohl ich es nicht getestet habe). Voller Geist ist available here, wenn Sie es wollen.

+0

funktioniert wie Charme :) danke – Orr

4

Die erste Sache, die in den Sinn kam, animierte die LayoutParams, über eine Handler. Nicht sicher, ob es Ihren Anforderungen entspricht und das erfordert wahrscheinlich einige weitere Tests.

In jedem Fall ist es ziemlich Spaß Erinnerung an die Mathe ^^ war also hier ist mein geht an ihn, nur mit den nativen Android-Tools:

Code:

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

} 

Layout:

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

Vorschau:

enter image description here