Android实现箭头无限循环上升的简单动画
需求说明
一个箭头图片在某个区域做上升的动画,到区域顶部时,消失的部分又从底部出现,如此循环往复。
先看实现的效果图,如下。
缺点:实际上并不是连贯的循环重复,而是第二次上升的箭头消失并没有再从底部出来,这是用简单动画实现的劣势。
优点:简单,便于理解和实现。
原理说明
用两张图片箭头一起做向上平移的动画,就可实现该功能。
一张图片放在运动区域下方,另一张图片放在运动区域的两部位置下方,然后一起使用同一个平移相同距离的动画,使之实现上述效果。
代码实现
首先布局设定好ImageView的具体宽高,然后用marginBottom负数的方式使之置于运动区域下方,第二个箭头margin的距离是运动区域的高度+自身的高度。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".animation.ArrowMoveActivity"
tools:showIn="@layout/activity_arrow_move">
<FrameLayout
android:id="@+id/fl_move_area"
android:background="@color/color_list_divider"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.494"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/iv_arrow_a"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@mipmap/arraw_up"
android:scaleType="centerCrop"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="-60dp"
android:contentDescription="arrowA" />
<ImageView
android:id="@+id/iv_arrow_b"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@mipmap/arraw_up"
android:scaleType="centerCrop"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="-360dp"
android:contentDescription="arrowB" />
</FrameLayout>
</android.support.constraint.ConstraintLayout>
其次是获得向上平移的具体距离,具体距离=2倍运动区域的高度+箭头本身的高度。
由于布局时写死了高度,所以可以直接得出。
/方式一:直接换算运动距离,因为高度已经限定
//向上平移的距离计算,等于2倍运动区域高度+自身的高度
float moveDistance = 660 * getResources().getDisplayMetrics().density;
还有另一种方式就是用代码计算出高度,这一定要在界面布局完成后才得测量出View的具体高度,既然要写死高度才能实现动画效果,再用代码计算就有点画蛇添足了,仅供参考。
ivArrowA.post(new Runnable() {
@Override
public void run() {
//向上平移的距离计算,等于2倍运动区域高度+自身的高度
float moveDistance = flMoveArea.getHeight()*2 + ivArrowA.getHeight();
}
});
最后则是开始平移动画无限循环,记得destroy时要将之移除。
//生成一个平移动画对象,记得向上运动是负数
Animation moveUpAnimation = new TranslateAnimation(0,0,0,-moveDistance);
moveUpAnimation.setDuration(3000);//3秒运动完一次循环
moveUpAnimation.setRepeatCount(Animation.INFINITE); //无限循环
moveUpAnimation.setInterpolator(new LinearInterpolator()); //匀速运动
//开始视图动画
ivArrowA.startAnimation(moveUpAnimation);
ivArrowB.startAnimation(moveUpAnimation);
总结
这个方案比较简陋一些,布局里也要设定死ImageView和运动区域的大小,相当不灵活,当然最佳的方案就是用自定义View,但暂时还没有思路实现。
如果大家有更好的解决方案,欢迎一起讨论。
最后贴上Activity的代码
package cn.pigdreams.blogdemo.animation;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import cn.pigdreams.blogdemo.R;
/**
*
*/
public class ArrowMoveActivity extends AppCompatActivity {
private FrameLayout flMoveArea;
private ImageView ivArrowA;
private ImageView ivArrowB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_arrow_move);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
initView();
startArrowMove();
}
/**
* 开始平移动画
*/
private void startArrowMove() {
//方式一:直接换算运动距离,因为高度已经限定
//向上平移的距离计算,等于2倍运动区域高度+自身的高度
float moveDistance = 660 * getResources().getDisplayMetrics().density;
//生成一个平移动画对象,记得向上运动是负数
Animation moveUpAnimation = new TranslateAnimation(0,0,0,-moveDistance);
moveUpAnimation.setDuration(3000);//3秒运动完一次循环
moveUpAnimation.setRepeatCount(Animation.INFINITE); //无限循环
moveUpAnimation.setInterpolator(new LinearInterpolator()); //匀速运动
//开始视图动画
ivArrowA.startAnimation(moveUpAnimation);
ivArrowB.startAnimation(moveUpAnimation);
//方式二:代码计算运动距离,一定要等布局完成后才能开始动画
// ivArrowA.post(new Runnable() {
// @Override
// public void run() {
// //向上平移的距离计算,等于2倍运动区域高度+自身的高度
// float moveDistance = flMoveArea.getHeight()*2 + ivArrowA.getHeight();
// //生成一个平移动画对象,记得向上运动是负数
// Animation moveUpAnimation = new TranslateAnimation(0,0,0,-moveDistance);
// moveUpAnimation.setDuration(3000);//3秒运动完一次循环
// moveUpAnimation.setRepeatCount(Animation.INFINITE); //无限循环
// moveUpAnimation.setInterpolator(new LinearInterpolator()); //匀速运动
// //开始视图动画
// ivArrowA.startAnimation(moveUpAnimation);
// ivArrowB.startAnimation(moveUpAnimation);
// }
// });
}
private void initView() {
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
flMoveArea = findViewById(R.id.fl_move_area);
ivArrowA = findViewById(R.id.iv_arrow_a);
ivArrowB = findViewById(R.id.iv_arrow_b);
}
@Override
protected void onDestroy() {
ivArrowA.clearAnimation();
ivArrowB.clearAnimation();
super.onDestroy();
}
}