listview reclyerview上下拉刷新

来源:http://www.cnblogs.com/LiuZhen/archive/2017/04/06/6656949.html
-Advertisement-
Play Games

x寫控制項挺麻煩的,因為有很多細節要處理好,列表控制項使用太頻繁了,網上也各種自定義的方法,一般的listview自定義肯定會聯想到加個頭部,然後監聽事件加動畫,其實方式很多種,今天記錄的方式是另外一種方式,個人覺得復用性更強,寫好了可以通用,思路就是在不動原列表控制項的情況下給它上面套個殼,然後讓殼來操 ...


x寫控制項挺麻煩的,因為有很多細節要處理好,列表控制項使用太頻繁了,網上也各種自定義的方法,一般的listview自定義肯定會聯想到加個頭部,然後監聽事件加動畫,其實方式很多種,今天記錄的方式是另外一種方式,個人覺得復用性更強,寫好了可以通用,思路就是在不動原列表控制項的情況下給它上面套個殼,然後讓殼來操作刷新顯示,這樣的話是不是以後要用的時候加個殼就行了,而且我可以基本上不管裡面的控制項是什麼。

 

下載地址: http://download.csdn.net/detail/u010864175/9805339

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="cn.com.listwebview.demo.MainActivity">

    <cn.com.listwebview.demo.ListRefreshLayout
        android:id="@+id/refreshLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#f4a148"
        android:orientation="vertical" >

        <ListView android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#dff19a"
            android:divider="#595958"/>
        <!--<android.support.v7.widget.RecyclerView-->
            <!--android:id="@+id/recyclerview"-->
            <!--android:divider="#ffff0000"-->
            <!--android:dividerHeight="10dp"-->
            <!--android:background="#dff19a"-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="match_parent" />-->

    </cn.com.listwebview.demo.ListRefreshLayout>

</LinearLayout>
View Code

 

MainActivity很簡單,啥都不用做,按著 RecyclerView或者listview載入數據的方式載入就好了

MainActivity

重點都在這個殼裡了,註釋非常詳細,因為殼是父容器,所以不用傳遞視圖進去,這樣也會產生依賴關係,界面開始顯示listview或者recyclerview列表,監聽時間滑動,然後判斷滑動的程度,距離,
是否在刷新中等等,接著通過更新MarginTop來展示頭部的刷新,而頭部都是在載入事件裡面動態添加進去的,因為效果都是在殼裡實現的,所以不影響列表控制項添加頭部腳部,以後也可以更換配置頭部樣
式,這樣xml里也不用特地的為這個控制項去寫特定的佈局,不然這種做法就失去了意義了,和直接添加頭部也沒什麼大區別了,顯示出了頭部根據你滑動的狀態來控制動畫,然後已經運行了動畫就不能讓它
在更新top了,不然重覆刷新,接著抬起操作,此處我直接設置的兩秒完成,實際是訪問回調介面停止的。

ListRefreshLayout
package cn.com.listwebview.demo;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.webkit.WebView;
import android.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;

import java.lang.reflect.Field;

import cn.com.listwebview.demo.utils.DensityUtil;

/**
 * Created by LiuZhen on 2017/3/24.
 */
public class ListRefreshLayout extends FrameLayout{

    private String TAG = "TwinklingRefreshLayout";
    private int downY;// 按下時y軸的偏移量
    private final static float RATIO = 3f;
    //頭部的高度
    protected int mHeadHeight;
    //頭部layout
    protected FrameLayout mHeadLayout;//頭部父容器
    private HeaderView mHeadView;//頭部
    protected FrameLayout mFootLayout;//頭部父容器
    private ImageView ivArrow_left,ivArrow_right; //頭佈局的剪頭
    private ProgressBar mProgressBar; // 底佈局的進度條
    private Animation upAnimation;// 向上旋轉的動畫
    private Animation downAnimation;// 向下旋轉的動畫

    private final int DOWN_PULL_REFRESH = 0;// 下拉刷新狀態
    private final int RELEASE_REFRESH = 1;// 鬆開刷新
    private final int REFRESHING = 2;// 正在刷新中
    private final int END = 3;// 正在刷新中
    private int currentState = DOWN_PULL_REFRESH;// 頭佈局的狀態: 預設為下拉刷新狀態
    private View list;//子節點中的listview視圖
    private LayoutParams listParam,footParam;//用於控制下拉動畫展示
    private boolean isLoadingMore = false;// 是否進入載入狀態,防止多次重覆的啟動
    private boolean isStart = false;//表示正在載入刷新中,還沒停止
    private boolean isTop = false,isBottom = false;
    private int mTouchSlop;

