Android SwipeRequestLayout

前言

Google對Android的刷新控件開刀之後,目前很多APP已經開始在使用SwipeRefreshLayout這個控件了,當我第一次看見這個控件的時候我就決定使用它,只因爲看着簡單實用,不需要對刷新進行過多的修飾。但是這個控件有一個致命的問題就是Google對控件沒有寫加載更多。很多Android同胞於是就對這個控件進行了重寫,補足了這個控件的缺點,但是此次我不是對此控件重寫,我是重頭開始寫,看着這個控件的效果自己來書寫這個控件,這樣自己的書寫的控件就算是出問題了,自己好修改而不過多依賴於Google工程師的View。此次的控件在原控件的效果之上對控件下拉到可以鬆開的時刻,進行陰影顏色的同色處理。當然也可以刷新或者加載的時候對控件本身進行一次陰影顏色的同色處理。

知識

(1)View的Circle和Arc繪製

 canvas.drawCircle(centerX, centerY, circleRadius, paint);、

 RectF oval = new RectF(centerX - arcRadius, centerY - arcRadius, centerX + arcRadius, centerY + arcRadius);
 canvas.drawArc(oval, startAngle, sweepAngle, false, paint);

(2)Arc繪製動畫效果實現

ValueAnimator animator = ValueAnimator.ofFloat(sweepAngle);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float animatedValue = (float) animation.getAnimatedValue();
               //拿到動畫值,進行重寫繪製即可
            }
        });
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(Animation.INFINITE);
        animator.setDuration(duration);
        animator.start();

(3)事件的分發機制

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float moveY = event.getY() - downY;
                float moveX = event.getX() - downX;
                if (Math.abs(moveY) < moveX) {
                    return super.onInterceptTouchEvent(event);
                }
                if (moveY > 0 ) {
                	//isContentViewRefreshEnable()==true.即是對事件進行攔截,交給onTouchEvent,這樣你才能下拉刷新,
                	//反之不能攔截,你就得不到事件無法操作。
                    return isContentViewRefreshEnable();
                }
                if (moveY < 0 ) {
                     //isContentViewLoadEnable()==true.即是對事件進行攔截,交給onTouchEvent,這樣你才能上拉加載,
                	//反之不能攔截,你就得不到事件無法操作。
                    return isContentViewLoadEnable();
                }
                break;
        }
        return super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
          		//手指鬆開處理代碼
                break;
            case MotionEvent.ACTION_MOVE:
                float moveY = event.getY() - downY;
                if (moveY > 0) {//下滑
                   	
                } else {//上滑
                   
                }
                //重繪
                requestLayout();
                break;
        }
        return super.onTouchEvent(event);
    }

使用

類文件 apk
下載 下載

(1)xml佈局

	<!---注意類包名-->
    <com.android.widget.swipe.SwipeRequestLayout
        android:id="@+id/sal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ListView
            android:id="@+id/lv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></ListView>
            
    </com.android.widget.swipe.SwipeRequestLayout>

(2)設置刷新監聽

	/**
     * 設置刷新監聽
     *
     * @param refreshListener
     */
    public void setOnSwipeRefreshListener(OnSwipeRefreshListener refreshListener)

(3)設置加載監聽

    /**
     * 設置加載監聽
     *
     * @param loadListener
     */
    public void setOnSwipeLoadListener(OnSwipeLoadListener loadListener)

(4)刷新顯示隱藏

	/**
     * 設置正在刷新
     *
     * @param isRefreshing 是否開始刷新
     */
    public void setRefreshing(boolean isRefreshing) 

(5)加載顯示隱藏

    /**
     * 設置正在加載
     *
     * @param isLoading 是否開始加載
     */
    public void setLoading(boolean isLoading)

SwipeRequestLayout

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.ScrollView;

/**
 * Created by Relin
 * on 2018-09-26.
 */
public class SwipeRequestLayout extends FrameLayout {

