动画
2019-12-29 本文已影响0人
匠人plus
Android中的动画主要分为补间动画、帧动画和属性动画。
1.补间动画与帧动画都是canvas对matrix的操作,所以并没有改变view的实际属性。
补间动画主要有:
AlphaAnimation、TranslateAnimation、ScaleAnimation、RotateAnimation
以下是一个demo的效果图:
![](https://img.haomeiwen.com/i5161724/24ba922c467ebe51.gif)
以下是示例代码:
public class AnimView extends View{
private AnimationSet animationSet;
public AnimView(Context context) {
super(context);
init();
}
public AnimView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public AnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public AnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
// 透明度动画
public Animation alpha() {
AlphaAnimation alphaAnimation = new AlphaAnimation(0.2f, 1);
alphaAnimation.setDuration(3000);// 每次动画持续时间3秒
alphaAnimation.setFillAfter(true);// 动画最后是否停留在终止的状态
alphaAnimation.setRepeatCount(3);// 动画重复的次数
alphaAnimation.setRepeatMode(Animation.REVERSE);// REVERSE: 反转模式
// RESTART:重新开始
// 动画监听
alphaAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
Log.e("alphaAnimation","动画开始回调");
}
@Override
public void onAnimationRepeat(Animation animation) {
Log.e("alphaAnimation","动画重复回调");
}
@Override
public void onAnimationEnd(Animation animation) {
Log.e("alphaAnimation","动画结束回调");
}
});
return alphaAnimation;
}
public Animation translate() {
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.ABSOLUTE,
this.getWidth(), // 当前屏幕密度 :240 标准的屏幕密度:160 则dp转px :
// px=dp*240/160
Animation.ABSOLUTE, this.getWidth(),
Animation.ABSOLUTE, 0,
Animation.RELATIVE_TO_SELF, 1);
translateAnimation.setDuration(3000);// 每次动画持续时间3秒
translateAnimation.setFillAfter(true);// 动画最后停留在终止的状态
translateAnimation.setRepeatCount(3);// 动画重复的次数
translateAnimation.setRepeatMode(Animation.REVERSE);// REVERSE: 反转模式
// RESTART:重新开始
translateAnimation.setInterpolator(new BounceInterpolator());// 设置特效,弹簧效果
return translateAnimation;
}
public Animation scale() {
ScaleAnimation scaleAnimation =new ScaleAnimation
(0, 2, 0, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(3000);// 每次动画持续时间3秒
scaleAnimation.setFillAfter(true);// 动画最后停留在终止的状态
scaleAnimation.setRepeatCount(1);// 动画重复的次数
this.startAnimation(scaleAnimation);
return scaleAnimation;
}
public Animation rotate() {
RotateAnimation rotateAnimation =new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
LinearInterpolator lin = new LinearInterpolator();
rotateAnimation.setInterpolator(lin);
rotateAnimation.setDuration(1500);//设置动画持续时间
rotateAnimation.setRepeatCount(5);//设置重复次数
rotateAnimation.setFillAfter(true);//动画执行完后是否停留在执行完的状态
rotateAnimation.setStartOffset(10);//执行前的等待时间
return rotateAnimation;
}
public void execAnim(){
animationSet=new AnimationSet(false);
animationSet.addAnimation(alpha());
animationSet.addAnimation(translate());
animationSet.addAnimation(scale());
animationSet.addAnimation(rotate());
//设置动画结束之后是否保持动画的目标状态
animationSet.setFillAfter(false);
//设置动画结束之后是否保持动画开始时的状态
animationSet.setFillBefore(true);
this.startAnimation(animationSet);
}
private void init(){
this.setBackgroundColor(Color.RED);
execAnim();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (animationSet!=null){
animationSet.cancel();
animationSet.reset();
}
}
}
2.属性动画是通过设置view的属性,不断刷新视图实现的,所以属性动画实际改变了view的属性。可以通过属性动画改变view的宽、高、透明度等,以及其他自定义属性
属性动画主要有:ValueAnimator、ObjectAnimator
ValueAnimator是属性动画框架的基础,它实现了动画相关的接口,
ObjectAnimator继承自ValueAnimator,它将Object与动画绑定,可以方便的对某些属性进行改变,以达到快速实现动画效果的目的。
以下是属性动画两个重要的方法,
setInterpolator//设置插值器,用于模拟动画的自然进度
Interpolator实现TimeInterpolator接口,
主要有以下实现类
AccelerateDecelerateInterpolator、AccelerateInterpolator、
DecelerateInterpolator、LinearInterpolator、BounceInterpolator、
AnticipateInterpolator、OvershootInterpolator、AnticipateOvershootInterpolator、CycleInterpolator。
setEvaluator//设置估值器,用于修改动画的轨迹
Evaluator实现TypeEvaluator接口,
主要实现类IntEvaluator、FloatEvaluator、ArgbEvalutor
ArgbEvalutor可以方便的实现颜色值过渡转换。
以下例子通过LoopTextView,展示ObjectAnimator的常规用法。
public class LoopTextView extends RelativeLayout {
private Context context;
private MyTextView textView1;
private MyTextView textView2;
private List<String> dataList;
private AnimatorSet animSet;
private boolean circleAnim = true;
private int animPos = 0;
private int viewHeight = 0;
private int repeatTimes = ValueAnimator.INFINITE;
private int repeatCount = 0;
public LoopTextView(Context context) {
super(context);
initView(context);
}
public LoopTextView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public LoopTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
public LoopTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView(context);
}
private void initView(Context context) {
this.context = context;
textView1 = new MyTextView(context);
addView(textView1, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
textView2 = new MyTextView(context);
addView(textView2, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
textView1.setGravity(Gravity.CENTER);
textView2.setGravity(Gravity.CENTER);
textView1.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
textView2.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
// textView1.setBackgroundColor(Color.RED);
// textView2.setBackgroundColor(Color.BLUE);
}
public void setDataList(List<String> stringList) {
this.dataList = stringList;
}
public void setCircleTimes(int repeatTimes) {
this.repeatTimes = repeatTimes;
}
private void initAnim() {
if (dataList == null || dataList.size() == 0) {
return;
}
// ObjectAnimator anim1 = ObjectAnimator.ofFloat(textView1, View.TRANSLATION_Y, 0.0f, -1.0f * viewHeight);
// ObjectAnimator anim2 = ObjectAnimator.ofFloat(textView2, View.TRANSLATION_Y, 1.0f * viewHeight, 0.0f);
final ObjectAnimator anim1 = ObjectAnimator.ofInt(textView1, "MyTranslationY", 0, -1 * viewHeight);
ObjectAnimator anim2 = ObjectAnimator.ofInt(textView2, "MyTranslationY", 1 * viewHeight, 0);
anim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Object value = animation.getAnimatedValue();
// Log.e("anim1Update", value + "");
}
});
anim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Object value = animation.getAnimatedValue();
// Log.e("anim2Update", value + "");
}
});
animSet = new AnimatorSet();
animSet.setDuration(2000);
animSet.setInterpolator(new AccelerateDecelerateInterpolator());
//两个动画同时执行
animSet.playTogether(anim1, anim2);
animSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
if (circleAnim) {
repeatCount = (animPos + 1) / (dataList.size() - 1);
if (repeatCount != repeatTimes) {
postDelayed(new Runnable() {
@Override
public void run() {
++animPos;
circleTextAnim();
}
}, 300);
} else {
Toast.makeText(getContext(), "anim finish", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
circleTextAnim();
}
private void print() {
}
private void circleTextAnim() {
circleAnim = true;
textView1.setText(dataList.get(animPos % dataList.size()));
textView2.setText(dataList.get((animPos + 1) % dataList.size()));
textView1.setEachCharRandomTextColor();
textView2.setShowLinearGradient(true);
animSet.start();
}
public void cancelTextAnim() {
circleAnim = false;
animSet.cancel();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
cancelTextAnim();
}
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return super.drawChild(canvas, child, drawingTime);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.e("onSizeChanged", h + "");
viewHeight = h;
initAnim();
}
@SuppressLint("AppCompatCustomView")
class MyTextView extends TextView {
private Paint mPaint;
private int mViewWidth;
private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private int mTransalte;
Random random = new Random();
private boolean showLinearGradient;
private Timer timer;
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setMyTranslationY(int translationY) {
super.setTranslationY(translationY);
}
public float getMyTranslationY() {
return super.getTranslationY();
}
public void setRandomTextColor() {
setTextColor(getRandomTextColor());
}
public int getRandomTextColor() {
return random.nextInt(0xffffff + 1) + 0xff000000;
}
public void setEachCharRandomTextColor() {
SpannableString span = new SpannableString(getText());
for (int i = 0; i < getText().length(); i++) {
span.setSpan(new ForegroundColorSpan(getRandomTextColor()), i, i + 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
setText(span);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth == 0) {
mViewWidth = getMeasuredWidth();
if (if (showLinearGradient && mViewWidth > 0) {
mPaint = getPaint();//获得当前绘制的Paint对象
mLinearGradient = new LinearGradient(
0,//渐变起始点x坐标
0,//渐变起始点y坐标
mViewWidth,//渐变结束点x点坐标
0,//渐变结束点y坐标
new int[]{
Color.RED, 0xffffffff,
Color.BLUE, Color.RED, Color.YELLOW},//颜色的int数组
null,//相对位置的颜色数组,可为null, 若为null,可为null,颜色沿渐变线均匀分布
Shader.TileMode.MIRROR);//REPEAT
mPaint.setShader(mLinearGradient);//给这个paint设置linearFradient属性
mGradientMatrix = new Matrix();
drawMatrix();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (showLinearGradient && mGradientMatrix != null) {
mTransalte += mViewWidth / 5;
if (mTransalte > 2 * mViewWidth) {
mTransalte -= mViewWidth;
}
mGradientMatrix.setTranslate(mTransalte, 0);
mLinearGradient.setLocalMatrix(mGradientMatrix);//通过矩阵的方式不断平移产生渐变效果
}
}
private void drawMatrix() {
if (showLinearGradient && mGradientMatrix != null) {
if (timer != null) {
timer.cancel();
timer = null;
}
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
post(new Runnable() {
@Override
public void run() {
invalidate();
}
});
}
},0,100);
}
}
public void setShowLinearGradient(boolean showLinearGradient) {
this.showLinearGradient = showLinearGradient;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (timer != null) {
timer.cancel();
timer = null;
}
}
}
}
调用方法:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:orientation="vertical"
tools:context="lpc.org.gradletest.MainActivity">
<com.test.anim.LoopTextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:id="@+id/looptv"
android:background="@android:color/holo_green_light"></com.test.anim.LoopTextView>
<com.test.anim.AnimView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerInParent="true"/>
</RelativeLayout>
LoopTextView looptv = findViewById(R.id.looptv);
List<String> data = new ArrayList<>();
for (int i = 0; i < 10; i++) {
data.add("this is"+i);
}
looptv.setDataList(data);
// looptv.setCircleTimes(1);