    public ListRefreshLayout(Context context) {
        this(context, null, 0);
    }

    public ListRefreshLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ListRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TwinklingRefreshLayout, defStyleAttr, 0);
        try {

            mHeadHeight = a.getDimensionPixelSize(R.styleable.TwinklingRefreshLayout_tr_head_height, DensityUtil.dp2px(context, 40));
        } finally {
            a.recycle();
        }
        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        addHeader();
        init();
    }

    private void addHeader() {
        FrameLayout headViewLayout = new FrameLayout(getContext());
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,mHeadHeight);
        this.addView(headViewLayout,layoutParams);

        mHeadLayout = headViewLayout;
    }

    private void init(){
        initAnimation();
    }

    @Override
    protected void onFinishInflate() {//佈局載入成xml時觸發
        super.onFinishInflate();

        if (mHeadView == null) setHeaderView(new HeaderView(getContext()));
        setFootView();
        if (list == null) {
            list = getChildAt(1);
            listParam = (LayoutParams) list.getLayoutParams();
            list.setOnTouchListener(new OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    return onFingerTouch(event);
                }
            });
            //下麵是原先的方法,比較簡單,也可以達到同樣的效果,思路也是需要判斷類型,畢竟每個列表控制項的判斷top bottom方式不同,只是此處需要用具體的list對象來監聽,所以採用另外的方式來判斷