    //頭部視圖
    private SwipeRequestView headerView;
    //內容視圖
    private View contentView[];
    //底部視圖
    private SwipeRequestView footerView;
    //頭部高度
    private float headerHeight = dpToPx(80);
    //腳部高度
    private float footerHeight = dpToPx(80);
    //刷新是否可用
    private boolean refreshEnable = true;
    //加載是否可用
    private boolean loadEnable = true;
    //是否正在刷新
    private boolean isRefreshing;
    //是否正在加載
    private boolean isLoading;
    //按下的座標
    private float downX, downY;
    //刷新移動距離
    private float refreshMoveY = 0;
    //加載移動距離
    private float loadMoveY = 0;
    //刷新停留距離
    private float refreshRemainY = 0;
    //加載停留距離
    private float loadRemainY = 0;
    //刷新監聽
    private OnSwipeRefreshListener refreshListener;
    //加載監聽
    private OnSwipeLoadListener loadListener;
    //縮放動畫師
    private ValueAnimator animator;
    //縮放動畫運行狀態
    private boolean isAnimatorRunning;
    //縮放時間
    private int scaleDuration = 500;
    //延遲時間
    private int delayDuration = 300;
    //頭部陰影
    private int headerShadowColor;
    //腳部陰影
    private int footerShadowColor;
    //內容類型-列表
    private AbsListView absListView;
    //內容類型-ScrollView
    private ScrollView scrollView;

    public SwipeRequestLayout(@NonNull Context context) {
        super(context);
        initHeaderFooter(context);
    }

