屬性動畫 補間動畫 幀動畫 基本使用案例 MD

Markdown版本筆記 個人GitHub首頁 個人博客 個人微信 個人郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目錄

屬性動畫

屬性動畫基本使用演示

MainActivity

// 可操控的屬性有:alpha;x/y;scaleX/scaleY;rotation/rotationX/rotationY;transitionX/transitionY;pivotX/pivotY
public class MainActivity extends ListActivity {
    private ImageView iv_src;
    private boolean b = true;

    private String[] array = {"重啓當前Activity,啓動一個新的Activity",
        "最簡單的ObjectAnimator,控制scaleX、rotationX",
        "不存在get/set方法時不會有任何效果\n爲Object的某個屬性手動提供get/set方法",
        "只有set沒有get方法時,get的值不存在,可是set能夠正常使用",
        "監聽動畫更新:AnimatorUpdateListener\n監聽動畫狀態:AnimatorListener",
        "組合動畫:AnimatorSet.playTogether\n組合動畫:AnimatorSet.with/before/after",
        "組合動畫:AnimatorUpdateListener\n組合動畫:PropertyValuesHolder",
        "組合動畫:PVHolder + KeyFrame\nView的animate動畫,最簡潔的屬性動畫",
        "View的animate動畫也能夠組合動畫\n能夠在animate動畫前/後執行一些操做",
        "最簡單的ValueAnimator,控制translationY\n要明白ValueAnimator只是幫你計算插值的"};

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));

        iv_src = new ImageView(this);
        iv_src.setBackgroundColor(0x330000ff);
        iv_src.setImageResource(R.drawable.icon);
        getListView().addHeaderView(iv_src);
    }

    @Override
    protected void onListItemClick(ListView l, View view, int position, long id) {
        switch (position) {
            case 0:
                b = !b;
                Toast.makeText(this, "目前爲:" + b, Toast.LENGTH_SHORT).show();
                break;
            case 1:
                if (b) recreate();//重啓當前Activity
                else startActivity(new Intent(this, SecondActivity.class));
                break;
            case 2://最簡單的ObjectAnimator,控制scaleX、rotationX
                if (b) ObjectAnimator.ofFloat(iv_src, "scaleX", 1f, 0.1f, 3f, 0.1f, 1f)//X軸縮放
                    .setDuration(1000)
                    .start();
                else ObjectAnimator.ofFloat(iv_src, "rotationX", 0.0f, 720.0f)//沿X軸旋轉
                    .setDuration(500)
                    .start();
                break;
            case 3://不存在get/set方法時不會有任何效果,能夠爲Object的某個屬性手動提供get/set方法
                int i = new Random().nextInt(8) + 1;
                if (b) ObjectAnimator.ofFloat(iv_src, "width", 100 * i)//沒任何效果,但並不會報錯
                    .setDuration(500)
                    .start();
                else ObjectAnimator.ofInt(new WrapperView(iv_src), "width", 100 * i) //提供set方法
                    .setDuration(500)
                    .start();
                break;
            case 4://只有set沒有get方法時,get的值不存在,可是set能夠正常使用
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    iv_src.setBackgroundColor(Color.WHITE); //有set方法,可是沒有get方法,因此get結果爲0
                    if (b) ObjectAnimator.ofArgb(iv_src, "backgroundColor", Color.RED)
                        .setDuration(1000)
                        .start();
                    else ObjectAnimator.ofArgb(iv_src, "backgroundColor", Color.RED, Color.GREEN, Color.BLUE)
                        .setDuration(3000)
                        .start();
                }
                break;
            case 5://監聽動畫更新:AnimatorUpdateListener,監聽動畫狀態:AnimatorListener
                AnimHelper.addAnimListener(iv_src).start();
                break;
            case 6://組合動畫:AnimatorSet.playTogether/with/before/after
                if (b) AnimHelper.combinedAnimPlayTogether(iv_src).start();
                else AnimHelper.combinedAnimAfterBefore(iv_src).start();
                break;
            case 7://組合動畫:AnimatorUpdateListener,PropertyValuesHolder
                if (b) AnimHelper.combinedAnimUpdate(iv_src).start();
                else AnimHelper.propertyValuesHolder(iv_src).start();
                break;
            case 8://組合動畫:PVHolder + KeyFrame,View的animate動畫,最簡潔的屬性動畫
                if (b) AnimHelper.pVHolderKeyFrame(iv_src).start();
                else iv_src.animate().y(200 * (new Random().nextInt(8))).setDuration(500).start();//ViewPropertyAnimator
                break;
            case 9://View的animate動畫也能夠組合動畫,也能夠在animate動畫前/後執行一些操做
                if (b) iv_src.animate()
                    .setDuration(1000)
                    .rotation(360 * new Random().nextInt(8))
                    .scaleX(new Random().nextInt(8) * 0.5f)
                    .setInterpolator(new DecelerateInterpolator())
                    .start();
                else iv_src.animate()
                    .alpha(0.1f)
                    .y(1500)
                    .setDuration(800)
                    .withStartAction(() -> iv_src.setX(300))
                    .withEndAction(() -> {
                        iv_src.setX(0);
                        iv_src.setY(0);
                        iv_src.setAlpha(1f);
                    })
                    .start();
                break;
            case 10://最簡單的ValueAnimator,控制translationY,要明白ValueAnimator只是幫你計算插值的
                if (b) AnimHelper.valueAnimator(iv_src).start();
                else AnimHelper.valueAnimator(iv_src, getListView()).start();
                break;
        }
    }
}