//            list.setOnScrollListener(new AbsListView.OnScrollListener() {
//                @Override
//                public void onScrollStateChanged(AbsListView view, int scrollState) {
//
//                }
//
//                @Override
//                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//                    isTop = false;
//                    isBottom = false;
//                    //判斷頂部底部
//                    if (firstVisibleItem == 0) {
//                        Log.d(TAG, "滾動到頂部");
//                        isTop = true;
//                        isBottom = false;
//                    } else if ((firstVisibleItem + visibleItemCount) == totalItemCount) {
//                        Log.d(TAG, "滾動到底部");
//                        isTop = false;
//                        isBottom = true;
//                    }
//                }
//            });
        }
    }

    /**
     * 設置頭部View
     */
    public void setHeaderView(final HeaderView headerView) {
        if (headerView != null) {
            post(new Runnable() {
                @Override
                public void run() {
                    mHeadLayout.removeAllViewsInLayout();
                    mHeadLayout.addView(headerView.getView());

                    View view = LayoutInflater.from(getContext()).inflate(R.layout.item_head_progress,null);
//                    mProgressBar = (ProgressBar) view.findViewById(R.id.pb_listview_header);
                    ivArrow_left = (ImageView) view.findViewById(R.id.iv_listview_header_arrow_left) ;
                    ivArrow_right = (ImageView) view.findViewById(R.id.iv_listview_header_arrow_right) ;
                    mHeadLayout.addView(view);
                }
            });
            mHeadView = headerView;
        }
    }

    /**
     * 設置尾部View
     */
    public void setFootView() {
        footParam = new LayoutParams(LayoutParams.MATCH_PARENT,mHeadHeight);
        FrameLayout footViewLayout = new FrameLayout(getContext());//底部佈局
        this.addView(footViewLayout,footParam);
        this.mFootLayout = footViewLayout;
        mFootLayout.setBackgroundColor(Color.BLACK);
        footParam.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
        footParam.setMargins(0,0,0,-mHeadHeight);
        mProgressBar = new ProgressBar(getContext(),null,android.R.attr.progressBarStyleSmallInverse);
        mFootLayout.addView(mProgressBar);
    }


    public boolean onFingerTouch(MotionEvent ev) {
        isTop = isViewToTop(list,mTouchSlop);
        isBottom = isViewToBottom(list,mTouchSlop);
//        Log.e(TAG,"isTop "+isTop+" isBottom "+isBottom);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN :
                currentState = REFRESHING;
                downY = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE :
                if (!isTop && !isBottom)//沒有到頂,無需計算操作
                    break;
                int moveY = (int) ev.getY();
                int diff = (int) (((float)moveY - (float)downY) / RATIO);
//                int paddingTop = -mHeadLayout.getHeight() + diff;
                int paddingTop = diff;
                if (diff>0 && isTop) {
                    //向下滑動多少後開始啟動刷新
                    if (paddingTop >= 200 && currentState == DOWN_PULL_REFRESH) { // 完全顯示了.
//                        Log.i(TAG, "鬆開刷新 RELEASE_REFRESH");
                        currentState = RELEASE_REFRESH;
                        refreshHeaderView();
                        start();
                    } else if (currentState == REFRESHING) { // 沒有顯示完全
//                        Log.i(TAG, "下拉刷新 DOWN_PULL_REFRESH");
                        currentState = DOWN_PULL_REFRESH;
                        refreshHeaderView();
                    }
                    if (paddingTop <= 400 && !isStart) {//已經處於運行刷新狀態的時候禁止設置
                        listParam.setMargins(0, paddingTop, 0, 0);
                        list.setLayoutParams(listParam);
                    }

                }else if (isBottom){
                    //限制上滑時不能超過底部的寬度,不然會超出邊界
                    if (paddingTop <= -50 && paddingTop >= -mHeadHeight && !isStart) {//已經處於運行刷新狀態的時候禁止設置
                        listParam.setMargins(0, 0, 0, -paddingTop);
                        footParam.setMargins(0,0,0,-paddingTop-mHeadHeight);
                        list.setLayoutParams(listParam);
                    }
                    if (paddingTop <= -mHeadHeight)
                        isLoadingMore = true;
                }
//                Log.i(TAG,"paddingTop "+paddingTop);
                break;
            case MotionEvent.ACTION_UP :

                if (isLoadingMore){
                    isLoadingMore = false;
                    postDelayed(new Runnable() {
                        @Override
                        public void run() {
//                            Log.i(TAG, "停止 END");
//                            currentState = END;
                            refreshHeaderView();
                            listParam.setMargins(0, 0, 0, 0);
                            footParam.setMargins(0,0,0,-mHeadHeight);
                            list.setLayoutParams(listParam);
                            stop();
                        }
                    },2000);
                }else{
                    if (!isStart){
                        // 隱藏頭佈局
                        listParam.setMargins(0, 0,0,0);
                        footParam.setMargins(0,0,0,-mHeadHeight);
                        list.setLayoutParams(listParam);
                    }
                }
//                Log.i(TAG, "鬆開 REFRESHING");
                currentState = REFRESHING;
                break;
            default :
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 初始化動畫
     */
    private void initAnimation() {
        /*
         * Animation.RELATIVE_TO_SELF   相對於自身的動畫
         * Animation.RELATIVE_TO_PARENT 相對於父控制項的動畫
         * 0.5f,表示在控制項自身的 x,y的中點坐標處,為動畫的中心。
         *
         * 設置動畫的變化速率
         * setInterpolator(newAccelerateDecelerateInterpolator()):先加速,後減速
         * setInterpolator(newAccelerateInterpolator()):加速
         * setInterpolator(newDecelerateInterpolator()):減速
         * setInterpolator(new CycleInterpolator()):動畫迴圈播放特定次數,速率改變沿著正弦曲線
         * setInterpolator(new LinearInterpolator()):勻速
         */
        upAnimation = new RotateAnimation(0f, -180f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        upAnimation.setInterpolator(new LinearInterpolator());
        upAnimation.setDuration(700);
        upAnimation.setFillAfter(true); // 動畫結束後, 停留在結束的位置上

        downAnimation = new RotateAnimation(-180f, -360f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        downAnimation.setInterpolator(new LinearInterpolator());//這句話可以不寫,預設勻速
        downAnimation.setDuration(700);
        downAnimation.setFillAfter(true); // 動畫結束後, 停留在結束的位置上
    }

    /**
     * 根據currentState刷新頭佈局的狀態
     */
    private void refreshHeaderView() {
        switch (currentState) {
            case DOWN_PULL_REFRESH : // 下拉刷新狀態
//                tvState.setText("下拉刷新");
                ivArrow_left.startAnimation(downAnimation); // 執行向下旋轉
                ivArrow_right.startAnimation(downAnimation); // 執行向下旋轉
                break;
            case RELEASE_REFRESH : // 鬆開刷新狀態
//                tvState.setText("鬆開刷新");
                ivArrow_left.startAnimation(upAnimation);// 執行向上旋轉
                ivArrow_right.startAnimation(upAnimation);// 執行向上旋轉
                break;
            case REFRESHING : // 正在刷新中狀態
                ivArrow_left.clearAnimation();
                ivArrow_right.clearAnimation();
//                tvState.setText("正在刷新中...");
                break;
            default :
                break;
        }
    }

    public static boolean isViewToTop(View view,int mTouchSlop){
        if (view instanceof AbsListView) return isAbsListViewToTop((AbsListView) view);
        if (view instanceof RecyclerView) return isRecyclerViewToTop((RecyclerView) view);
        return  (view != null && Math.abs(view.getScrollY()) <= 2 * mTouchSlop);
    }

    public static boolean isViewToBottom(View view,int mTouchSlop){
        if (view instanceof AbsListView) return isAbsListViewToBottom((AbsListView) view);
        if (view instanceof RecyclerView) return isRecyclerViewToBottom((RecyclerView) view);
//        if (view instanceof WebView) return isWebViewToBottom((WebView) view,mTouchSlop);
//        if (view instanceof ViewGroup) return isViewGroupToBottom((ViewGroup) view);
        return false;
    }

    public static boolean isAbsListViewToTop(AbsListView absListView) {
        if (absListView != null) {
            int firstChildTop = 0;
            if (absListView.getChildCount() > 0) {
                // 如果AdapterView的子控制項數量不為0,獲取第一個子控制項的top
                firstChildTop = absListView.getChildAt(0).getTop() - absListView.getPaddingTop();
            }
            if (absListView.getFirstVisiblePosition() == 0 && firstChildTop == 0) {
                return true;
            }
        }
        return false;
    }

    public static boolean isRecyclerViewToTop(RecyclerView recyclerView) {
        if (recyclerView != null) {
            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            if (manager == null) {
                return true;
            }
            if (manager.getItemCount() == 0) {
                return true;
            }

            if (manager instanceof LinearLayoutManager) {
                LinearLayoutManager layoutManager = (LinearLayoutManager) manager;

                int firstChildTop = 0;
                if (recyclerView.getChildCount() > 0) {
                    // 處理item高度超過一屏幕時的情況
                    View firstVisibleChild = recyclerView.getChildAt(0);
                    if (firstVisibleChild != null && firstVisibleChild.getMeasuredHeight() >= recyclerView.getMeasuredHeight()) {
                        if (android.os.Build.VERSION.SDK_INT < 14) {
                            return !(ViewCompat.canScrollVertically(recyclerView, -1) || recyclerView.getScrollY() > 0);
                        } else {
                            return !ViewCompat.canScrollVertically(recyclerView, -1);
                        }
                    }

                    // 如果RecyclerView的子控制項數量不為0,獲取第一個子控制項的top

                    // 解決item的topMargin不為0時不能觸發下拉刷新
                    View firstChild = recyclerView.getChildAt(0);
                    RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) firstChild.getLayoutParams();
                    firstChildTop = firstChild.getTop() - layoutParams.topMargin - getRecyclerViewItemTopInset(layoutParams) - recyclerView.getPaddingTop();
                }

                if (layoutManager.findFirstCompletelyVisibleItemPosition() < 1 && firstChildTop == 0) {
                    return true;
                }
            } else if (manager instanceof StaggeredGridLayoutManager) {
                StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) manager;

                int[] out = layoutManager.findFirstCompletelyVisibleItemPositions(null);
                if (out[0] < 1) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 通過反射獲取RecyclerView的item的topInset
     *
     * @param layoutParams
     * @return
     */
    private static int getRecyclerViewItemTopInset(RecyclerView.LayoutParams layoutParams) {
        try {
            Field field = RecyclerView.LayoutParams.class.getDeclaredField("mDecorInsets");
            field.setAccessible(true);
            // 開發者自定義的滾動監聽器
            Rect decorInsets = (Rect) field.get(layoutParams);
            return decorInsets.top;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static boolean isAbsListViewToBottom(AbsListView absListView) {
        if (absListView != null && absListView.getAdapter() != null && absListView.getChildCount() > 0 && absListView.getLastVisiblePosition() == absListView.getAdapter().getCount() - 1) {
            View lastChild = absListView.getChildAt(absListView.getChildCount() - 1);

            return lastChild.getBottom() <= absListView.getMeasuredHeight();
        }
        return false;
    }

    public static boolean isRecyclerViewToBottom(RecyclerView recyclerView) {
        if (recyclerView != null) {
            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            if (manager == null || manager.getItemCount() == 0) {
                return false;
            }

            if (manager instanceof LinearLayoutManager) {
                // 處理item高度超過一屏幕時的情況
                View lastVisibleChild = recyclerView.getChildAt(recyclerView.getChildCount() - 1);
                if (lastVisibleChild != null && lastVisibleChild.getMeasuredHeight() >= recyclerView.getMeasuredHeight()) {
                    if (android.os.Build.VERSION.SDK_INT < 14) {
                        return !(ViewCompat.canScrollVertically(recyclerView, 1) || recyclerView.getScrollY() < 0);
                    } else {
                        return !ViewCompat.canScrollVertically(recyclerView, 1);
                    }
                }

                LinearLayoutManager layoutManager = (LinearLayoutManager) manager;
                if (layoutManager.findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) {
                    return true;
                }
            } else if (manager instanceof StaggeredGridLayoutManager) {
                StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager) manager;

                int[] out = layoutManager.findLastCompletelyVisibleItemPositions(null);
                int lastPosition = layoutManager.getItemCount() - 1;
                for (int position : out) {
                    if (position == lastPosition) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public void start(){
        isLoadingMore = true;
        isStart = true;
        mHeadView.onPullingDown(0);
        mHeadView.startAnim();
    }

    public void stop(){
        isLoadingMore = false;
        isStart = false;
        mHeadView.reset();
    }

}

 

有了頭部還得要漂亮的動畫啊,增強體驗,動畫就隨自己配置了,本身控制項的動畫就是兩個箭頭我這裡是加了個自定義的視圖來做動畫,繪製了幾個圓圈,可以根據自己的需要調製,畢竟頭部是一個小的容器佈局,展示的頭部也都是在這個佈局裡面,所以可以任意搭配。

底部就簡單的以同樣的思路來做的了,同樣是通過滑動上拉來展示刷新的底部和啟動動畫(個人覺得滑動後載入體驗好點,不太喜歡直接滑到頂就顯示載入了),底部實現簡單了點,就添加了一個底部的佈局(這裡添加是載入好xml佈局後在添加的,因為是動態佈局會添加在佈局上面),然後裡面加了個簡單的progress。

 

HeaderView
package cn.com.listwebview.demo;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.DecelerateInterpolator;

import cn.com.listwebview.demo.utils.DensityUtil;

/**
 * Created by LiuZhen on 2017/3/28.
 */
public class HeaderView extends View {

    private Paint mPath;
    ValueAnimator animator1, animator2;
    private float r;
    float fraction1;
    float fraction2;
    boolean animating = false;
    private int num = 5;
    private int cir_x = 0;

    public HeaderView(Context context) {
        this(context, null, 0);
    }

    public HeaderView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

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

    private void init() {
        r = DensityUtil.dp2px(getContext(), 6);

        mPath = new Paint();
        mPath.setAntiAlias(true);
        mPath.setColor(Color.rgb(114, 114, 114));

        animator1 = ValueAnimator.ofFloat(1f, 1.2f, 1f, 0.8f);//從左到右過渡
        animator1.setDuration(800);
        animator1.setInterpolator(new DecelerateInterpolator());//DecelerateInterpolator減速插補器
        animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction1 = (float) animation.getAnimatedValue();//監聽動畫運動值
                invalidate();
            }
        });
        animator1.setRepeatCount(ValueAnimator.INFINITE);//設置重覆次數為無限次
        animator1.setRepeatMode(ValueAnimator.REVERSE);//RESTART是直接重新播放

        animator2 = ValueAnimator.ofFloat(1f, 0.8f, 1f, 1.2f);
        animator2.setDuration(800);
        animator2.setInterpolator(new DecelerateInterpolator());
        animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction2 = (float) animation.getAnimatedValue();
            }
        });
        animator2.setRepeatCount(ValueAnimator.INFINITE);
        animator2.setRepeatMode(ValueAnimator.REVERSE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int w = getMeasuredWidth() / num - 10;
        for (int i = 0; i < num; i++) {
            if (animating) {
                switch (i) {
                    case 0:
                        mPath.setAlpha(105);
                        mPath.setColor(getResources().getColor(R.color.Yellow));
                        canvas.drawCircle(getMeasuredWidth() / 2 - cir_x * 2 - 2 * w / 3 * 2, getMeasuredHeight() / 2, r * fraction2, mPath);
                        break;
                    case 1:
                        mPath.setAlpha(145);
                        mPath.setColor(getResources().getColor(R.color.Green));
                        canvas.drawCircle(getMeasuredWidth() / 2 - cir_x * 1 - w / 3 * 2, getMeasuredHeight() / 2, r * fraction2, mPath);
                        break;
                    case 2:
                        mPath.setAlpha(255);
                        mPath.setColor(getResources().getColor(R.color.Blue));
                        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, r * fraction1, mPath);
                        break;
                    case 3:
                        mPath.setAlpha(145);
                        mPath.setColor(getResources().getColor(R.color.Orange));
                        canvas.drawCircle(getMeasuredWidth() / 2 + cir_x * 1 + w / 3 * 2, getMeasuredHeight() / 2, r * fraction2, mPath);
                        break;
                    case 4:
                        mPath.setAlpha(105);
                        mPath.setColor(getResources().getColor(R.color.Yellow));
                        canvas.drawCircle(getMeasuredWidth() / 2 + cir_x * 2 + 2 * w / 3 * 2, getMeasuredHeight() / 2, r * fraction2, mPath);
                        break;
                }
            } else {
                switch (i) {
                    case 0:
                        mPath.setAlpha(105);
                        mPath.setColor(getResources().getColor(R.color.Yellow));
                        canvas.drawCircle(getMeasuredWidth() / 2 - cir_x * 2 - 2 * w / 3 * 2, getMeasuredHeight() / 2, r, mPath);
                        break;
                    case 1:
                        mPath.setAlpha(145);
                        mPath.setColor(getResources().getColor(R.color.Green));
                        canvas.drawCircle(getMeasuredWidth() / 2 - cir_x * 1 - w / 3 * 2, getMeasur

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • vue-cli 用vue-cli來搭建vue項目 第一步:全局安裝vue-cli 第二步:初始化一個項目 第三步:把文件切換到初始化的項目中 第四步:安裝依賴 當然也可以用淘寶鏡像cnpm來安裝,這樣快很多 第五步:運行 完成一個初始化vue項目 webpack 要開始一個vue項目, 那也必須要了 ...
  • iOS10.3新增了可以讓開發者去更改app的icon,接下來看看怎麼更改。 ...
  • 0x00 私有組件淺談 android應用中,如果某個組件對外導出,那麼這個組件就是一個攻擊面。很有可能就存在很多問題,因為攻擊者可以以各種方式對該組件進行測試攻擊。但是開發者不一定所有的安全問題都能考慮全面。 ​ 對於這樣的問題,最方便的修複方式就是在確定不影響業務的情況下,將這個存在問題的組件不 ...
  • 項目在AppStore的展示信息一、首部1.圖標作用:一個軟體的logo。修改:每次提交新版本時可以修改。要求:1》1024*1024像素 2》72dpi、RGB、平展、不透明、沒有圓角 3》高品質的JPEG或PNG圖像文件格式。2.項目名稱作用:顯示在App Store中的App本地化名稱。修改: ...
  • 前言: 系統的學習下Android開發中涉及到的一些專業名詞 和Android開發工具 名詞: 一、SDK(Software Development Kit) 軟體開發工具包:一般都是一些軟體工程師為特定的軟體包、軟體框架、硬體平臺、操作系統等建立應用軟體時的開發工具的集合。 二、API(Appli ...
  • 由OpenDigg 出品的iOS開源項目周報第十五期來啦。我們的iOS開源周報集合了OpenDigg一周來新收錄的優質的iOS開源項目,方便iOS開發人員便捷的找到自己需要的項目工具等。 ...
  • 該項目主要介紹了二維碼掃描、閃光燈開啟、本地二維碼圖片識別、二維碼生成。分別是zxing和zbar(網格二維碼)分別實現,具體效果運行項目apk... ...
  • 在安卓下實現RTSP流的視頻的播放應該怎麼實現呢? 最近找幾個方法來實現它; 視頻的網路鏈接:rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov 都是需要網路許可權的 1.第一個可以用Android原生的VideoView來實現 佈局文件: 2. ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...