    public SwipeRequestLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initHeaderFooter(context);
    }

    public SwipeRequestLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initHeaderFooter(context);
    }

    /**
     * 初始話頭部和底部View
     *
     * @param context
     */
    private void initHeaderFooter(Context context) {
        headerView = new SwipeRequestView(context);
        footerView = new SwipeRequestView(context);
        headerShadowColor = headerView.getShadowColor();
        footerShadowColor = footerView.getShadowColor();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //內容
        contentView = new View[getChildCount()];
        for (int i = 0; i < getChildCount(); i++) {
            if (getChildAt(i) instanceof ScrollView) {
                scrollView = (ScrollView) getChildAt(i);
            }
            if (getChildAt(i) instanceof AbsListView) {
                absListView = (AbsListView) getChildAt(i);
            }
            contentView[i] = getChildAt(i);
        }
        //頭部
        LayoutParams headerParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int) headerHeight);
        headerView.setLayoutParams(headerParams);
        headerView.setBackgroundColor(Color.TRANSPARENT);
        addView(headerView);
        //腳部
        LayoutParams footerParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int) footerHeight);
        footerView.setLayoutParams(footerParams);
        footerView.setBackgroundColor(Color.TRANSPARENT);
        footerParams.gravity = Gravity.BOTTOM;
        addView(footerView);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //頭部
        MarginLayoutParams headerParams = (MarginLayoutParams) headerView.getLayoutParams();
        left = getPaddingLeft() + headerParams.leftMargin;
        top = getPaddingTop() + headerParams.topMargin - headerView.getMeasuredHeight() + (int) refreshRemainY + (int) refreshMoveY;
        right = left + headerView.getMeasuredWidth();
        bottom = top + headerView.getMeasuredHeight();
        headerView.layout(left, top, right, bottom);
        //內容區域
        for (int i = 0; i < contentView.length; i++) {
            View child = contentView[i];
            MarginLayoutParams childParams = (MarginLayoutParams) child.getLayoutParams();
            left = getPaddingLeft() + childParams.leftMargin;
            top = getPaddingTop() + childParams.topMargin;
            right = left + child.getMeasuredWidth();
            bottom = top + child.getMeasuredHeight();
            child.layout(left, top, right, bottom);
        }
        //腳部
        MarginLayoutParams footerParams = (MarginLayoutParams) footerView.getLayoutParams();
        left = getPaddingLeft() + footerParams.leftMargin;
        top = getMeasuredHeight() + getPaddingTop() + footerParams.topMargin + (int) loadRemainY + (int) loadMoveY;
        right = left + footerView.getMeasuredWidth();
        bottom = top + footerView.getMeasuredHeight();
        footerView.layout(left, top, right, bottom);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float moveY = event.getY() - downY;
                float moveX = event.getX() - downX;
                if (Math.abs(moveY) < moveX) {
                    return super.onInterceptTouchEvent(event);
                }
                if (moveY > 0 && refreshEnable) {
                    return isContentViewRefreshEnable();
                }
                if (moveY < 0 && loadEnable) {
                    return isContentViewLoadEnable();
                }
                break;
        }
        return super.onInterceptTouchEvent(event);
    }

    private float bound = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                bound = 0;
                if (isRefreshing) {
                    headerView.start();
                    headerView.setShadowColor(headerShadowColor);
                    //刷新可用就停留,不可用就不停留
                    if (refreshListener != null && refreshMoveY > 0 && refreshEnable) {
                        refreshListener.onSwipeRefresh();
                    }
                    if (refreshEnable) {
                        refreshRemainY = headerHeight;
                        refreshMoveY = 0;
                        isRefreshing = true;
                        requestLayout();
                    } else {
                        createScaleAnimator(headerView).start();
                    }
                    return true;
                }
                if (isLoading) {
                    footerView.start();
                    footerView.setShadowColor(footerShadowColor);
                    loadRemainY = loadEnable ? -footerView.getMeasuredHeight() : 0;
                    if (loadListener != null && loadMoveY < 0 && loadEnable) {
                        loadListener.onSwipeLoad();
                    }
                    if (loadEnable) {
                        loadMoveY = 0;
                        isLoading = true;
                        requestLayout();
                    } else {
                        createScaleAnimator(footerView).start();
                    }
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float moveY = event.getY() - downY;
                //正在刷新不能上拉加載,正在加載不能下拉刷新
                if (isRefreshing && moveY < 0 || isLoading && moveY > 0 || isAnimatorRunning) {
                    break;
                }
                //利用屏幕的1/5做界限
                int each = 5;
                float movePercent = (moveY) / (getMeasuredHeight() / each);
                if (Math.abs(movePercent) > each) {
                    break;
                }
                if (moveY > 0) {//下滑
                    headerView.setScaleX(1);
                    headerView.setScaleY(1);
                    headerView.cancel();
                    headerView.setArcStartAngle(-90);
                    headerView.setArcSweepAngle(360 * movePercent);
                    refreshEnable = movePercent >= 1;
                    headerView.setShadowColor(refreshEnable ? headerView.getArcColor() : headerShadowColor);
                    refreshMoveY = moveY;
                    isRefreshing = true;
                    isLoading = false;
                } else {//上滑
                    footerView.setScaleX(1);
                    footerView.setScaleY(1);
                    footerView.cancel();
                    footerView.setArcStartAngle(-90);
                    footerView.setArcSweepAngle(360 * Math.abs(movePercent));
                    loadEnable = Math.abs(movePercent) >= 1;
                    footerView.setShadowColor(loadEnable ? footerView.getArcColor() : footerShadowColor);
                    loadMoveY = moveY;
                    isRefreshing = false;
                    isLoading = true;
                }
                if (refreshEnable || loadEnable) {
                    requestLayout();
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (animator != null) {
            animator.removeAllUpdateListeners();
            animator.cancel();
            animator = null;
        }
    }

    public float dpToPx(float dp) {
        return dp * getScreenDensity();
    }

    public float getScreenDensity() {
        return Resources.getSystem().getDisplayMetrics().density;
    }

    public interface OnSwipeRefreshListener {
        void onSwipeRefresh();
    }

    /**
     * 設置刷新監聽
     *
     * @param refreshListener
     */
    public void setOnSwipeRefreshListener(OnSwipeRefreshListener refreshListener) {
        this.refreshListener = refreshListener;
        if (isRefreshing) {
            setRefreshing(isRefreshing);
        }
    }

    public interface OnSwipeLoadListener {
        void onSwipeLoad();
    }

    /**
     * 設置加載監聽
     *
     * @param loadListener
     */
    public void setOnSwipeLoadListener(OnSwipeLoadListener loadListener) {
        this.loadListener = loadListener;
        if (isLoading) {
            setLoading(isLoading);
        }
    }

    /**
     * 設置正在刷新
     *
     * @param isRefreshing 是否開始刷新
     */
    public void setRefreshing(boolean isRefreshing) {
        this.isRefreshing = isRefreshing;
        if (!isRefreshing) {
            createScaleAnimator(headerView).start();
        } else {
            refreshRemainY = headerHeight;
            headerView.start();
            if (refreshListener != null) {
                refreshListener.onSwipeRefresh();
            }
        }
    }

    /**
     * 設置正在加載
     *
     * @param isLoading 是否開始加載
     */
    public void setLoading(boolean isLoading) {
        this.isLoading = isLoading;
        if (!isLoading) {
            createScaleAnimator(footerView).start();
        } else {
            footerView.start();
            loadRemainY = -footerHeight;
            if (loadListener != null) {
                loadListener.onSwipeLoad();
            }
        }
    }

    /**
     * 創建縮放動畫師
     *
     * @param view 控件
     * @return 動畫師
     */
    private ValueAnimator createScaleAnimator(final View view) {
        isAnimatorRunning = true;
        if (animator != null && animator.isStarted() && animator.isRunning()) {
            animator.removeAllUpdateListeners();
            animator.cancel();
            animator = null;
        }
        animator = ValueAnimator.ofFloat(1, 0);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value = (float) animation.getAnimatedValue();
                view.setScaleX(value);
                view.setScaleY(value);
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                refreshRemainY = 0;
                loadRemainY = 0;
                refreshMoveY = 0;
                loadMoveY = 0;
                requestLayout();
                isRefreshing = false;
                isLoading = false;
                isAnimatorRunning = false;
                refreshEnable = true;
                loadEnable = true;
                headerView.cancel();
                footerView.cancel();
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        animator.setStartDelay(refreshEnable ? delayDuration : 0);
        animator.setDuration(scaleDuration);
        return animator;
    }

    /**
     * 內容去是否可以刷新
     *
     * @return
     */
    private boolean isContentViewRefreshEnable() {
        if (scrollView != null && scrollView.getScrollY() > 0) {
            return false;
        }
        if (absListView != null && absListView.getFirstVisiblePosition() > 0) {
            return false;
        }
        return true;
    }

    /**
     * 內容區域是否可以加載
     *
     * @return
     */
    private boolean isContentViewLoadEnable() {
        if (scrollView != null && scrollView.getScrollY() > 0) {
            return false;
        }
        if (absListView.getLastVisiblePosition() < (absListView.getCount() - 1)) {
            return false;
        }
        return true;
    }

}

