2014-11-14 58 views
5

我需要在ImageView上實現動畫,類似於許多Android設備中存在的幻燈片來回答動畫。 的requiremnts:安卓幻燈片回答像ImageView動畫

  1. 支持API級> = 8(如果不可能,那麼9),所以方便drag listener是不可能的
  2. 動議ImageView的右或拖動時,它離開,僅水平拖動是必需的。首先圖像水平居中。
  3. 在拖動時縮放ImageView - 越靠近屏幕末端,圖像越小。
  4. 釋放拖動時,圖像需要動畫回到屏幕的中心,和規模它的起源大小(也動畫)

我發現很多代碼樣本,並試圖實現它在我自己但所有這些要求的組合使得它變得非常困難,並且我無法得到一個體面的結果,所以請不要將鏈接指向谷歌搜索的第一頁的內容,因爲我花了很多時間來試圖實現這些示例,我將不勝感激一個工作代碼示例+ xml佈局(如果需要)

回答

6

你可以做到這一點沒有太複雜的結合:

首先,獲得ImageView對象並附上View.OnTouchListener它。

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

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

其次,程序OnTouchListener捕獲ACTION_DOWN事件和存儲初始觸摸位置的X座標。然後爲每個ACTION_MOVE計算delta(用於平移和縮放),並且用於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標誌指示翻譯和縮放是否應該是即時的(對每個移動事件作出響應時爲真,在恢復初始位置和縮放時爲假)。

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沒有問題的情況下工作(儘管我沒有測試過它)。如果你想要的話,完整的要點是available here

+0

作品像魅力:)謝謝 – Orr 2014-11-18 10:45:33

4

首先想到的是動畫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> 

Preview:

enter image description here