preface

We all know that Android comes with Roate Scale Translate Alpha, a variety of frame animations that we can use to achieve rich animation effects, but these wide animations have a fatal flaw, they just change the size of the View display, Without changing the response area of the View. This is where attribute animations like ObjectAnimator and ValueAnimator come in.


Effect of simple


The working principle of

Property animation word as its name, is to change the View of the property value to change the shape of the control, to put it plainly is through reflection technology to obtain some properties of the control such as width, height, get and set methods, so as to achieve the so-called animation effect. Therefore, this requires our View (as in a custom View) to have set and get methods, otherwise it will cause the program to Clash. Specific steps

  1. First, the system obtains the property value through the GET method
  2. Under the action of the time interpolator, the system changes the attribute values
  3. The system calls the set method to re-assign the property value to the control

It can also be seen that the property animation directly changes the properties of the control, so the control is permanently changed after the animation is over.


Implement four animations using ObjectAnimator

Here I intend to implement four animation frameworks using ObjectAnimator:

  1. alpha
  2. scaleX/scaleY
  3. translateX/translateY
  4. rotation

Let me show you how ObjectAnimator works

    private void iniAnimation(a){
        // Transparency animation
        ObjectAnimator.ofFloat(mAlphaImage, "alpha".1.0.1)
                .setDuration(4000)
                .start();
        
        / / zoom
        final AnimatorSet animatorSet = new AnimatorSet();
        mScaleImage.setPivotX(mScaleImage.getWidth()+250);
        mScaleImage.setPivotY(mScaleImage.getHeight()+250);
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(mScaleImage, "scaleX".1.0)
                        .setDuration(2000),
                ObjectAnimator.ofFloat(mScaleImage, "scaleY".1.0)
                        .setDuration(2000)); animatorSet.start();/ / translation translation
        final AnimatorSet translationAnimatorSet = new AnimatorSet();
        translationAnimatorSet.playTogether(
                ObjectAnimator.ofFloat(mTranslationImage, "translationX".20.100)
                        .setDuration(2000),
                ObjectAnimator.ofFloat(mTranslationImage, "translationY".20.100)
                        .setDuration(2000)); translationAnimatorSet.start();// Use ObjectAnimator to animate the rotation
        final AnimatorSet rotateAnimationSet = new AnimatorSet();
        rotateAnimationSet.playTogether(
                ObjectAnimator.ofFloat(mRotationImage, "rotation".0.360)
                        .setDuration(2000)); rotateAnimationSet.start(); }Copy the code

The above code is implemented through ObjectAnimator. The four effects can be summarized as follows

  1. Create an AnimatorSet object
  2. Set the axis where changes occur (part required)
  3. Set the animation that needs to change (usually in the playTogether() method)
  4. Open animation

As shown in the opening animation, we can add multiple animations to a playTogether method to achieve the effect of multiple animation combinations. I won’t repeat it here, but you can try it yourself (the animation in the lower right corner of my GIF is rotation + transparency).


Use ValueAnimator to animate properties

ValueAnimator is a parent of ObjectAnimator. The difference between ObjectAnimator and ValueAnimator is that ObjectAnimator uses reflection technology to animate objects on top of ValueAnimator. ObjectAnimator is given two values (from, to), and after determining the animation type (” scale, translate “) it automatically generates the animation. ValueAnimator does not automatically execute anything. Instead, the ValueAnimator uses the listener method addUpdateListener. Returns a sequence of values that we can then use to set the control.

The instance

FloatingActionButton = FloatingActionButton = FloatingActionButton = FloatingActionButton


Implementation method

This effect can be achieved either through the animation frame or through the property animation. Here I will show you how to achieve this effect. We use ValueAnimator, so it has more ValueAnimator characteristics. After we set the time interpolator to it, it will regularly return a series of numbers. So we just need to set the properties of the control with this series of numbers.

    private FloatingActionButton fab;
    private ImageView imageView;
    private int buttonSize = 0, imageSize = 0;
    private float startY = 0;
    private float endY   = 0;
    
    private ValueAnimator createValueAnimate(final View view, int start, int end){
        ValueAnimator valueAnimator = ValueAnimator.ofInt(start, end);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                ViewGroup.LayoutParams params = view.getLayoutParams();
                params.height = (int) animation.getAnimatedValue();
                params.width  = (int) animation.getAnimatedValue(); view.setLayoutParams(params); }});return valueAnimator;
    }
Copy the code