SwipeRequestView

(1)attrs.xml

    <declare-styleable name="SwipeRequestView">
        <attr name="circle_radius" format="dimension" />
        <attr name="circle_color" format="color" />
        <attr name="shadow_radius" format="dimension" />
        <attr name="shadow_color" format="color" />
        <attr name="arc_scheme_colors" format="string" />
        <attr name="arc_radius" format="dimension" />
        <attr name="arc_stroke_width" format="dimension" />
        <attr name="arc_gap_angle" format="float" />
        <attr name="duration" format="integer" />
    </declare-styleable>

(2)源碼


import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;

import com.android.R;

/**
 * Created by Relin
 * on 2018-09-26.
 */
public class SwipeRequestView extends View {

    private Paint paint;
    private float centerX, centerY;

    //背景半徑
    private float circleRadius = dpToPx(18);
    //背景顏色
    private int circleColor = Color.WHITE;
    //背景陰影半徑
    private float shadowRadius = dpToPx(5);
    //背景陰影顏色
    private int shadowColor = Color.parseColor("#5F000000");

    //扇形半徑
    private float arcRadius = dpToPx(9);
    //扇形組合顏色
    private int arcSchemeColors[] = {
            Color.parseColor("#4285F4"),
            Color.parseColor("#EA4335"),
            Color.parseColor("#34A853"),
            Color.parseColor("#FBBC05")};
    //扇形初始顏色
    private int arcColor = arcSchemeColors[0];
    //扇形線寬度
    private float arcStrokeWidth = dpToPx(2.5F);
    //弧度缺口大小
    private float arcGapAngle = 30;
    //扇形終點弧度
    private float arcSweepAngle = 360;
    //扇形起始弧度
    private float arcStartAngle = 0;

