Using spannablestring to implement a load small animation

Source: Internet
Author: User



It's still GitHub. Open Source project: Waitingdots
The project code is not many, the implementation is very simple, but very interesting because the basic elements of animation is not drawn, but the use of spannablestring to achieve.


    • Dotstextview.java
    • Jumpingspan.java
    • Mainactivity.java
      Dotstextview is the implementation body of the animation.
      Jumpingspan is the basic element, is the plugin in the animation
      In Mainactivity, you can simply introduce Dotstextview in the layout.
      Here is the split line, show code:
package pl.tajchert.sample;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.text.style.ReplacementSpan;
/ * ReplacementSpan is a really amazing thing, and it is rarely introduced on the official api
 * Several main functions are also do nothing.
 * The custom translationX and translationY in this example have no effect. If you assign values to two variables
 * Then the first JumpingSpan increases the distance from the previous element. This is used here to allow each "." To operate independently for a single unit.
 * /
public class JumpingSpan extends ReplacementSpan {

    private float translationX = 0;
    private float translationY = 0;

    @Override
    public int getSize (Paint paint, CharSequence text, int start, int end, FontMetricsInt fontMetricsInt) {
        return (int) paint.measureText (text, start, end);
    }

    @Override
    public void draw (Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        canvas.drawText (text, start, end, x + translationX, y + translationY, paint);
    }

    public void setTranslationX (float translationX) {
        this.translationX = translationX;
    }

    public void setTranslationY (float translationY) {
        this.translationY = translationY;
    }
} 
package pl.tajchert.sample;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Looper;
import android.text.SpannableString;
import android.text.Spanned;
import android.util.AttributeSet;
import android.widget.TextView;

import pl.tajchert.waitingdots.R;

public class DotsTextView extends TextView {

    private JumpingSpan dotOne;
    private JumpingSpan dotTwo;
    private JumpingSpan dotThree;

    private int showSpeed = 700;

    private int jumpHeight;
    private boolean autoPlay;
    private boolean isPlaying;
    private boolean isHide;
    private int period;
    private long startTime;

    private boolean lockDotOne;
    private boolean lockDotTwo;
    private boolean lockDotThree;

    private Handler handler;
    private AnimatorSet mAnimatorSet = new AnimatorSet ();
    private float textWidth;

    public DotsTextView (Context context) {
        super (context);
        init (context, null);
    }

    public DotsTextView (Context context, AttributeSet attrs) {
        super (context, attrs);
        init (context, attrs);
    }

    public DotsTextView (Context context, AttributeSet attrs, int defStyleAttr) {
        super (context, attrs, defStyleAttr);
        init (context, attrs);
    }

