Property animation shortcomings and improvements

1, the disadvantages

Property animation is really fun to use, and many animation effects can be achieved using property animation. Here are a few typical usage scenarios:

1.1. Code is difficult to reuse

view.animate()
    .traslationX(10.20)
    .alpha(0.1)
    .start();

Animator animator = ObjectAnimator.ofFloat(view,"TranslationX".10.20.30);
animator.start();
Copy the code

The code is simple and intuitive to write. But it’s hard to reuse, and if I need the same animation somewhere else, I have to write the same code all over again.

1.2. Size information cannot be obtained

Most of the time we need to animate based on some size information:

public class MainActivity extends AppCompatActivity {

    private TextView mTextView;
    private LinearLayout mLinearLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         mTextView = (TextView)findViewById(R.id.txt);
         mLinearLayout = (LinearLayout)findViewById(R.id.txt);

         //mLinearLayout is the parent of mTextView,
         // Move mTextView 10px to the right of mLinearLayout
         int distance = mLinearLayout.getWidth() - 10- mTextView.getRight(); mTextView.animate() .TranslationX(distance) .start(); }}Copy the code

The above code does not work as expected because the view is drawn on a different thread than the Activity is created on, and the view dimensions are not available in onCreate(). But a lot of times, we need some size information to animate. The API provided by the system is in px, not dp.

1.3. Complex animation implementation is not simple enough

Animatorsets can execute multiple animators together or sequentially, but to execute multiple animatorsets sequentially, we have to write:

 mAnimatorSet1.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {

                }

                @Override
                public void onAnimationEnd(Animator animator) {
                     mAnimatorSet2.addListener(new Animator.AnimatorListener() {
                         @Override
                         public void onAnimationStart(Animator animator) {

                         }

                         @Override
                         public void onAnimationEnd(Animator animator) {
                                    mAnimatorSet3.start()
                         }

                         @Override
                         public void onAnimationCancel(Animator animator) {

                         }

                         @Override
                         public void onAnimationRepeat(Animator animator) {

                         }
                     });
                     mAnimatorSet2.start();
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });

            mAnimatorSet1.start();
Copy the code

As you can see, the code is a little bit cumbersome.

2,The solution

This is a property animation encapsulating library that solves the problem mentioned above, using the following method:

  • Add the dependent

The root directory build

allprojects {
    repositories {
        jcenter()
        maven {
            url 'https://dl.bintray.com/zouzhihao/maven'}}}Copy the code

Project build

compile 'com. Runningzou. Leptanimator: library: 0.0.1'
Copy the code
  • Define a LeptAnimator subclass
public class SimpleAnimator extends LeptAnimator {

    // This constructor must be overridden. View represents the control that needs to animate and the control that needs to measure dimension information
    public SimpleAnimator(View... view) {
        super(view);

    }