View包裝類

View包裝類,爲不存在get/set方法的屬性提供get/set方法java

// View包裝類,爲不存在get/set方法的屬性提供get/set方法
public class WrapperView {
    private View mTarget;

    public WrapperView(View target) {
        mTarget = target;
    }

    public int getWidth() {
        return mTarget.getLayoutParams().width;
    }

    public void setWidth(int width) {
        mTarget.getLayoutParams().width = width;
        mTarget.requestLayout();
    }

    public int getHeight() {
        return mTarget.getLayoutParams().height;
    }

    public void setHeight(int height) {
        mTarget.getLayoutParams().height = height;
        mTarget.requestLayout();//Call this when something has changed which has invalidated the layout of this view
    }
}

構建動畫的工具類

public class AnimHelper {

    //監聽動畫繪製過程
    public static ObjectAnimator addAnimListener(View view) {
        ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", 0.1f, 1f).setDuration(1000);
        //方法一,實現AnimatorListener接口,監聽開始Start、結束End、被取消Cancel、重複Repeat等事件
        //方法二,繼承AnimatorListenerAdapter,只實現本身想實現的事件
        anim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                Log.i("bqt", "【onAnimationStart】");
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                Log.i("bqt", "【onAnimationEnd】");
            }
        });
        anim.addUpdateListener(animation -> {
            Float value = (Float) animation.getAnimatedValue();
            view.setRotationX((1 - value) * 360);
            Log.i("bqt", "onAnimationUpdate--" + value); //很是頻繁
        });
        return anim;
    }

    public static AnimatorSet combinedAnimPlayTogether(View view) {
        ObjectAnimator anim1 = ObjectAnimator.ofFloat(view, "scaleX", 0, 2f, 1f);
        ObjectAnimator anim2 = ObjectAnimator.ofFloat(view, "alpha", 0, 1f);
        ObjectAnimator anim3 = ObjectAnimator.ofFloat(view, "rotationY", 360);

        AnimatorSet animSet = new AnimatorSet().setDuration(2000);
        animSet.playTogether(anim1, anim2, anim3);
        return animSet;
    }

    public static AnimatorSet combinedAnimAfterBefore(View view) {
        ObjectAnimator anim1 = ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 2f);
        ObjectAnimator anim2 = ObjectAnimator.ofFloat(view, "scaleY", 0.1f, 1f);
        ObjectAnimator anim3 = ObjectAnimator.ofFloat(view, "x", 0, -view.getWidth(), 0);
        ObjectAnimator anim4 = ObjectAnimator.ofFloat(view, "y", 0, view.getY() + 500f, 0);
        ObjectAnimator anim5 = ObjectAnimator.ofFloat(view, "rotationX", 360 * 2);

        AnimatorSet animSet = new AnimatorSet();
        animSet.play(anim1).with(anim2);//anim1,anim2同時執行
        animSet.play(anim2).with(anim3);//anim1,anim2,anim3同時執行
        animSet.play(anim4).after(anim3).before(anim5);//anim4在anim1,anim2,anim3以後,在anim5以前
        animSet.setDuration(1000);
        return animSet;
    }

    public static Animator combinedAnimUpdate(View view) {
        //ObjectAnimator anim = ObjectAnimator.ofFloat(view, "包青天", 0.5f, 0.1f, 2f).setDuration(2000);
        ValueAnimator anim = ValueAnimator.ofFloat(0.5f, 0.1f, 2f).setDuration(2000); //效果和上面的ObjectAnimator徹底同樣
        anim.addUpdateListener(animation -> {
            float cVal = (Float) animation.getAnimatedValue();
            view.setScaleX(cVal);
            view.setAlpha(cVal);
            view.setRotationX(cVal * 360);
        });
        return anim;
    }

    public static ObjectAnimator propertyValuesHolder(View view) {
        PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", 0.2f, 1f);
        PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", 0.8f, 0, 2f);
        PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("rotationY", 360 * 2f, 0);
        return ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY, pvhZ).setDuration(1000);//三個動畫是同時執行的
    }

    public static ObjectAnimator pVHolderKeyFrame(View view) {
        Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
        Keyframe kf1 = Keyframe.ofFloat(0.5f, 360f);
        Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
        kf1.setInterpolator(new AccelerateInterpolator());
        PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
        return ObjectAnimator.ofPropertyValuesHolder(view, pvhRotation).setDuration(1000);
    }

    public static ValueAnimator valueAnimator(View view) {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 800, 0).setDuration(500);//沒有設置要操做的【對象】及【對象的屬性】
        //animator.setTarget(view);//對ValueAnimator來講,這個方法是空方法(沒有意義),由於它不會做用在任何View上
        //對ValueAnimator來講,是經過addUpdateListener,在回調中根據動畫的值來手動設置屬性的值的
        animator.addUpdateListener(animation -> {
            Float f = (Float) animation.getAnimatedValue();//這裏只能強轉爲of**時指定的類型
            view.setTranslationY(f);//設置要操做的對象的屬性。或者你可使用獲取到的值作任何事情
        });
        return animator;
    }

    public static ValueAnimator valueAnimator(View view, ListView listView) {
        ValueAnimator animator = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            animator = ValueAnimator.ofArgb(Color.GREEN).setDuration(5000);
            animator.addUpdateListener(animation -> {
                Integer greenColor = (Integer) animation.getAnimatedValue(); //實時獲取值
                int myColor = (int) (animation.getAnimatedFraction() * Color.YELLOW);
                int count = listView.getAdapter().getCount();
                for (int i = 1; i < count; i++) {
                    if (i % 2 == 0) listView.getChildAt(i).setBackgroundColor(greenColor);
                    else listView.getChildAt(i).setBackgroundColor(myColor);
                }
            });
        }
        return animator;
    }
}