    //動畫記錄次數
    private int recordCount = 0;
    //動畫重複次數 - 記錄作用
    private int repeatCount = 0;
    //動畫對象
    private ValueAnimator animator;
    //單次動畫時間
    private int duration = 800;
    //動畫狀態
    private boolean isStart;

    public SwipeRequestView(Context context) {
        super(context);
        initAttrs(context, null);
    }

    public SwipeRequestView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initAttrs(context, attrs);
    }

    public SwipeRequestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
    }

    /**
     * 初始化Attrs屬性值
     *
     * @param context
     * @param attrs   屬性值
     */
    private void initAttrs(Context context, AttributeSet attrs) {
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        if (attrs == null) {
            return;
        }
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SwipeRequestView);
        circleRadius = typedArray.getDimension(R.styleable.SwipeRequestView_circle_radius, circleRadius);
        circleColor = typedArray.getColor(R.styleable.SwipeRequestView_circle_color, circleColor);
        shadowRadius = typedArray.getDimension(R.styleable.SwipeRequestView_shadow_radius, shadowRadius);
        shadowColor = typedArray.getColor(R.styleable.SwipeRequestView_shadow_color, shadowColor);
        arcRadius = typedArray.getDimension(R.styleable.SwipeRequestView_arc_radius, arcRadius);
        attrsSchemeColors(typedArray);
        arcStrokeWidth = typedArray.getDimension(R.styleable.SwipeRequestView_arc_stroke_width, arcStrokeWidth);
        arcGapAngle = typedArray.getFloat(R.styleable.SwipeRequestView_arc_gap_angle, arcGapAngle);
        duration = typedArray.getInt(R.styleable.SwipeRequestView_duration, duration);
        typedArray.recycle();
    }

    /**
     * Attrs組合顏色值
     *
     * @param typedArray
     */
    private void attrsSchemeColors(TypedArray typedArray) {
        String schemeColors = typedArray.getString(R.styleable.SwipeRequestView_arc_scheme_colors);
        if (!TextUtils.isEmpty(schemeColors) && schemeColors.contains(",")) {
            String split[] = schemeColors.split(",");
            arcSchemeColors = new int[split.length];
            for (int i = 0; i < split.length; i++) {
                arcSchemeColors[i] = Color.parseColor(split[i]);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int w = widthSpecSize;
        int h = heightSpecSize;
        int needWidth = (int) (arcRadius * 2 + shadowRadius * 2 + getPaddingLeft() + getPaddingRight());
        int needHeight = (int) (arcRadius * 2 + shadowRadius * 2 + getPaddingTop() + getPaddingBottom());
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            w = needWidth;
            h = needHeight;
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {//wrap-content
            w = needWidth;
            h = heightSpecSize;
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            w = widthSpecSize;
            h = needHeight;
        }
        setMeasuredDimension(w, h);
        centerY = getMeasuredHeight() / 2;
        centerX = getMeasuredWidth() / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawLoadingBackground(canvas);
        drawLoadingArc(canvas, arcColor, arcStartAngle, arcSweepAngle);
        startAnimation(360 - arcGapAngle);
    }

    /**
     * 繪製背景
     *
     * @param canvas
     */
    private void drawLoadingBackground(Canvas canvas) {
        paint = createPaint(circleColor, Paint.Style.FILL, 0);
        paint.setShadowLayer(shadowRadius, 0, 0, shadowColor);
        canvas.drawCircle(centerX, centerY, circleRadius, paint);
    }

    /**
     * 繪製弧線
     *
     * @param canvas     畫布
     * @param arcColor   弧線顏色
     * @param startAngle 開始弧度
     * @param sweepAngle 掃描弧度
     */
    private void drawLoadingArc(Canvas canvas, int arcColor, float startAngle, float sweepAngle) {
        paint = createPaint(arcColor, Paint.Style.STROKE, arcStrokeWidth);
        RectF oval = new RectF(centerX - arcRadius, centerY - arcRadius, centerX + arcRadius, centerY + arcRadius);
        canvas.drawArc(oval, startAngle, sweepAngle, false, paint);
    }

    /**
     * 開始動畫
     *
     * @param sweepAngle 掃描弧度
     */
    private void startAnimation(float sweepAngle) {
        if (!isStart) {
            return;
        }
        if (animator != null) {
            return;
        }
        animator = ValueAnimator.ofFloat(sweepAngle);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int mod = repeatCount % arcSchemeColors.length;
                arcColor = arcSchemeColors[mod];
                float animatedValue = (float) animation.getAnimatedValue();
                arcSweepAngle = animatedValue;
                arcStartAngle = arcSweepAngle;
                if (repeatCount - recordCount > 0) {
                    arcStartAngle += arcGapAngle;
                }
                postInvalidate();
                recordCount = repeatCount;
            }
        });
        animator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {

            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                repeatCount++;
            }
        });
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(Animation.INFINITE);
        animator.setDuration(duration);
        animator.start();
    }

    /**
     * 創建畫筆
     *
     * @param color       顏色
     * @param style       樣式
     * @param strokeWidth 寬度
     * @return
     */
    private Paint createPaint(int color, Paint.Style style, float strokeWidth) {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(style);
        paint.setStrokeWidth(strokeWidth);
        paint.setColor(color);
        return paint;
    }

    public float dpToPx(float dp) {
        return dp * getScreenDensity();
    }

    public float getScreenDensity() {
        return Resources.getSystem().getDisplayMetrics().density;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (animator != null) {
            animator.cancel();
            animator = null;
        }
    }

    /**
     * 開啓動畫
     */
    public void start() {
        this.isStart = true;
        postInvalidate();
    }

    /**
     * 取消動畫
     */
    public void cancel() {
        this.isStart = false;
        if (animator != null) {
            animator.removeAllUpdateListeners();
            animator.cancel();
            animator = null;
        }
        postInvalidate();
    }

    public float getCircleRadius() {
        return circleRadius;
    }

    public void setCircleRadius(float circleRadius) {
        this.circleRadius = circleRadius;
    }

    public int getCircleColor() {
        return circleColor;
    }

    public void setCircleColor(int circleColor) {
        this.circleColor = circleColor;
    }

    public float getShadowRadius() {
        return shadowRadius;
    }

    public void setShadowRadius(float shadowRadius) {
        this.shadowRadius = shadowRadius;
    }

    public int getShadowColor() {
        return shadowColor;
    }

    public void setShadowColor(int shadowColor) {
        this.shadowColor = shadowColor;
    }

    public float getArcRadius() {
        return arcRadius;
    }

    public void setArcRadius(float arcRadius) {
        this.arcRadius = arcRadius;
    }

    public int[] getArcSchemeColors() {
        return arcSchemeColors;
    }

    public void setArcSchemeColors(int[] arcSchemeColors) {
        this.arcSchemeColors = arcSchemeColors;
    }

    public int getArcColor() {
        return arcColor;
    }

    public void setArcColor(int arcColor) {
        this.arcColor = arcColor;
    }

    public float getArcStrokeWidth() {
        return arcStrokeWidth;
    }

    public void setArcStrokeWidth(float arcStrokeWidth) {
        this.arcStrokeWidth = arcStrokeWidth;
    }

    public float getArcGapAngle() {
        return arcGapAngle;
    }

    public void setArcGapAngle(float arcGapAngle) {
        this.arcGapAngle = arcGapAngle;
    }

    public float getArcSweepAngle() {
        return arcSweepAngle;
    }

    public void setArcSweepAngle(float arcSweepAngle) {
        this.arcSweepAngle = arcSweepAngle;
    }

    public float getArcStartAngle() {
        return arcStartAngle;
    }

    public void setArcStartAngle(float arcStartAngle) {
        this.arcStartAngle = arcStartAngle;
    }

    public int getDuration() {
        return duration;
    }

    public void setDuration(int duration) {
        this.duration = duration;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章