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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...