自定義 TypeEvaluator 實現拋物線動畫效果

// 自定義TypeEvaluator實現拋物線動畫效果
public class TypeEvaluatorActivity extends Activity {
    private static final int RADIUS_BALL = 10; //小球的半徑
    private static final int RADIUS_TRACE = 3; //軌跡的半徑
    private ImageView iv_src;
    RelativeLayout layout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏
        initView();
        layout = new RelativeLayout(this);
        layout.setBackgroundColor(Color.LTGRAY);
        layout.setOnClickListener(v -> anim());
        layout.addView(iv_src, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
        setContentView(layout);
    }

    private void initView() {
        Bitmap bitmap = Bitmap.createBitmap(2 * RADIUS_BALL, 2 * RADIUS_BALL, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        paint.setAntiAlias(true);
        paint.setDither(true);
        canvas.drawCircle(RADIUS_BALL, RADIUS_BALL, RADIUS_BALL, paint);
        iv_src = new ImageView(this);
        iv_src.setImageBitmap(bitmap);
    }

    private void anim() {
        final int color = 0xFF000000 + new Random().nextInt(0xFFFFFF);
        Point point = new Point();
        ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(point);
        PointF startPointF = new PointF(0, 0);
        PointF endPointF = new PointF(point.x - iv_src.getWidth(), point.y - iv_src.getHeight());

        ValueAnimator animator = new ValueAnimator().setDuration(1000);
        animator.setInterpolator(new LinearInterpolator()); //插值器,默認爲AccelerateDecelerateInterpolator【慢-快-慢】
        //經過new建立的ValueAnimator必須setObjectValues和setEvaluator,而且必定要先setObjectValues,再setEvaluator
        animator.setObjectValues(startPointF, endPointF);

        animator.setEvaluator((TypeEvaluator<PointF>) (fraction, startValue, endValue) -> { //估值器
            //只要能保證:當fraction=0時返回值爲startValue,而且當fraction=1時返回值爲endValue,就是一個比較合理的函數
            PointF pointF = new PointF();
            pointF.x = startValue.x + fraction * (endValue.x - startValue.x);// x方向勻速移動
            pointF.y = startValue.y + fraction * fraction * (endValue.y - startValue.y);// y方向拋物線加速移動
            return pointF;
        });

        animator.addUpdateListener(animation -> {
            PointF pointf = (PointF) animation.getAnimatedValue();
            iv_src.setX(pointf.x);
            iv_src.setY(pointf.y);
            addTrace(pointf, color);
        });
        animator.start();
    }

    private void addTrace(PointF pointf, int color) {
        View view = new View(this);
        view.setBackgroundColor(color);
        RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(2 * RADIUS_TRACE, 2 * RADIUS_TRACE);
        layoutParams.leftMargin = (int) pointf.x + RADIUS_TRACE;
        layoutParams.topMargin = (int) pointf.y + RADIUS_TRACE;
        layout.addView(view, layoutParams);
    }
}

使用 LayoutTransition 爲佈局容器中子View的顯示與消失設置過渡動畫

public class LayoutTransitionActivity extends ListActivity {

    private GridLayout gl_container;//父佈局

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = {"點擊添加一個View,點擊添加的View刪除此View"};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));

        gl_container = new GridLayout(this);
        gl_container.setColumnCount(4);
        gl_container.setLayoutTransition(new LayoutTransition());//佈局動畫,當容器中的視圖發生變化時存在過渡的動畫效果
        getListView().addFooterView(gl_container);

        addCheckBox(LayoutTransition.APPEARING, "APPEARING");//當一個View在VG中【出現】時設置的動畫
        addCheckBox(LayoutTransition.CHANGE_APPEARING, "CHANGE_APPEARING");
        //當一個View在ViewGroup中【出現】時,對此View對其餘View位置形成影響,對【其餘View】設置的動畫
        addCheckBox(LayoutTransition.DISAPPEARING, "DISAPPEARING");//當一個View在VG中【消失】時設置的動畫
        addCheckBox(LayoutTransition.CHANGE_DISAPPEARING, "CHANGE_DISAPPEARING");
        //當一個View在ViewGroup中【消失】時,對此View對其餘View位置形成影響,對【其餘View】設置的動畫
        addCheckBox(-1, "等價因而否設置xml中GridLayout的animateLayoutChanges屬性爲true");
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Button button = new Button(this);
        button.setText(gl_container.getChildCount() + 1 + "");
        gl_container.addView(button, gl_container.getChildCount());//放置在最後那個位置
        button.setOnClickListener(view -> gl_container.removeView(button));
    }

    private void addCheckBox(int transitionType, String text) {
        CheckBox checkBox = new CheckBox(this);
        checkBox.setText(text);
        checkBox.setChecked(true);
        checkBox.setOnCheckedChangeListener((v, isChecked) -> {
            LayoutTransition mTransition = new LayoutTransition(); //默認爲所有開啓狀態,默認的動畫效果都是能夠更改的
            if (transitionType == -1) mTransition = isChecked ? mTransition : null;
            else mTransition.setAnimator(transitionType, isChecked ? mTransition.getAnimator(transitionType) : null);
            gl_container.setLayoutTransition(mTransition);
        });
        getListView().addFooterView(checkBox);
    }
}

使用 LayoutAnimationController 爲佈局容器中的控件播放一樣的動畫

public class LayoutAnimationControllerActivity extends ListActivity {
    LinearLayout linearLayout;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = {"點擊添加HeaderView",
            "點擊添加的HeaderView",
            "移除添加移除View時沒任何動畫效果",
            "ListView不支持addView操做"//UnsupportedOperationException:addView(View) is not supported in AdapterView
        };
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
        getListView().setLayoutAnimation(getLayoutAnimationController());//android:layoutAnimation="@anim/layout_anim"
        addFooterView();
    }

    private void addFooterView() {
        linearLayout = new LinearLayout(this);
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        new Handler().postDelayed(() -> linearLayout.addView(getView(v -> linearLayout.removeView(v))), 500);
        new Handler().postDelayed(() -> linearLayout.addView(getView(v -> linearLayout.removeView(v))), 1500);
        new Handler().postDelayed(() -> linearLayout.addView(getView(v -> linearLayout.removeView(v))), 3500);
        linearLayout.setLayoutAnimation(getLayoutAnimationController());
        getListView().addHeaderView(linearLayout);
    }

    private LayoutAnimationController getLayoutAnimationController() {
        Animation animation = AnimationUtils.loadAnimation(this, android.R.anim.slide_in_left);//補間動畫
        LayoutAnimationController lac = new LayoutAnimationController(animation);//佈局動畫
        lac.setOrder(LayoutAnimationController.ORDER_NORMAL);//顯示順序 normal=0 默認,reverse=1 倒序,random=2 隨機
        lac.setDelay(0.6f);//顯示間隔時間,注意單位是秒,能夠爲70%,也能夠是一個浮點數
        return lac;
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        int index = position - getListView().getHeaderViewsCount();
        if (index == 0) {
            getListView().addHeaderView((getView(view -> getListView().removeHeaderView(view))));
        } else if (index == 1) {
            linearLayout.addView(getView(view -> linearLayout.removeView(view)));
        }
    }

    private ImageView getView(View.OnClickListener listener) {
        ImageView iv = new ImageView(this);
        iv.setImageResource(R.drawable.ic_launcher);
        iv.setOnClickListener(listener);
        return iv;
    }
}

幾十種 Interpolator 演示

SecondActivity

public class SecondActivity extends ListActivity {
    private final String[][] array = {INTERPOLATORS1, INTERPOLATORS2, INTERPOLATORS3, INTERPOLATORS4, INTERPOLATORS5, INTERPOLATORS6,};

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏
        requestWindowFeature(Window.FEATURE_NO_TITLE);//取消標題欄
        String[] names = {"-1-", "-2-", "-3-", "-4-", "-5-", "-6-", //
            "自定義TypeEvaluator實現拋物線動畫效果", //
            "使用LayoutTransition爲佈局容器中子View的顯示與消失設置過渡動畫", //
            "使用LayoutAnimationController爲佈局容器中的控件播放一樣的動畫",};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, names));
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        switch (position) {
            case 6:
                startActivity(new Intent(this, TypeEvaluatorActivity.class));
                break;
            case 7:
                startActivity(new Intent(this, LayoutTransitionActivity.class));
                break;
            case 8:
                startActivity(new Intent(this, LayoutAnimationControllerActivity.class));
                break;
            default:
                Intent intent = new Intent(this, InterpolatorActivity.class);
                intent.putExtra(InterpolatorActivity.EXTRA_NAME, array[position]);
                startActivity(intent);
                break;
        }
    }

    public static final String[] INTERPOLATORS1 = new String[]{"EaseBackInInterpolator", "EaseBackInOutInterpolator", "EaseBackOutInterpolator",
        "EaseBounceInInterpolator", "EaseBounceInOutInterpolator", "EaseBounceOutInterpolator", "EaseBreathInterpolator", "EaseCircularInInterpolator",
        "EaseCircularInOutInterpolator", "EaseCircularOutInterpolator",};
    public static final String[] INTERPOLATORS2 = new String[]{"EaseCubicInInterpolator", "EaseCubicInOutInterpolator", "EaseCubicOutInterpolator",
        "EaseExponentialInInterpolator", "EaseExponentialInOutInterpolator", "EaseExponentialOutInterpolator", "EaseInBackInterpolator",
        "EaseInBounceInterpolator", "EaseInCircInterpolator", "EaseInCubicInterpolator",};
    public static final String[] INTERPOLATORS3 = new String[]{"EaseInElasticInterpolator", "EaseInExpoInterpolator", "EaseInOutBackInterpolator",
        "EaseInOutBounceInterpolator", "EaseInOutCircInterpolator", "EaseInOutCubicInterpolator", "EaseInOutElasticInterpolator",
        "EaseInOutExpoInterpolator", "EaseInOutQuadInterpolator", "EaseInOutQuartInterpolator",};
    public static final String[] INTERPOLATORS4 = new String[]{"EaseInOutQuintInterpolator", "EaseInOutSineInterpolator", "EaseInQuadInterpolator",
        "EaseInQuartInterpolator", "EaseInQuintInterpolator", "EaseInSineInterpolator", "EaseOutBackInterpolator", "EaseOutBounceInterpolator",
        "EaseOutCircInterpolator", "EaseOutCubicInterpolator",};
    public static final String[] INTERPOLATORS5 = new String[]{"EaseOutElasticInterpolator", "EaseOutExpoInterpolator", "EaseOutQuadInterpolator",
        "EaseOutQuartInterpolator", "EaseOutQuintInterpolator", "EaseOutSineInterpolator", "EaseQuadInInterpolator", "EaseQuadInOutInterpolator",
        "EaseQuadOutInterpolator", "EaseQuartInInterpolator",};
    public static final String[] INTERPOLATORS6 = new String[]{"EaseQuartInOutInterpolator", "EaseQuartOutInterpolator", "EaseQuintInInterpolator",
        "EaseQuintInOutInterpolator", "EaseQuintOutInterpolator",};
}