    private void init (Context context, AttributeSet attrs) {
        handler = new Handler (Looper.getMainLooper ());
        // Custom attributes
        if (attrs! = null) {
            TypedArray typedArray = context.obtainStyledAttributes (attrs, R.styleable.WaitingDots);
            period = typedArray.getInt (R.styleable.WaitingDots_period, 6000);
            jumpHeight = typedArray.getInt (R.styleable.WaitingDots_jumpHeight, (int) (getTextSize () / 4));
            autoPlay = typedArray.getBoolean (R.styleable.WaitingDots_autoplay, true);
            typedArray.recycle ();
        }
        dotOne = new JumpingSpan ();
        dotTwo = new JumpingSpan ();
        dotThree = new JumpingSpan ();
        // Set each point to jumpingSpan type
        SpannableString spannable = new SpannableString ("...");
        spannable.setSpan (dotOne, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        spannable.setSpan (dotTwo, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        spannable.setSpan (dotThree, 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        setText (spannable, BufferType.SPANNABLE);

        textWidth = getPaint (). measureText (".", 0, 1);
        // The next two are to add updateListener to point 1, and use it to perform the refresh action
        ObjectAnimator dotOneJumpAnimator = createDotJumpAnimator (dotOne, 0);
        dotOneJumpAnimator.addUpdateListener (new AnimatorUpdateListener () {

            @Override
            public void onAnimationUpdate (ValueAnimator valueAnimator) {
                invalidate ();
            }
        });
        // Here is an animationSet to control the combined action of three points
        mAnimatorSet.playTogether (dotOneJumpAnimator, createDotJumpAnimator (dotTwo,
                period / 6), createDotJumpAnimator (dotThree, period * 2/6));

        isPlaying = autoPlay;
        if (autoPlay) {
            start ();
        }
    }

    public void start () {
        isPlaying = true;
        // INFINITE once started
        setAllAnimationsRepeatCount (ValueAnimator.INFINITE);
        mAnimatorSet.start ();
    }
    / * Animation implementation core
    * @ param jumpingSpan passing point,
    * @delay The animation is delayed. Use this parameter to make the three points move with time difference
     * /
    private ObjectAnimator createDotJumpAnimator (JumpingSpan jumpingSpan, long delay) {
        ObjectAnimator jumpAnimator = ObjectAnimator.ofFloat (jumpingSpan, "translationY", 0, -jumpHeight);
        / * setEvaluator is an important function to smooth the "rhythm" of point movement through equations. You can try to remove this section.
            You will find that the point will move up and down at the default speed, which is particularly stiff. Evaluate in TypeEvaluator can calculate the current position of the point.
            The trajectory movement of the point is indirectly designed by calculating the current point, and achieves the same effect as the time interpolation TimeInterpolator, just like you don't know the speed but you know that the position per second is equivalent to the speed.
            The calculation method is this: see this blog post
            http://blog.csdn.net/serapme/article/details/47006049
            ValueAnimator also encapsulates a TypeAnimator, and calculates the attribute value based on the start and end values and the value calculated by TimeIniterpolator.
            ValueAnimator calculates a time factor (0 ~ 1) according to the ratio of the animation's time to the total animation time (duration), then calculates another factor based on TimeInterpolator, and finally TypeAnimator calculates the attribute value through this factor, as in the example above 10ms Time:
            First calculate the time factor, that is, the percentage of time elapsed: t = 10ms / 40ms = 0.25
            The interpolation factor after interpolation calculation (inteplator) is about 0.15. In the above example, AccelerateDecelerateInterpolator is used. The calculation formula is (input is the time factor):
            (Math.cos ((input + 1) * Math.PI) / 2.0f) + 0.5f;
            Finally, the attribute value at 10ms is calculated according to the TypeEvaluator: 0.15 * (40-0) = 6pixel. In the above example, TypeEvaluator is FloatEvaluator, and the calculation method is:
            public Float evaluate (float fraction, Number startValue, Number endValue) {
                    float startFloat = startValue.floatValue ();
                    return startFloat + fraction * (endValue.floatValue ()-startFloat);
             }
         * /
        jumpAnimator.setEvaluator (new TypeEvaluator <Number> () {

            @Override
            public Number eval
uate (float fraction, Number from, Number to) {
                return Math.max (0, Math.sin (fraction * Math.PI * 2)) * (to.floatValue ()-from.floatValue ());
            }
        });
        jumpAnimator.setDuration (period);
        jumpAnimator.setStartDelay (delay);
        jumpAnimator.setRepeatCount (ValueAnimator.INFINITE);
        jumpAnimator.setRepeatMode (ValueAnimator.RESTART);
        return jumpAnimator;
    }
    // The following non-core functions are not difficult to comment, mainly because they are lazy ~
    public void stop () {
        isPlaying = false;
        setAllAnimationsRepeatCount (0);
    }

    private void setAllAnimationsRepeatCount (int repeatCount) {
        for (Animator animator: mAnimatorSet.getChildAnimations ()) {
            if (animator instanceof ObjectAnimator) {
                ((ObjectAnimator) animator) .setRepeatCount (repeatCount);
            }
        }
    }

    public void hide () {

        createDotHideAnimator (dotThree, 2) .start ();

        ObjectAnimator dotTwoMoveRightToLeft = createDotHideAnimator (dotTwo, 1);
        dotTwoMoveRightToLeft.addUpdateListener (new AnimatorUpdateListener () {

            @Override
            public void onAnimationUpdate (ValueAnimator valueAnimator) {
                invalidate ();
            }
        });

        dotTwoMoveRightToLeft.start ();
        isHide = true;
    }

    public void show () {
        ObjectAnimator dotThreeMoveRightToLeft = createDotShowAnimator (dotThree, 2);

        dotThreeMoveRightToLeft.start ();

        ObjectAnimator dotTwoMoveRightToLeft = createDotShowAnimator (dotTwo, 1);
        dotTwoMoveRightToLeft.addUpdateListener (new AnimatorUpdateListener () {

            @Override
            public void onAnimationUpdate (ValueAnimator valueAnimator) {
                invalidate ();
            }
        });

        dotTwoMoveRightToLeft.start ();
        isHide = false;
    }

    private ObjectAnimator createDotHideAnimator (JumpingSpan span, float widthMultiplier) {
        return createDotHorizontalAnimator (span, 0, -textWidth * widthMultiplier);
    }

    private ObjectAnimator createDotShowAnimator (JumpingSpan span, int widthMultiplier) {
        return createDotHorizontalAnimator (span, -textWidth * widthMultiplier, 0);
    }

    private ObjectAnimator createDotHorizontalAnimator (JumpingSpan span, float from, float to) {
        ObjectAnimator dotThreeMoveRightToLeft = ObjectAnimator.ofFloat (span, "translationX", from, to);
        dotThreeMoveRightToLeft.setDuration (showSpeed);
        return dotThreeMoveRightToLeft;
    }

    public void showAndPlay () {
        show ();
        start ();
    }

    public void hideAndStop () {
        hide ();
        stop ();
    }

    public boolean isHide () {
        return isHide;
    }

    public boolean isPlaying () {
        return isPlaying;
    }

    public void setJumpHeight (int jumpHeight) {
        this.jumpHeight = jumpHeight;
    }

    public void setPeriod (int period) {
        this.period = period;
    }
} 


There is no doubt that the layout can be introduced into the activity.



dotsTextView = (DotsTextView) findViewById(R.id.dots);


A simple-to-use Loadingview.



Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.



Using spannablestring to implement a load small animation


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.