Android幻灯片回答类似ImageView动画
我需要在图像视图上实现一个动画,类似于许多Android设备中存在的幻灯片回答动画。 要求:Android幻灯片回答类似ImageView动画,android,imageview,android-animation,Android,Imageview,Android Animation,我需要在图像视图上实现一个动画,类似于许多Android设备中存在的幻灯片回答动画。 要求: 支持API级别>=8(如果不可能,则为9),因此方便是不可能的 拖动图像视图时,将其向右或向左移动,仅需要水平拖动。首先,图像水平居中 拖动时缩放图像视图——越靠近屏幕末端,图像越小 释放拖动时,图像需要设置动画回到屏幕中心,并缩放到其原始大小(也设置动画) 我找到了很多代码示例,并试图自己实现,但所有要求的结合使得实现非常困难,我无法得到一个合适的结果,因此请不要在Google搜索的第一页上添加指向内
我找到了很多代码示例,并试图自己实现,但所有要求的结合使得实现非常困难,我无法得到一个合适的结果,因此请不要在Google搜索的第一页上添加指向内容的链接,因为我已经花了很多天的时间尝试实现这些示例,我希望您能提供一个工作代码示例+xml布局(如果需要)您可以通过结合以下内容来做到这一点,而不会太复杂:
- A(为了检测拖动顺序,基本上是
,动作向下
,…,动作向上移动
)动作向上
- Jake Wharton的优秀库,支持API级别11之前的视图属性动画
ImageView
对象并将View.OnTouchListener
附加到该对象上
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
...
mImage = findViewById(...);
mImage.setOnTouchListener(mTouchListener);
}
其次,编程OnTouchListener
以捕获动作向下
事件并存储初始触摸位置X坐标。然后,对于每个操作_MOVE
计算增量(用于平移和缩放),对于操作_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
标志指示平移和缩放是否应该是立即的(在响应每个移动事件时为真,在返回初始位置和缩放时为假)
您可能需要修改缩放。如前所述,这意味着将图像拖到屏幕边界时,图像将保持其原始大小的50%
这个解决方案在API级别8中应该可以正常工作(尽管我还没有测试它)。完整的要点是您是否需要它。首先想到的是通过
处理程序为布局参数设置动画。不确定它是否符合您的要求,这可能需要更多的测试
在任何情况下,记住数学都是非常有趣的^^^因此,我只使用本机android工具:
代码:
package com.example.simon.draggableimageview;
导入android.os.Handler;
导入android.support.v7.app.ActionBarActivity;
导入android.os.Bundle;
导入android.util.Log;
导入android.view.MotionEvent;
导入android.view.view;
导入android.widget.ImageView;
导入android.widget.RelativeLayout;
/**
*由Simon于2014年11月18日创建。
*/
公共类MainActivity扩展了ActionBarActivity{
私有静态最终字符串TAG=“MainActivity”;
//避免以下两项的小值,否则setSize将开始滞后
//动画(从最小)到默认大小所需的最大时间
专用静态最终整数最大持续时间=500;
//每个环路的最小延迟(ms)
专用静态最终整数最小延迟=20;
//每个(动画返回默认状态)循环将移动图像多少像素(至少)
专用静态最终浮点最小值X移位=3;
私有图像视图;
米尼蒂亚尔、米尼蒂亚尔、麦肯特斯私人酒店;
私有内部mMaxMargin;
私人回溯式人体模型;
私人经理人;
@凌驾
创建时受保护的void(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler=新处理程序();
mImage=(ImageView)findViewById(R.id.ImageView);
最终RelativeLayout imageHolder=(RelativeLayout)findViewById(R.id.imageHolder);
mImage.post(新的Runnable(){
@凌驾
公开募捐{
//图像准备好了,测量一下
mInitialH=mImage.getHeight();
mInitialW=mImage.getWidth();
imageHolder.post(新的Runnable(){
@凌驾
公开募捐{
//计算其他测量值
int containerWidth=imageHolder.getWidth();
mCenterX=集装箱宽度/2;
mMaxMargin=集装箱宽度-最小值;
}
});
}
});
imageHolder.setOnTouchListener(新视图.OnTouchListener(){
@凌驾
公共布尔onTouch(视图、运动事件、运动事件){
开关(motionEvent.getAction()){
case MotionEvent.ACTION\u UP:
case MotionEvent.ACTION\u取消:
mAnimateBack=新的AnimateBack();
mAnimateBack.run();
打破
case MotionEvent.ACTION\u移动:
mHandler.removeCallbacks(假人背部);
if(motionEvent.getX()>mMaxMargin+mInitialW | | motionEvent.getX()<0){
//如果超出容器边界,则假装行动
motionEvent.setAction(motionEvent.ACTION\u UP);
onTouch(视图、运动事件);
返回true;
}
设置大小(motionEvent.getX()-mCenterX);
打破
}
返回true;
}
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();
}
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>