/**
* 動畫 測試
* @author hufeiyang
*/
public class AnimationTestActivity extends AppCompatActivity {
private Unbinder unbinder;
@BindView(R.id.textView1)
TextView textView1;
@BindView(R.id.textView2)
TextView textView2;
@BindView(R.id.textView3)
TextView textView3;
@BindView(R.id.textView4)
TextView textView4;
@BindView(R.id.button_animator_test)
Button button;
private AnimatorSet mAnimatorSet;
public static void launch(Activity activity) {
Intent intent = new Intent(activity, AnimationTestActivity.class);
activity.startActivity(intent);
//overridePendingTransition必須位於startActivity會finish的後面,纔會有動畫效果
activity.overridePendingTransition(R.anim.enter_from_right, 0);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animation_test);
unbinder = ButterKnife.bind(this);
viewAnimationTest();
objectAnimatorTest();
testAnimatorAboutButtonWidth();
}
/**
* 屬性動畫test
* 屬性動畫幾乎無所不能,只要對象有這個屬性,就可以對這個屬性做動畫。
* 原理:內部通過反射 調用對應屬性的set、get方法。
*
*
* 差值器:Interpolator,根據 時間流逝的百分比,計算,當前屬性值改變的百分比。
* 例如duration是1000,start後過了200,那麼時間百分比是0.2,那麼如果差值器是LinearInterpolator線性差值器,那麼屬性值改變的百分比也是0.2
*
* 估值器:Evaluator,就是根據 差值器獲取的 屬性值百分比,計算改變後的屬性值。
* ofInt、onFloat內部會自動設置IntEvaluator、FloatEvaluator。如果使用ofInt且是顏色相關的屬性,就要設置ArgbEvaluator。見下面 文字顏色變化 的例子。
*
*/
private void objectAnimatorTest() {
//屬性動畫使用,方式一:代碼,建議使用。
//setTranslationX,像view動畫一樣,view實際的layout位置沒變,改變了視圖位置,但是 給觸摸點生效區域增加了位移。(而view動畫僅改變了視圖位置)
ObjectAnimator translationX = ObjectAnimator
.ofFloat(textView4, "translationX", 0, 200)
.setDuration(1000);
translationX.setInterpolator(new LinearInterpolator());
setAnimatorListener(translationX);
//屬性動畫使用,方式二:xml。
Animator animatorUpAndDown = AnimatorInflater.loadAnimator(this, R.animator.animator_test);
animatorUpAndDown.setTarget(textView4);
//文字顏色變化
ObjectAnimator textColor = ObjectAnimator
.ofInt(textView4, "textColor", 0xffff0000, 0xff00ffff)
.setDuration(1000);
textColor.setRepeatCount(ValueAnimator.INFINITE);
textColor.setRepeatMode(ValueAnimator.REVERSE);
//注意,這裏如果不設置 那麼顏色就是跳躍的,設置ArgbEvaluator 就是連續過度的顏色變化
textColor.setEvaluator(new ArgbEvaluator());
//animatorSet
mAnimatorSet = new AnimatorSet();
mAnimatorSet
.play(animatorUpAndDown)
.with(textColor)
.after(translationX);
mAnimatorSet.start();
}
/**
* 對任意屬性做動畫 要求兩個條件:
* 1、object有對應屬性的set方法,沒設置初始值還要有get方法。
* 2、set方法要對object有所改變,如UI的變化。(如TextView調用setWidth方法卻不會改變UI,那就不產生動畫)
*
* 當不滿足條件時,有如下處理方法:
* 1、給object添加set、get方法,如果有權限。(一般不行,如TextView是SDK裏面的不能直接改)
* 2、給Object包裝一層,在包裝類中提供set、get方法。{@link #}
* 3、使用ValueAnimator,監聽Value變化過程,自己實現屬性的改變。
*/
private void testAnimatorAboutButtonWidth() {
//Button width 屬性動畫:如果xml中寬度是wrap_content,那麼動畫有效。
// 如果設置button確切的dp值,那麼無效,因爲對應屬性"width"的setWidth()方法就是 在wrap_content是纔有效。
ObjectAnimator width1 = ObjectAnimator.ofInt(button, "width", 600);
width1.setDuration(2000);
// width1.start();
//那麼,想要在button原本有確切dp值時,要能對width動畫,怎麼做呢?
//方法一,包一層,然後用layoutParams
ViewWrapper wrapper = new ViewWrapper(button);
ObjectAnimator width2 = ObjectAnimator.ofInt(wrapper, "width", 600);
width2.setDuration(2000);
// width2.start();
//方法二,使用ValueAnimator,每一幀自己顯示寬度的變化
ValueAnimator valueAnimator = ValueAnimator.ofInt(button.getLayoutParams().width, 600);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (Integer) animation.getAnimatedValue();
Log.i("hfy", "onAnimationUpdate: animatedValue="+animatedValue);
// IntEvaluator intEvaluator = new IntEvaluator();
//// 獲取屬性值改變比例、計算屬性值
// float animatedFraction = animation.getAnimatedFraction();
// Integer evaluate = intEvaluator.evaluate(animatedFraction, 300, 600);
// Log.i("hfy", "onAnimationUpdate: evaluate="+evaluate);
button.getLayoutParams().width = animatedValue;
button.requestLayout();
}
});
valueAnimator.setDuration(4000).start();
}
private void setAnimatorListener(ObjectAnimator translationX) {
translationX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//每播放一幀,都會調用
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
translationX.addPauseListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationResume(Animator animation) {
super.onAnimationResume(animation);
}
});
}
translationX.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
}
/**
* view動畫test
*/
private void viewAnimationTest() {
//view動畫使用,方式一:xml,建議使用。
Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_test);
textView1.startAnimation(animation);
//view動畫使用,方式二:new 動畫對象
AnimationSet animationSet = new AnimationSet(false);
animationSet.setDuration(3000);
animationSet.addAnimation(new TranslateAnimation(0, 100, 0, 0));
animationSet.addAnimation(new ScaleAnimation(0.1f, 1f, 0.1f, 1f));
animationSet.setFillAfter(true);
textView2.startAnimation(animationSet);
//view動畫使用,方式二:new 動畫對象,使用setAnimation
AnimationSet animationSet2 = new AnimationSet(false);
animationSet2.setDuration(3000);
animationSet2.addAnimation(new TranslateAnimation(0, 100, 0, 0));
animationSet2.addAnimation(new ScaleAnimation(0.1f, 1f, 0.1f, 1f));
animationSet2.setFillAfter(true);
animationSet2.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
MyToast.showMsg(AnimationTestActivity.this, "View動畫:代碼 set:View動畫結束~");
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
textView3.setAnimation(animationSet2);
}
@OnClick({R.id.button_animator_test,
R.id.textView4})
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
// linearLayout.addView(new Button(this));
break;
case R.id.textView4:
MyToast.showMsg(this, "點了textView4");
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
if (mAnimatorSet != null && mAnimatorSet.isRunning()) {
mAnimatorSet.cancel();
}
}
@Override
public void finish() {
super.finish();
overridePendingTransition(0, R.anim.exit_to_right);
}
/**
* 包一層,提供對應屬性的set、get方法
*/
private class ViewWrapper {
private final View mView;
public ViewWrapper(View view) {
mView = view;
}
public int getWidth(){
return mView.getLayoutParams().width;
}
public void setWidth(int width){
ViewGroup.LayoutParams layoutParams = mView.getLayoutParams();
layoutParams.width = width;
mView.setLayoutParams(layoutParams);
mView.requestLayout();
}
}
}
R.anim.animation_test
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:duration="5000"> <!--set裏面的duration如果有值,會覆蓋子標籤的duration-->
<translate
android:fromXDelta="0"
android:toXDelta="200"
android:duration="1000"/>
<scale android:fromXScale="0.5"
android:toXScale="1"
android:fromYScale="0.5"
android:toYScale="1"
android:duration="2000"/>
<alpha android:fromAlpha="0.2"
android:toAlpha="1"
android:duration="3000"/>
</set>
R.animator.animator_test
<?xml version="1.0" encoding="utf-8"?>
<!--屬性動畫test,一般建議採用代碼實現,不用xml-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">
<!--repeatCount:默認是0,-1是無限循環-->
<!--repeatMode:重複模式:restart-從頭來一遍、reverse-反向來一遍-->
<!--valueType:指定propertyName的類型可選intType、floatType-->
<!--android:pathData=""
android:propertyXName=""
android:propertyYName=""-->
<objectAnimator
android:propertyName="translationY"
android:duration="1000"
android:valueFrom="0"
android:valueTo="200"
android:startOffset="0"
android:repeatCount="0"
android:repeatMode="reverse"
android:valueType="floatType"
android:interpolator="@android:interpolator/accelerate_decelerate" />
<!--animator對用vueAnimator,比objectAnimator少了propertyName-->
<!--<animator-->
<!--android:duration="2000"-->
<!--android:valueFrom=""-->
<!--android:valueTo=""-->
<!--android:startOffset=""-->
<!--android:repeatCount=""-->
<!--android:repeatMode=""-->
<!--android:valueType=""-->
<!--android:interpolator=""-->
<!--android:pathData=""-->
<!--android:propertyXName=""-->
<!--android:propertyYName=""/>-->
</set>
R.anim.enter_from_left
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="-100%p"
android:toXDelta="0"/>
</set>
R.anim.enter_from_right
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="-100%p"
android:toXDelta="0"/>
</set>
R.anim.exit_to_left
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="-100%p"
android:toXDelta="0"/>
</set>
R.anim.exit_to_right
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="-100%p"
android:toXDelta="0"/>
</set>