Android幻灯片回答像ImageView动画一样

时间:2014-11-14 22:28:12

标签: android imageview android-animation

我需要在 ImageView 上实现动画,类似于幻灯片来回答许多Android设备中存在的动画。 要求:

  1. 支持API级别> = 8(如果不可能,则为9),因此方便的drag listener是不可能的
  2. 拖动时向右或向左移动 ImageView ,只需要水平拖动。首先,图像水平居中。
  3. 在拖动时缩放 ImageView - 距离屏幕末端越近,图像越小。
  4. 释放拖动时,图像需要动画回到屏幕中心,并缩放到原点大小(也是动画)
  5. 我找到了很多代码示例,并试图自己实现它,但所有要求的组合使得它非常困难,我无法得到一个不错的结果,所以请不要把链接放到东西上从谷歌搜索的第一页开始,因为我花了很多天试图实现这些示例,我将非常感谢一个有效的代码示例+ xml布局(如果需要)

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:4)

首先想到的是通过LayoutParamsHandler设置动画。不确定它是否符合您的要求,这可能需要更多测试。

在任何情况下记住数学都很有趣^^所以这里是我的主题,只使用原生的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>

预览:

enter image description here