我需要在 ImageView 上实现动画,类似于幻灯片来回答许多Android设备中存在的动画。 要求:
我找到了很多代码示例,并试图自己实现它,但所有要求的组合使得它非常困难,我无法得到一个不错的结果,所以请不要把链接放到东西上从谷歌搜索的第一页开始,因为我花了很多天试图实现这些示例,我将非常感谢一个有效的代码示例+ xml布局(如果需要)
答案 0 :(得分:6)
通过组合:
,您可以轻松完成此操作View.OnTouchListener
(用于检测拖动序列,基本上是ACTION_DOWN
,ACTION_MOVE
,...,ACTION_UP
)。首先,获取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)
首先想到的是通过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>
预览: