属性动画 补间动画 帧动画 基本使用案例 MD

时光怂恿深爱的人放手 提交于 2020-01-06 04:57:43
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方法

// 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 转场动画

默认转场动画:

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

自定义转场动画:

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>

fade_in 淡入

<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 淡出到右边屏幕

<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_left

<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_right

<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>

帧动画

基本使用案例

1、在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>

2、设置为背景

view.setBackgroundResource(R.drawable.frame_anim); //必须设为背景

3、开启动画

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

附件列表

     

    易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
    该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!