InterpolatorActivityInterpolatorActivity

public class InterpolatorActivity extends ListActivity {
    private ObjectAnimator mAnimator;
    private static final String IN_PG_NAME = "com.bqt.anim.interpolator.";
    public static final String EXTRA_NAME = "interpolators";
    private String[] mInterpolators;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//全屏
        mInterpolators = getIntent().getStringArrayExtra(EXTRA_NAME); //傳過來的名字
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mInterpolators));

        ImageView iv_src = new ImageView(this);
        iv_src.setBackgroundColor(0x330000ff);
        iv_src.setImageResource(R.drawable.icon);
        getListView().addHeaderView(iv_src);

        DisplayMetrics metric = new DisplayMetrics();
        ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metric);
        mAnimator = ObjectAnimator.ofFloat(iv_src, "y", 0, metric.heightPixels, 0).setDuration(1500);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        if (position == 0) recreate();
        else anim(position - 1);
    }

    private void anim(int position) {
        String name = mInterpolators[position];
        try {
            Class<?> clazz = Class.forName(IN_PG_NAME + name);
            TimeInterpolator interpolator = (TimeInterpolator) clazz.newInstance();
            mAnimator.cancel();
            mAnimator.setInterpolator(interpolator);
            mAnimator.start();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

補間動畫

補間動畫基本使用演示

MainActivity

public class MainActivity extends ListActivity {
    private ImageView iv;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = {"alpha",
            "trans",
            "scale",
            "rotate",
            "set",
            "fillAfter = true時,無論其餘怎麼設置,都是使用最後一幀",
            "fillAfter = false時,無論其餘怎麼設置,都是使用第一幀",};
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array));
        iv = new ImageView(this);
        iv.setBackgroundColor(0x330000ff);
        iv.setImageResource(R.drawable.icon);
        iv.setOnClickListener(v -> Toast.makeText(this, "點擊了View", Toast.LENGTH_SHORT).show());
        getListView().addHeaderView(iv);
    }

    @Override
    protected void onListItemClick(ListView l, View view, int position, long id) {
        Animation animation = null;
        switch (position - getListView().getHeaderViewsCount()) {
            case 0:
                animation = AnimHelper.alpha();
                break;
            case 1:
                animation = AnimHelper.trans();
                break;
            case 2:
                animation = AnimHelper.scale();
                break;
            case 3:
                animation = AnimHelper.rotate();
                break;
            case 4:
                animation = AnimHelper.set();
                break;
            case 5:
                animation = AnimHelper.scale();
                animation.setFillEnabled(new Random().nextBoolean());
                animation.setFillBefore(new Random().nextBoolean());
                animation.setFillAfter(true);//動做結束後停留在最後一幀
                break;
            case 6:
                animation = AnimHelper.scale();
                animation.setFillEnabled(new Random().nextBoolean());
                animation.setFillBefore(new Random().nextBoolean());
                animation.setFillAfter(false);//動做結束後停留在第一幀(沒有進行任何縮放)
                break;
        }
        iv.startAnimation(animation);
    }
}

AnimHelper

public class AnimHelper {

    //透明度動畫
    public static Animation alpha() {
        AlphaAnimation animation = new AlphaAnimation(1.0f, 0.1f);//開始、結束時的透明度。1爲全不透明,0爲全透明
        animation.setDuration(2000);//播放時間
        animation.setRepeatCount(1);//重複次數,默認爲0。播放次數=重複次數+1。Animation.INFINITE表示不中止的播放
        animation.setRepeatMode(Animation.RESTART);//【REVERSE】倒序重複播放,【RESTART】從新開始執行(默認)
        animation.setInterpolator(new AccelerateInterpolator());//加速
        return animation;
    }

    //位移動畫
    public static Animation trans() {
        TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, -0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f,//8個參數:fromXType, fromXValue, toXType, toXValue
            Animation.RELATIVE_TO_SELF, -1f, //【開始/結束】時【相對誰】的距離
            Animation.RELATIVE_TO_PARENT, 1f);
        animation.setDuration(1000);
        animation.setRepeatCount(Animation.INFINITE); //不中止的播放
        animation.setRepeatMode(Animation.REVERSE); //倒序重複播放
        animation.setInterpolator(new BounceInterpolator());//動畫結束的時候彈起
        return animation;
    }

    //縮放動畫
    public static Animation scale() {
        ScaleAnimation animation = new ScaleAnimation(0.5f, 2.5f, //開始、結束時x的縮放比例
            0.1f, 1.5f, //開始、結束時y的縮放比例
            Animation.RELATIVE_TO_SELF, 0.5f, //x縮放時所使用的模式和中心點
            Animation.RELATIVE_TO_SELF, 0.5f); //y縮放時所使用的模式和中心點
        animation.setDuration(1000);
        animation.setInterpolator(new AccelerateDecelerateInterpolator());//先加速後減速
        return animation;
    }

    //旋轉動畫
    public static Animation rotate() {
        RotateAnimation animation = new RotateAnimation(0, 360 * 5, //開始、結束時旋轉角度
            Animation.RELATIVE_TO_SELF, 0.5f,  //x旋轉時所使用的模式和中心點
            Animation.RELATIVE_TO_SELF, 0.5f); //y旋轉時所使用的模式和中心點
        animation.setDuration(1000);
        animation.setInterpolator(new LinearInterpolator());//勻速
        return animation;
    }

    //組合動畫
    public static AnimationSet set() {
        AnimationSet animationSet = new AnimationSet(false);//是否使用共同的插值器
        //位移
        TranslateAnimation ta = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0f,
            Animation.RELATIVE_TO_SELF, 0f,
            Animation.RELATIVE_TO_PARENT, 0.5f,
            Animation.RELATIVE_TO_PARENT, 0.5f);
        ta.setDuration(800);
        ta.setInterpolator(new AccelerateInterpolator());//快-慢

        //縮放
        ScaleAnimation sa = new ScaleAnimation(2f, 1f, 0.5f, 2f,
            Animation.RELATIVE_TO_PARENT, 0.5f,
            Animation.RELATIVE_TO_PARENT, 0.5f);
        sa.setDuration(800);
        sa.setStartOffset(1000);//延遲時間 When this Animation should start

        //旋轉
        RotateAnimation ra = new RotateAnimation(0, 360 * 5,
            Animation.RELATIVE_TO_SELF, 0.5f,
            Animation.RELATIVE_TO_SELF, 0.5f);
        ra.setDuration(1500);
        ra.setStartOffset(2000);
        sa.setInterpolator(new DecelerateInterpolator()); //慢-快

        //將上面這些動畫放到集合中
        animationSet.addAnimation(ta);
        animationSet.addAnimation(sa);
        animationSet.addAnimation(ra);
        //set.setFillEnabled(true);
        animationSet.setFillAfter(true);
        //set.setFillBefore(false);
        return animationSet;
    }
}

幾十種 Interpolator 演示

public class SecondActivity extends ListActivity {
    int[] array_id = {R.anim.alpha_in, R.anim.disappear_bottom_right_in, R.anim.disappear_bottom_right_out,
        R.anim.disappear_top_left_in, R.anim.disappear_top_left_out, R.anim.drawroll_ani_in, R.anim.drawroll_ani_out,
        R.anim.fade_out, R.anim.flip_horizontal_in, R.anim.flip_horizontal_out, R.anim.flip_vertical_in, R.anim.flip_vertical_out,
        R.anim.gallery_in, R.anim.grow_from_top, R.anim.left_in, R.anim.left_out, R.anim.mi_laucher_alpha,
        R.anim.mi_laucher_del_done, R.anim.mi_laucher_del_down, R.anim.mi_laucher_out, R.anim.mi_laucher_scale_in,
        R.anim.mi_laucher_scale_out, R.anim.pophidden_anim, R.anim.popshow_anim, R.anim.push_left_in, R.anim.push_left_out,
        R.anim.push_up_in, R.anim.push_up_out, R.anim.rbm_in_from_left, R.anim.rbm_out_to_left, R.anim.refreshable_list_rotate,
        R.anim.right_in, R.anim.right_out, R.anim.shrink_from_bottom, R.anim.slide_down_out, R.anim.slide_left,
        R.anim.slide_right, R.anim.slide_up_in, R.anim.small_2_big, R.anim.umeng_fb_slide_in_from_left,
        R.anim.umeng_fb_slide_in_from_right, R.anim.umeng_fb_slide_out_from_left, R.anim.umeng_fb_slide_out_from_right,
        R.anim.umeng_socialize_fade_in, R.anim.umeng_socialize_fade_out, R.anim.umeng_socialize_shareboard_animation_in,
        R.anim.umeng_socialize_shareboard_animation_out, R.anim.umeng_socialize_slide_in_from_bottom,
        R.anim.umeng_socialize_slide_out_from_bottom, R.anim.unzoom_in, R.anim.unzoom_out, R.anim.welcome_fade_in_scale,
        R.anim.welcome_fade_out, R.anim.zoom_enter, R.anim.zoom_exit};

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array_name = new String[array_id.length];
        for (int i = 0; i < array_id.length; i++) {
            String name = getResources().getResourceName(array_id[i]);
            array_name[i] = name.replace(getPackageName() + ":anim/", "");
        }
        setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array_name));

        getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                Log.i("bqt", "【onScrollStateChanged】" + scrollState); //臥槽,我發現滑動時這個方法並不必定會回調
                // AbsListView.OnScrollListener.SCROLL_STATE_FLING; //屏幕處於甩動狀態
                // AbsListView.OnScrollListener.SCROLL_STATE_IDLE; //中止滑動狀態
                // AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;// 手指接觸狀態
                if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                    int visibleCount = getListView().getLastVisiblePosition() - getListView().getFirstVisiblePosition() + 1; //可見的數量
                    for (int i = 0; i < visibleCount; i++) {
                        int reallyIndex = i + getListView().getFirstVisiblePosition();//真正的位置
                        int alpha = 255 * reallyIndex / view.getCount();
                        int color;
                        if (reallyIndex % 2 == 0) color = Color.argb(alpha, 0, 255, 0);
                        else color = Color.argb(alpha, 0, 0, 255);
                        getListView().getChildAt(i).setBackgroundColor(color);
                    }
                }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                //當前窗口中能看見的第一個列表項ID,當前窗口中能看見的列表項的個數,列表項的總數
                //Log.i("bqt", "【onScroll】" + firstVisibleItem + "," + visibleItemCount + "," + totalItemCount);
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("bqt", "【onResume】");
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        v.setAnimation(AnimationUtils.loadAnimation(this, array_id[position]));
    }
}

自定義 Activity 轉場動畫

默認轉場動畫:android

  • 淡入淡出效果:overridePendingTransition(android.R.anim.fade_in,android.R.anim.fade_out);
  • 由左向右滑入的效果:overridePendingTransition(android.R.anim.slide_in_left,android.R.anim.slide_out_right);

自定義轉場動畫:git

public class BaseActivity extends Activity {
    @Override
    public void startActivity(Intent intent) {
        super.startActivity(intent);
        overridePendingTransition(R.anim.activity_in_from_down, R.anim.activity_out_to_up);
    }

    @Override
    public void finish() {
        super.finish();
        overridePendingTransition(R.anim.activity_in_from_top, R.anim.activity_out_to_down);
    }
}
public class Activity1 extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ImageView imageView = new ImageView(this);
        imageView.setImageResource(R.drawable.icon);
        imageView.setBackgroundColor(0xFF000000 + new Random().nextInt(0xFFFFFF));
        imageView.setOnClickListener(v -> startActivity(new Intent(this, Activity1.class)));
        setContentView(imageView);
    }
}

系統定義的幾個補間動畫

存放位置:android_sdk\platforms\android-28\data\res\anim
默認時間:<integer name="config_mediumAnimTime">400</integer>github

fade_in 淡入canvas

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="@android:integer/config_longAnimTime"
       android:fromAlpha="0.0"
       android:interpolator="@interpolator/decelerate_quad"
       android:toAlpha="1.0"/>

fade_out 淡出微信

<alpha xmlns:android="http://schemas.android.com/apk/res/android"
       android:duration="@android:integer/config_mediumAnimTime"
       android:fromAlpha="1.0"
       android:interpolator="@interpolator/accelerate_quad"
       android:toAlpha="0.0"/>

slide_out_right 淡出到右邊屏幕app

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="0"
        android:toXDelta="50%p"/>
    <alpha
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromAlpha="1.0"
        android:toAlpha="0.0"/>
</set>

slide_out_leftdom

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="0"
        android:toXDelta="-50%p"/>
    <alpha
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromAlpha="1.0"
        android:toAlpha="0.0"/>
</set>

slide_in_rightide

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="50%p"
        android:toXDelta="0"/>
    <alpha
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromAlpha="0.0"
        android:toAlpha="1.0"/>
</set>

slide_in_left 從左邊淡入到屏幕函數

<set xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://schemas.android.com/apk/res/android ">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromXDelta="-50%p"
        android:toXDelta="0"/>
    <alpha
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromAlpha="0.0"
        android:toAlpha="1.0"/>
</set>

slide_in_child_bottom

<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@interpolator/decelerate_quad">
    <translate
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromYDelta="100%"
        android:toYDelta="0"/>
    <alpha
        android:duration="@android:integer/config_mediumAnimTime"
        android:fromAlpha="0.0"
        android:toAlpha="1.0"/>
</set>

經常使用的窗口顯示、消失動畫

經過下面代碼能夠實如今Dialog或AlertDialog顯示、消失時的具備可愛的動畫效果:

dialog.getWindow().setWindowAnimations(R.style.dialog_anim);

經過下面代碼能夠實如今popupWindow 顯示、消失時的具備可愛的動畫效果:

popWindow.setAnimationStyle(R.style.dialog_anim);

其中,R.style.dialog_anim爲在styles.xml中定義的一個樣式:

<style name="dialog_animation" parent="@android:style/Animation">
    <!--窗體進入動畫--><item name="android:windowEnterAnimation">@anim/popshow_anim</item>
    <!--窗體退出動畫--><item name="android:windowExitAnimation">@anim/pophidden_anim</item>
</style>

其中引用的即是兩個自定義的補間動畫,經常使用的效果的設置以下:

popshow_anim.xml 由下往上淡入

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="1000"
        android:fromYDelta="100%p"
        android:toYDelta="0" />
    <alpha
        android:duration="1000"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

pophidden_anim.xml 由上往下淡出

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <translate
        android:duration="1000"
        android:fromYDelta="0"
        android:toYDelta="50%p" />
    <alpha
        android:duration="1000"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