    // Target is the view passed in the constructor
    // These views can be used to animate or measure dimensions
    @Override
    public AnimatorBuilder prepare(View... target) {

            AnimatorBuilder provides many useful methods
            return AnimatorBuilder
                    .animate(target[0]) 1 / / animationTranslationX (100) Dp / / unit
                    .ParentTop(10) // 10dp from the top
                    .duration(1000)

                    .with(target[1]) // Animation 2, animation 2 is executed synchronously with animation 1
                    .leftof(target[3].10); // Target [1] is moved to the right of target[3] at a distance of 10dp
                    .alpha(0.1)

                    .after(target[2]) // Animation 3, animation 1, animation 1, animation 3TranslationX (100); }}Copy the code
  • use

LeptAnimator animator = new SimpleAnimator(view1,view2,view3,view4);
animator.start();

animator.setStartListener(new ViewAnimatorListener.startListener() {
            @Override
            public void onAnimatorStart(a) {
                Log.d("tag"."Animation starts"); }}); animator.setEndListener(new ViewAnimatorListener.endListener() {
            @Override
            public void onAnimatorEnd(a) {
                Log.d("tag"."End of animation"); }});Copy the code

3. Has the problem been solved?

3.1 code reuse

In this wrapper library, animations are encapsulated into classes, such as the SimpleAnimator example, which can be used by simply passing in a new object and the view to operate on. I’m going to say, I’m going to write my animation in XML, and I’m going to load it in code. I have always found it unelegant to write animations in XML. Code can be done very easily (get an Animator instance in a few lines of code). In addition to increasing the number of files and lines of code, writing in XML leads to increased IO operations and decreased efficiency (read files, parse files).

3.2 Size information cannot be obtained

The prepare method for LeptAnimator runs something like this

view.post(new Runnable(){
    public void run(a) { prepare(); }})Copy the code

So during the process of preparing the animation (i.e. in the prepare method), you can get the size information of the view.

For example: Place the view view close to the right boundary of the parent layout with margin DP


public class SimpleAnimator extends LeptAnimator {
    @Override
    public AnimatorBuilder prepare(View... targets) {
        View view = targets[0];
        View ParentView = (View) view.getParent();

        int distance = ParentView.getWidth() - view.getLeft() - view.getWidth() - DistanceUtil.dp2px(10);
        return newAnimatorBuilder() .translationX(distance); }}Copy the code

It is now possible to calculate the size of a view, but it is still a bit of a hassle to calculate it every time, so the library’s AnimatorBuilder class provides several methods to simplify your sizing

Margin dp public AnimatorBuilder parentTop(int margin) // Place View close to the bottom of parent layout, ParentBottom (int margin) public AnimatorBuilder parentBottom(int margin) ParentLeft (int margin) public AnimatorBuilder parentLeft(int margin) Public AnimatorBuilder parentRight(int margin) // Move View to the left of target, RigntMargin public AnimatorBuilder leftof(View target, Public AnimatorBuilder rightof(View Target, int margin) public AnimatorBuilder topof(View target, int margin) public AnimatorBuilder bottomof(View target, int margin)Copy the code

All distances in the library are in dp. With these methods, the code above can be changed

public class SimpleAnimator extends LeptAnimator {
    @Override
    public AnimatorBuilder prepare(View... targets) {
        return new AnimatorBuilder()
                        .parentRignt(10); }}Copy the code

With a combination of these methods, it’s much easier to achieve some cool effects.

3.3. Complex animation implementation is not simple enough

Look at the sample code

public class SimpleAnimator extends LeptAnimator {

    // This constructor must be overridden. View represents the control that needs to animate and the control that needs to measure dimension information
    public SimpleAnimator(View... view) {
        super(view);

    }

    // Target is the view passed in the constructor
    // These views can be used to animate or measure dimensions
    @Override
    public AnimatorBuilder prepare(View... target) {

            AnimatorBuilder provides many useful methods
            return AnimatorBuilder
                    .animate(target[0]) 1 / / animationTranslationX (100) Dp / / unit
                    .ParentTop(10) // 10dp from the top
                    .duration(1000)

                    .with(target[1]) // Animation 2, animation 2 is executed synchronously with animation 1
                    .leftof(target[3].10); // Target [1] is moved to the right of target[3] at a distance of 10dp
                    .alpha(0.1)

                    .after(target[2]) // Animation 3, animation 1, animation 1, animation 3TranslationX (100); }}Copy the code
  • With allows you to define synchronously executed animations
  • After allows you to define sequentially executed animations

4, eggs

The library provides an interesting feature

// Sets the percentage of animation execution, 0.5 means half animation execution. LeptAnimator. Setpercent (0.2);Copy the code

This feature can be used to achieve some interesting effects, such as ScrollActivity in the demo (see the GIF in the next section)

5 or more

See demo for more usage, below is the demo renderings.





6, thank you

The implementation process of the library is based on the following open source projects:

  • ViewAnimator
  • AndroidViewAnimations