You can see that we pass in three parameters. Here I’m doing a zoom animation, so I give the control, the current size of the control, and the target size of the control. Then set it based on the value returned by the listener. In terms of invocation, I have implemented pull-up hiding and pull-down display, so we need to determine the sliding direction of the Y-axis:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_UP:
                startY = event.getY();
                if ((startY - endY) < 0) {/ / to narrow
                    animationDown(fab, buttonSize);
                    animationDown(imageView, imageSize);
                }else if ((startY - endY) > 0) {Enlarge / /
                    animationUp(fab, buttonSize);
                    animationUp(imageView, imageSize);
                }
                break;

            case MotionEvent.ACTION_DOWN:
                endY   = event.getY();
                break;
        }
        return super.onTouchEvent(event);
    }

    private void animationDown(final View view, int originalSize){
        ValueAnimator animator = createValueAnimate(view, originalSize, 0);
        animator.addListener(new AnimatorListenerAdapter(){
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation); view.setVisibility(View.GONE); }}); animator.setInterpolator(new BounceInterpolator());
        animator.setDuration(500).start();
    }

    private void animationUp(final View view, int originalSize){
        view.setVisibility(View.VISIBLE);
        ValueAnimator animator = createValueAnimate(view, 0, originalSize);
        animator.setInterpolator(new BounceInterpolator());
        animator.setDuration(500).start();
    }
Copy the code

Here we can see that since it is a property animation, the change is directly the size of the control, which causes a problem if the control size is retrieved in real time. So we will not be able to get the original size of the control after we perform the small animation, that is, animationDown. So here we get the control size in the onResume method:

    @Override
    protected void onResume(a) {
        super.onResume();
        fab.post(new Runnable() {
            @Override
            public void run(a) { buttonSize = fab.getHeight(); }}); imageView.post(new Runnable() {
            @Override
            public void run(a) { imageSize = imageView.getHeight(); }}); }Copy the code

We practice

Property animation can be used as a ViewGroup to increase and reduce the animation and interface transformation is not so abrupt. In fact, careful students may find that Android has its own switching effect, but the form is relatively simple, so HERE I use a custom ObjectAnimator method.

The final result

  1. First instantiate a LayoutTransition object
  2. Then through ObjectAnimator. OfPropertyValuesHolder () instantiate one for loading animation
  3. ObjectAnimator object
  4. Then the ObjectAnimator. OfPropertyValuesHolder () set in a series of animation effects
  5. SetAnimation sets the ObjectAnimator to the Transition animation
  6. Set the Duration execution time for ObjectAnimator
  7. Set animation delay setStartDelay

Set the Remove animation in the same way

        LayoutTransition transition = new LayoutTransition();

        ObjectAnimator appendAnimator = ObjectAnimator.ofPropertyValuesHolder(
                (ImageView) null,
                PropertyValuesHolder.ofFloat("scaleX".0.0 f.1.0 f),
                PropertyValuesHolder.ofFloat("scaleY".0.0 f.1.0 f),
                PropertyValuesHolder.ofFloat("alpha" , 0.0 f.1.0 f)); appendAnimator.setInterpolator(new BounceInterpolator());
        transition.setAnimator(LayoutTransition.APPEARING, appendAnimator);
        transition.setDuration(LayoutTransition.APPEARING, transition.getDuration(LayoutTransition.APPEARING));
        transition.setStartDelay(LayoutTransition.APPEARING, transition.getStartDelay(LayoutTransition.APPEARING));

        ObjectAnimator removeAnimator = ObjectAnimator.ofPropertyValuesHolder(
                (ImageView) null,
                PropertyValuesHolder.ofFloat("scaleX".1.0 f.0.0 f),
                PropertyValuesHolder.ofFloat("scaleY".1.0 f.0.0 f),
                PropertyValuesHolder.ofFloat("alpha".1.0 f.0.0 f)); removeAnimator.setInterpolator(new BounceInterpolator());
        transition.setAnimator(LayoutTransition.DISAPPEARING, removeAnimator);
        transition.setDuration(LayoutTransition.DISAPPEARING, transition.getDuration(LayoutTransition.DISAPPEARING));
        transition.setStartDelay(LayoutTransition.DISAPPEARING, transition.getStartDelay(LayoutTransition.DISAPPEARING));
Copy the code

And then you just give that LayoutTransition object to your ViewGroup by setLayoutTransition

        layout = findViewById(R.id.layout);
        layout.setLayoutTransition(transition);
Copy the code

The test link

The test is divided into add control and remove control, function in the activity dynamic execution: add method:

  1. So I’m going to create a control, and I’m going to use ImageView.
  2. The size, content and other properties are set
  3. Call the addView method of the LinearLayout to add controls to the specified position in the layout

Remove method

  1. First determine whether there are controls in the offline layout
  2. In some cases, the control is removed
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_add_image:
                ImageView imageView = new ImageView(ExtendActivity.this);
                imageView.setScaleType(ImageView.ScaleType.FIT_XY);
                imageView.setImageResource(R.drawable.heart);
                ViewGroup.LayoutParams params = new LinearLayout.LayoutParams(200.200);
                imageView.setLayoutParams(params);
                layout.addView(imageView,0);
                break;

            case R.id.btn_remove_image:
                int count = layout.getChildCount();
                if (count > 0){
                    layout.removeViewAt(0);
                }
                break; }}Copy the code

Click https://github.com/FishInWater-1999/android_view_user_defined_first for the Demo project

So far, the use of all attribute animations is basically introduced

Since this is a summary of personal learning, if you have any questions or omissions, please leave a message in the comments section

Happy programming, less code bugs, hahaha