水波纹

来源:互联网 发布:文明5 mac 语言 编辑:程序博客网 时间:2024/06/11 20:55
公司中项目中看到,学习下源码。
// 使用
<xx.xxx.view.extend.PulsatorLayout            android:id="@+id/wait"            android:layout_width="200dp"            android:layout_height="200dp"            android:layout_gravity="center"            android:layout_marginTop="@dimen/margin_2xxl"            app:pulse_color="@color/colorAccent"            app:pulse_count="4"            app:pulse_duration="5000"            app:pulse_repeat="0"            app:pulse_startFromScratch="false">                    </xx.xxx.view.extend.PulsatorLayout>


// 源码import android.animation.Animator;import android.animation.AnimatorSet;import android.animation.ObjectAnimator;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;import android.view.animation.LinearInterpolator;import android.widget.RelativeLayout;import java.util.ArrayList;import java.util.List;/** * 水波纹 */public class PulsatorLayout extends RelativeLayout {    public static final int INFINITE = 0;    private static final int DEFAULT_COUNT = 4;    private static final int DEFAULT_COLOR = Color.rgb(0, 116, 193);    private static final int DEFAULT_DURATION = 7000;    private static final int DEFAULT_REPEAT = INFINITE;    private static final boolean DEFAULT_START_FROM_SCRATCH = true;    private int mCount;    private int mDuration;    private int mRepeat;    private boolean mStartFromScratch;    private final List<View> mViews = new ArrayList<>();    private AnimatorSet mAnimatorSet;    private Paint mPaint;    private float mRadius;    private float mCenterX;    private float mCenterY;    private boolean mIsStarted;    /**     * Simple constructor to use when creating a view from code.     *     * @param context The Context the view is running in, through which it can access the current     *                theme, resources, etc.     */    public PulsatorLayout(Context context) {        this(context, null, 0);    }    /**     * Constructor that is called when inflating a view from XML.     *     * @param context The Context the view is running in, through which it can access the current     *                theme, resources, etc.     * @param attrs   The attributes of the XML tag that is inflating the view.     */    public PulsatorLayout(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    /**     * Perform inflation from XML and apply a class-specific base style from a theme attribute.     *     * @param context      The Context the view is running in, through which it can access the current     *                     theme, resources, etc.     * @param attrs        The attributes of the XML tag that is inflating the view.     * @param defStyleAttr An attribute in the current theme that contains a reference to a style     *                     resource that supplies default values for the view. Can be 0 to not look     *                     for defaults.     */    public PulsatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        // get attributes        TypedArray attr = context.getTheme().obtainStyledAttributes(                attrs, R.styleable.Pulsator4Droid, 0, 0);        mCount = DEFAULT_COUNT;        mDuration = DEFAULT_DURATION;        mRepeat = DEFAULT_REPEAT;        mStartFromScratch = DEFAULT_START_FROM_SCRATCH;        int color = DEFAULT_COLOR;        try {            mCount = attr.getInteger(R.styleable.Pulsator4Droid_pulse_count, DEFAULT_COUNT);            mDuration = attr.getInteger(R.styleable.Pulsator4Droid_pulse_duration,                    DEFAULT_DURATION);            mRepeat = attr.getInteger(R.styleable.Pulsator4Droid_pulse_repeat, DEFAULT_REPEAT);            mStartFromScratch = attr.getBoolean(R.styleable.Pulsator4Droid_pulse_startFromScratch,                    DEFAULT_START_FROM_SCRATCH);            color = attr.getColor(R.styleable.Pulsator4Droid_pulse_color, DEFAULT_COLOR);        } finally {            attr.recycle();        }        // create paint        mPaint = new Paint();        mPaint.setAntiAlias(true);        mPaint.setStyle(Paint.Style.FILL);        mPaint.setColor(color);        // create views        build();    }    /**     * Start pulse animation.     */    public synchronized void start() {        if (mAnimatorSet == null || mIsStarted) {            return;        }        mAnimatorSet.start();        if (!mStartFromScratch) {            ArrayList<Animator> animators = mAnimatorSet.getChildAnimations();            for (Animator animator : animators) {                ObjectAnimator objectAnimator = (ObjectAnimator) animator;                long delay = objectAnimator.getStartDelay();                objectAnimator.setStartDelay(0);                objectAnimator.setCurrentPlayTime(mDuration - delay);            }        }    }    /**     * Stop pulse animation.     */    public synchronized void stop() {        if (mAnimatorSet == null || !mIsStarted) {            return;        }        mAnimatorSet.end();    }    public synchronized boolean isStarted() {        return (mAnimatorSet != null && mIsStarted);    }    /**     * Get number of pulses.     *     * @return Number of pulses     */    public int getCount() {        return mCount;    }    /**     * Get pulse duration.     *     * @return Duration of single pulse in milliseconds     */    public int getDuration() {        return mDuration;    }    /**     * Set number of pulses.     *     * @param count Number of pulses     */    public void setCount(int count) {        if (count < 0) {            throw new IllegalArgumentException("Count cannot be negative");        }        if (count != mCount) {            mCount = count;            reset();            invalidate();        }    }    /**     * Set single pulse duration.     *     * @param millis Pulse duration in milliseconds     */    public void setDuration(int millis) {        if (millis < 0) {            throw new IllegalArgumentException("Duration cannot be negative");        }        if (millis != mDuration) {            mDuration = millis;            reset();            invalidate();        }    }    @Override    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();        int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom();        mCenterX = width * 0.5f;        mCenterY = height * 0.5f;        mRadius = Math.min(width, height) * 0.5f;        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    /**     * Remove all views and animators.     */    private void clear() {        // remove animators        stop();        // remove old views        for (View view : mViews) {            removeView(view);        }        mViews.clear();    }    /**     * Build pulse views and animators.     */    private void build() {        // create views and animators        LayoutParams layoutParams = new LayoutParams(                LayoutParams.MATCH_PARENT,                LayoutParams.MATCH_PARENT);        int repeatCount = (mRepeat == INFINITE) ? ObjectAnimator.INFINITE : mRepeat;        List<Animator> animators = new ArrayList<>();        for (int index = 0; index < mCount; index++) {            // setup view            PulseView pulseView = new PulseView(getContext());            pulseView.setScaleX(0);            pulseView.setScaleY(0);            pulseView.setAlpha(1);            addView(pulseView, index, layoutParams);            mViews.add(pulseView);            long delay = index * mDuration / mCount;            // setup animators            ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(pulseView, "ScaleX", 0f, 1f);            scaleXAnimator.setRepeatCount(repeatCount);            scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);            scaleXAnimator.setStartDelay(delay);            animators.add(scaleXAnimator);            ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(pulseView, "ScaleY", 0f, 1f);            scaleYAnimator.setRepeatCount(repeatCount);            scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);            scaleYAnimator.setStartDelay(delay);            animators.add(scaleYAnimator);            ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(pulseView, "Alpha", 1f, 0f);            alphaAnimator.setRepeatCount(repeatCount);            alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);            alphaAnimator.setStartDelay(delay);            animators.add(alphaAnimator);        }        mAnimatorSet = new AnimatorSet();        mAnimatorSet.playTogether(animators);        mAnimatorSet.setInterpolator(new LinearInterpolator());        mAnimatorSet.setDuration(mDuration);        mAnimatorSet.addListener(mAnimatorListener);    }    /**     * Reset views and animations.     */    private void reset() {        boolean isStarted = isStarted();        clear();        build();        if (isStarted) {            start();        }    }    private class PulseView extends View {        public PulseView(Context context) {            super(context);        }        @Override        protected void onDraw(Canvas canvas) {            canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);        }    }    private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {        @Override        public void onAnimationStart(Animator animator) {            mIsStarted = true;        }        @Override        public void onAnimationEnd(Animator animator) {            mIsStarted = false;        }        @Override        public void onAnimationCancel(Animator animator) {            mIsStarted = false;        }        @Override        public void onAnimationRepeat(Animator animator) {        }    };}


// attr.xml文件
<declare-styleable name="Pulsator4Droid">        <attr name="pulse_count" format="integer"/>        <attr name="pulse_duration" format="integer"/>        <attr name="pulse_color" format="color"/>        <attr name="pulse_repeat" format="integer"/>        <attr name="pulse_startFromScratch" format="boolean"/>    </declare-styleable>