让你的Textview 跳跃起来

来源:互联网 发布:财务评价软件 编辑:程序博客网 时间:2024/05/17 20:34

直入主题!!!
这里写图片描述

在你的build.gradle 引入

compile 'com.android.support:support-annotations:23.1.1

android-support-annotations是Android官方提供的一个注解库,它提供了许多有用的注解,这些注解的生命周期为源码时期,也就是在编译之后则不再保留,通常用于辅助代码上的静态检查。但是如果你已经通过这种方式依赖了support-v4库的话,则不必再显式声明依赖它,因为support-v4也依赖于这个库,由于Gradle的传递依赖的特性,你的项目如果依赖了support-v4库的话,也会依赖到这个注解库。

详细介绍

http://blog.csdn.net/maosidiaoxian/article/details/50452706

使用

1.让 Jumping 跳跃起来
这里写图片描述

@Override    protected void onResume() {        super.onResume();        // those instead)        final TextView textView1 = (TextView) findViewById(R.id.jumping_text_1);        jumpingBeans1 = JumpingBeans.with(textView1)                .appendJumpingDots()                .build();    }    @Override    protected void onPause() {        super.onPause();        jumpingBeans1.stopJumping();        jumpingBeans2.stopJumping();    }

2.让。。。跳跃起来
这里写图片描述

final TextView textView2 = (TextView) findViewById(R.id.jumping_text_2);        jumpingBeans2 = JumpingBeans.with(textView2)                .makeTextJump(0, textView2.getText().toString().indexOf(' '))                .setIsWave(false)                .setLoopDuration(1000)                .build();

功能Bean
1.JumpingBeans

public final class JumpingBeans {    private static final String ELLIPSIS_GLYPH = "…";    private static final String THREE_DOTS_ELLIPSIS = "...";    private static final int THREE_DOTS_ELLIPSIS_LENGTH = 3;    private final JumpingBeansSpan[] jumpingBeans;    private final WeakReference<TextView> textView;    private JumpingBeans(JumpingBeansSpan[] beans, TextView textView) {        this.jumpingBeans = beans;        this.textView = new WeakReference<>(textView);    }    /**     * Create an instance of the {@link net.frakbot.jumpingbeans.JumpingBeans.Builder}     * applied to the provided {@code TextView}.     *     * @param textView The TextView to apply the JumpingBeans to     * @return the {@link net.frakbot.jumpingbeans.JumpingBeans.Builder}     */    public static Builder with(@NonNull TextView textView) {        return new Builder(textView);    }    /**     * Stops the jumping animation and frees up the animations.     */    public void stopJumping() {        for (JumpingBeansSpan bean : jumpingBeans) {            if (bean != null) {                bean.teardown();            }        }        cleanupSpansFrom(textView.get());    }    private static void cleanupSpansFrom(TextView textView) {        if (textView == null) {            return;        }        CharSequence text = textView.getText();        if (text instanceof Spanned) {            CharSequence cleanText = removeJumpingBeansSpansFrom((Spanned) text);            textView.setText(cleanText);        }    }    private static CharSequence removeJumpingBeansSpansFrom(Spanned text) {        SpannableStringBuilder sbb = new SpannableStringBuilder(text.toString());        Object[] spans = text.getSpans(0, text.length(), Object.class);        for (Object span : spans) {            if (!(span instanceof JumpingBeansSpan)) {                sbb.setSpan(span, text.getSpanStart(span), text.getSpanEnd(span), text.getSpanFlags(span));            }        }        return sbb;    }    /**     * Builder class for {@link net.frakbot.jumpingbeans.JumpingBeans} objects.     * <p/>     * Provides a way to set the fields of a {@link JumpingBeans} and generate     * the desired jumping beans effect. With this builder you can easily append     * a Hangouts-style trio of jumping suspension points to any TextView, or     * apply the effect to any other subset of a TextView's text.     * <p/>     * <p>Example:     * <p/>     * <pre class="prettyprint">     * JumpingBeans jumpingBeans = JumpingBeans.with(myTextView)     *   .appendJumpingDots()     *   .setLoopDuration(1500)     *   .build();     * </pre>     *     * @see JumpingBeans#with(TextView)     */    public static class Builder {        private static final float DEFAULT_ANIMATION_DUTY_CYCLE = 0.65f;        private static final int DEFAULT_LOOP_DURATION = 1300;   // ms        private static final int DEFAULT_WAVE_CHAR_DELAY = -1;        private final TextView textView;        private int startPos;        private int endPos;        private float animRange = DEFAULT_ANIMATION_DUTY_CYCLE;        private int loopDuration = DEFAULT_LOOP_DURATION;        private int waveCharDelay = DEFAULT_WAVE_CHAR_DELAY;        private CharSequence text;        private boolean wave;        Builder(TextView textView) {            this.textView = textView;        }        /**         * Appends three jumping dots to the end of a TextView text.         * <p/>         * This implies that the animation will by default be a wave.         * <p/>         * If the TextView has no text, the resulting TextView text will         * consist of the three dots only.         * <p/>         * The TextView text is cached to the current value at         * this time and set again in the {@link #build()} method, so any         * change to the TextView text done in the meantime will be lost.         * This means that <b>you should do all changes to the TextView text         * <i>before</i> you begin using this builder.</b>         * <p/>         * Call the {@link #build()} method once you're done to get the         * resulting {@link net.frakbot.jumpingbeans.JumpingBeans}.         *         * @see #setIsWave(boolean)         */        @NonNull        public Builder appendJumpingDots() {            CharSequence text = appendThreeDotsEllipsisTo(textView);            this.text = text;            this.wave = true;            this.startPos = text.length() - THREE_DOTS_ELLIPSIS_LENGTH;            this.endPos = text.length();            return this;        }        private static CharSequence appendThreeDotsEllipsisTo(TextView textView) {            CharSequence text = getTextSafe(textView);            if (text.length() > 0 && endsWithEllipsisGlyph(text)) {                text = text.subSequence(0, text.length() - 1);            }            if (!endsWithThreeEllipsisDots(text)) {                text = new SpannableStringBuilder(text).append(THREE_DOTS_ELLIPSIS);  // Preserve spans in original text            }            return text;        }        private static CharSequence getTextSafe(TextView textView) {            return !TextUtils.isEmpty(textView.getText()) ? textView.getText() : "";        }        private static boolean endsWithEllipsisGlyph(CharSequence text) {            CharSequence lastChar = text.subSequence(text.length() - 1, text.length());            return ELLIPSIS_GLYPH.equals(lastChar);        }        @SuppressWarnings("SimplifiableIfStatement")                 // For readability        private static boolean endsWithThreeEllipsisDots(CharSequence text) {            if (text.length() < THREE_DOTS_ELLIPSIS_LENGTH) {                // TODO we should try to normalize "invalid" ellipsis (e.g., ".." or "....")                return false;            }            CharSequence lastThreeChars = text.subSequence(text.length() - THREE_DOTS_ELLIPSIS_LENGTH, text.length());            return THREE_DOTS_ELLIPSIS.equals(lastThreeChars);        }        /**         * Appends three jumping dots to the end of a TextView text.         * <p/>         * This implies that the animation will by default be a wave.         * <p/>         * If the TextView has no text, the resulting TextView text will         * consist of the three dots only.         * <p/>         * The TextView text is cached to the current value at         * this time and set again in the {@link #build()} method, so any         * change to the TextView text done in the meantime will be lost.         * This means that <b>you should do all changes to the TextView text         * <i>before</i> you begin using this builder.</b>         * <p/>         * Call the {@link #build()} method once you're done to get the         * resulting {@link net.frakbot.jumpingbeans.JumpingBeans}.         *         * @param startPos The position of the first character to animate         * @param endPos   The position after the one the animated range ends at         *                 (just like in {@link String#substring(int)})         * @see #setIsWave(boolean)         */        @NonNull        public Builder makeTextJump(@IntRange(from = 0) int startPos, @IntRange(from = 0) int endPos) {            CharSequence text = textView.getText();            ensureTextCanJump(startPos, endPos, text);            this.text = text;            this.wave = true;            this.startPos = startPos;            this.endPos = endPos;            return this;        }        private static CharSequence ensureTextCanJump(int startPos, int endPos, CharSequence text) {            if (text == null) {                throw new NullPointerException("The textView text must not be null");            }            if (endPos < startPos) {                throw new IllegalArgumentException("The start position must be smaller than the end position");            }            if (startPos < 0) {                throw new IndexOutOfBoundsException("The start position must be non-negative");            }            if (endPos > text.length()) {                throw new IndexOutOfBoundsException("The end position must be smaller than the text length");            }            return text;        }        /**         * Sets the fraction of the animation loop time spent actually animating.         * The rest of the time will be spent "resting".         *         * @param animatedRange The fraction of the animation loop time spent         *                      actually animating the characters         */        @NonNull        public Builder setAnimatedDutyCycle(@FloatRange(from = 0f, to = 1f, fromInclusive = false) float animatedRange) {            if (animatedRange <= 0f || animatedRange > 1f) {                throw new IllegalArgumentException("The animated range must be in the (0, 1] range");            }            this.animRange = animatedRange;            return this;        }        /**         * Sets the jumping loop duration.         *         * @param loopDuration The jumping animation loop duration, in milliseconds         */        @NonNull        public Builder setLoopDuration(@IntRange(from = 1) int loopDuration) {            if (loopDuration < 1) {                throw new IllegalArgumentException("The loop duration must be bigger than zero");            }            this.loopDuration = loopDuration;            return this;        }        /**         * Sets the delay for starting the animation of every single dot over the         * start of the previous one, in milliseconds. The default value is         * the loop length divided by three times the number of character animated         * by this instance of JumpingBeans.         * <p/>         * Only has a meaning when the animation is a wave.         *         * @param waveCharOffset The start delay for the animation of every single         *                       character over the previous one, in milliseconds         * @see #setIsWave(boolean)         */        @NonNull        public Builder setWavePerCharDelay(@IntRange(from = 0) int waveCharOffset) {            if (waveCharOffset < 0) {                throw new IllegalArgumentException("The wave char offset must be non-negative");            }            this.waveCharDelay = waveCharOffset;            return this;        }        /**         * Sets a flag that determines if the characters will jump in a wave         * (i.e., with a delay between each other) or all at the same         * time.         *         * @param wave If true, the animation is going to be a wave; if         *             false, all characters will jump ay the same time         * @see #setWavePerCharDelay(int)         */        @NonNull        public Builder setIsWave(boolean wave) {            this.wave = wave;            return this;        }        /**         * Combine all of the options that have been set and return a new         * {@link net.frakbot.jumpingbeans.JumpingBeans} instance.         * <p/>         * Remember to call the {@link #stopJumping()} method once you're done         * using the JumpingBeans (that is, when you detach the TextView from         * the view tree, you hide it, or the parent Activity/Fragment goes in         * the paused status). This will allow to release the animations and         * free up memory and CPU that would be otherwise wasted.         */        @NonNull        public JumpingBeans build() {            SpannableStringBuilder sbb = new SpannableStringBuilder(text);            JumpingBeansSpan[] spans;            if (wave) {                spans = buildWavingSpans(sbb);            } else {                spans = buildSingleSpan(sbb);            }            textView.setText(sbb);            return new JumpingBeans(spans, textView);        }        @SuppressWarnings("Range")          // Lint bug: the if makes sure waveCharDelay >= 0        private JumpingBeansSpan[] buildWavingSpans(SpannableStringBuilder sbb) {            JumpingBeansSpan[] spans;            if (waveCharDelay == DEFAULT_WAVE_CHAR_DELAY) {                waveCharDelay = loopDuration / (3 * (endPos - startPos));            }            spans = new JumpingBeansSpan[endPos - startPos];            for (int pos = startPos; pos < endPos; pos++) {                JumpingBeansSpan jumpingBean =                        new JumpingBeansSpan(textView, loopDuration, pos - startPos, waveCharDelay, animRange);                sbb.setSpan(jumpingBean, pos, pos + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);                spans[pos - startPos] = jumpingBean;            }            return spans;        }        private JumpingBeansSpan[] buildSingleSpan(SpannableStringBuilder sbb) {            JumpingBeansSpan[] spans;            spans = new JumpingBeansSpan[]{new JumpingBeansSpan(textView, loopDuration, 0, 0, animRange)};            sbb.setSpan(spans[0], startPos, endPos, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);            return spans;        }    }}

2 . JumpingBeansSpan

final class JumpingBeansSpan extends SuperscriptSpan implements ValueAnimator.AnimatorUpdateListener {    private final WeakReference<TextView> textView;    private final int delay;    private final int loopDuration;    private final float animatedRange;    private int shift;    private ValueAnimator jumpAnimator;    public JumpingBeansSpan(@NonNull TextView textView,                            @IntRange(from = 1) int loopDuration,                            @IntRange(from = 0) int position,                            @IntRange(from = 0) int waveCharOffset,                            @FloatRange(from = 0, to = 1, fromInclusive = false) float animatedRange) {        this.textView = new WeakReference<>(textView);        this.delay = waveCharOffset * position;        this.loopDuration = loopDuration;        this.animatedRange = animatedRange;    }    @Override    public void updateMeasureState(TextPaint tp) {        initIfNecessary(tp.ascent());        tp.baselineShift = shift;    }    @Override    public void updateDrawState(TextPaint tp) {        initIfNecessary(tp.ascent());        tp.baselineShift = shift;    }    private void initIfNecessary(float ascent) {        if (jumpAnimator != null) {            return;        }        this.shift = 0;        int maxShift = (int) ascent / 2;        jumpAnimator = ValueAnimator.ofInt(0, maxShift);        jumpAnimator                .setDuration(loopDuration)                .setStartDelay(delay);        jumpAnimator.setInterpolator(new JumpInterpolator(animatedRange));        jumpAnimator.setRepeatCount(ValueAnimator.INFINITE);        jumpAnimator.setRepeatMode(ValueAnimator.RESTART);        jumpAnimator.addUpdateListener(this);        jumpAnimator.start();    }    @Override    public void onAnimationUpdate(ValueAnimator animation) {        // No need for synchronization as this always runs on main thread anyway        TextView v = textView.get();        if (v != null) {            updateAnimationFor(animation, v);        } else {            cleanupAndComplainAboutUserBeingAFool();        }    }    private void updateAnimationFor(ValueAnimator animation, TextView v) {        if (isAttachedToHierarchy(v)) {            shift = (int) animation.getAnimatedValue();            v.invalidate();        }    }    private static boolean isAttachedToHierarchy(View v) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {            return v.isAttachedToWindow();        }        return v.getParent() != null;   // Best-effort fallback (without adding support-v4 just for this...)    }    private void cleanupAndComplainAboutUserBeingAFool() {        // The textview has been destroyed and teardown() hasn't been called        teardown();        Log.w("JumpingBeans", "!!! Remember to call JumpingBeans.stopJumping() when appropriate !!!");    }    public void teardown() {        if (jumpAnimator != null) {            jumpAnimator.cancel();            jumpAnimator.removeAllListeners();        }        if (textView.get() != null) {            textView.clear();        }    }    /**     * A tweaked {@link android.view.animation.AccelerateDecelerateInterpolator}     * that covers the full range in a fraction of its input range, and holds on     * the final value on the rest of the input range. By default, this fraction     * is 65% of the full range.     *     * @see JumpingBeans.Builder#DEFAULT_ANIMATION_DUTY_CYCLE     */    private static class JumpInterpolator implements TimeInterpolator {        private final float animRange;        public JumpInterpolator(float animatedRange) {            animRange = Math.abs(animatedRange);        }        @Override        public float getInterpolation(float input) {            // We want to map the [0, PI] sine range onto [0, animRange]            if (input > animRange) {                return 0f;            }            double radians = (input / animRange) * Math.PI;            return (float) Math.sin(radians);        }    }}
0 0
原创粉丝点击