</set>

幀動畫

基本使用案例

一、在res/drawable/目錄下定義動畫:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                android:oneshot="true">
    <item
        android:drawable="@drawable/icon"
        android:duration="200"/>
    <item
        android:drawable="@drawable/ic_launcher"
        android:duration="200"/>
    <item
        android:drawable="@drawable/pic"
        android:duration="200"/>
</animation-list>

二、設置爲背景

view.setBackgroundResource(R.drawable.frame_anim); //必須設爲背景

三、開啓動畫

Drawable drawable = view.getBackground();
if (drawable instanceof AnimationDrawable) {
    AnimationDrawable animationDrawable = (AnimationDrawable) drawable;
    Toast.makeText(this, "是否正在運行:" + animationDrawable.isRunning(), Toast.LENGTH_SHORT).show();
    animationDrawable.stop();
    animationDrawable.start();
}

注意事項:
It's important to note that the start() method called on the AnimationDrawable cannot be called during the onCreate() method of your Activity, because the AnimationDrawable is not yet fully attached to the window. If you want to play the animation immediately, without requiring interaction, then you might want to call it from the onWindowFocusChanged() method in your Activity, which will get called when Android brings your window into focus.

請注意,在Activity的onCreate()方法中,不能調用AnimationDrawable的start()方法,由於AnimationDrawable還沒有徹底附加到窗口。若是要當即播放動畫,而不須要交互,那麼您可能但願在Activity中的onWindowFocusChanged()方法中調用它,當Android將您的窗口置於焦點時,該動畫將被調用。

幀動畫簡介

Drawable Animation 或者 Frame Animation,幀動畫,就像GIF圖片(或電影)同樣,是經過依次顯示一系列的 Drawable 來模擬動畫的效果。

<animation-list>爲根元素,一個<item>表示一幀要輪換顯示的圖片

  • oneshot 表明着是否只展現一遍,設置爲false會不停的循環播放動畫
  • duration 屬性表示此幀顯示的時間

注意事項:

  • 幀動畫是指背景動畫,因此只能設置 background 屬性爲指定的幀動畫,或在代碼中經過 setBackgroundResource(R.drawable.amin_id) 指定幀動畫,可是不能設置 src 屬性爲指定的幀動畫。固然二者能夠同時設置,此時src保持不變,背景是個動畫會改變。
  • 開啓或關閉幀動畫前最好先判斷獲取的幀動畫是否爲空,由於幀動畫是經過 getBackground() 強轉過來的,可能不存在!
  • 最重要的:不要在onCreate()中調用start方法開啓幀動畫,由於此時幀動畫尚未徹底跟Window相關聯,若是想要在界面顯示時就當即開始動畫的話,能夠在onWindowFoucsChanged()方法中調用start方法。

雖然定義三種動畫的xml文件均可以放置在res/anim/文件夾中,可是建議:

  • 幀動畫放在res/drawable文件夾中
  • 補間動畫放在res/anim文件夾中
  • 屬性動畫放在res/animator文件夾中

AnimationDrawable 類簡介

public class AnimationDrawable extends DrawableContainer implements Runnable, Animatable

AnimationDrawableDrawable 的間接子類。

Drawable 相關的方法:

  • void addFrame(Drawable frame, int duration):Adds a frame to the animation
  • Drawable getFrame(int index):Return the Drawable at the specified frame index
  • Drawable mutate():Make this drawable mutable易變的. This operation cannot be reversed反轉.

動畫相關的方法:

  • int getDuration(int i):Return the duration in milliseconds of the frame at the specified index
  • int getNumberOfFrames():Return the number of frames in the animation
  • boolean isOneShot():Return true of the animation will play once, false otherwise
  • void setOneShot(boolean oneShot):Sets whether the animation should play once or repeat.

其餘不經常使用的方法:

  • void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Resources.Theme theme):Inflate this Drawable from an XML resource optionally styled by a theme.
  • void run():This method exists for implementation purpose only and should not be called directly. Invoke start() instead.
  • boolean setVisible(boolean visible, boolean restart):Sets whether this AnimationDrawable is visible.
  • void unscheduleSelf(Runnable what):Use the current Drawable.Callback implementation to have this Drawable unscheduled.

最經常使用的是 Animatable 接口中定義的三個方法:

public interface Animatable {
    void start(); //Starts the drawable's animation. This method has no effect if the animation is running.
    void stop(); //Stops the drawable's animation. This method has no effect if the animation is not running.
    boolean isRunning(); //Indicates whether the animation is running.
}

補充清單文件中註冊Activity

<activity android:name="com.bqt.anim.property.MainActivity"/>
<activity android:name="com.bqt.anim.property.SecondActivity"/>
<activity android:name="com.bqt.anim.property.InterpolatorActivity"/>
<activity android:name="com.bqt.anim.property.TypeEvaluatorActivity"/>
<activity android:name="com.bqt.anim.property.LayoutTransitionActivity"/>
<activity android:name="com.bqt.anim.property.LayoutAnimationControllerActivity"/>
<activity android:name="com.bqt.anim.tweened.MainActivity"/>
<activity android:name="com.bqt.anim.tweened.SecondActivity"/>
<activity android:name="com.bqt.anim.tweened.transition.Activity1"/>

2019-5-11

附件列表

相關文章
相關標籤/搜索