Android BrushRequestLayout

效果

一個仿寫QQ的刷新控件,只是樣子不跟QQ的一樣但是基本功能是一致的,同樣具有刷新功能和加載更多的功能。
在這裏插入圖片描述

使用

類文件 apk
下載 下載

下載類文件之後有如下文件:
在這裏插入圖片描述

更新

BUG-1 ==》在ScrollView滑動問題:
對下載的BrushRequestLayout.java類中isContentViewLoadEnable()方法進行替換如下:

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

複製對應的圖片和佈局還有attrs到自己項目,還有主要的BrushRequestLayout.java類,包名修改爲自己項目的包名即可。

(1)xml使用

    <com.android.widget.BrushRequestLayout
        android:id="@+id/prl"
        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"
            android:background="@color/colorPrimary"></ListView>
    </com.android.widget.BrushRequestLayout>

(2)attrs.xml屬性值

    <declare-styleable name="BrushRequestLayout">
		<attr name="headerHeight" format="dimension" />
    	<attr name="footerHeight" format="dimension" />
    	<attr name="refreshEnable" format="boolean" />
   	 	<attr name="loadEnable" format="boolean" />
    	<attr name="duration" format="integer" />
    </declare-styleable>

(3)自動刷新

	 BrushRequestLayout prl = (BrushRequestLayout) findViewById(R.id.prl);
	 prl.setRefreshing(true);

(4)自動加載

        BrushRequestLayout prl = (BrushRequestLayout) findViewById(R.id.prl);
        prl.setLoading(true);

(5)刷新監聽

 	  BrushRequestLayout prl = (BrushRequestLayout) findViewById(R.id.prl);
      prl.setOnRefreshListener(new BrushRequestLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                prl.setRefreshing(false);
            }
        });

(6)加載監聽

 	BrushRequestLayout prl = (BrushRequestLayout) findViewById(R.id.prl);
 	prl.setOnLoadListener(new BrushRequestLayout.OnLoadListener() {
            @Override
            public void onLoad() {
                prl.setLoading(false);
            }
        });

源碼

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.AnimationDrawable;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
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.widget.AbsListView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;

import com.android.R;

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

    //開始
    public final int BRUSH_START = 1;
    //過界
    public final int BRUSH_BOUND = 2;
    //停留
    public final int BRUSH_REMAIN = 3;
    //完成
    public final int BRUSH_COMPLETE = 4;
    //恢復
    public final int BRUSH_RECOVERY = 0;
    //佈局填充器
    private LayoutInflater inflater;
    //頭部
    private View headerView;
    private BrushHeader header;
    //腳部
    private View footerView;
    private BrushFooter footer;
    //頭部高度
    private float headerHeight = dpToPx(50);
    //腳部高度
    private float footerHeight = dpToPx(50);
    //內容視圖
    private View contentView[];
    //內容類型-列表
    private AbsListView absListView;
    //內容類型-ScrollView
    private ScrollView scrollView;
    //按下的座標
    private float downX, downY;
    //刷新移動距離
    private float refreshMoveY = 0;
    //加載移動距離
    private float loadMoveY = 0;
    //刷新停留距離
    private float refreshRemainY = 0;
    //加載停留距離
    private float loadRemainY = 0;
    //刷新是否可用
    private boolean refreshEnable = true;
    //加載是否可用
    private boolean loadEnable = true;
    //是否正在刷新
    private boolean isRefreshing;
    //是否正在加載
    private boolean isLoading;
    //刷新監聽
    private OnRefreshListener refreshListener;
    //加載監聽
    private OnLoadListener loadListener;
    //延時器
    private TimerHandler timerHandler;
    //頭部背景
    private int headerBackgroundColor = Color.parseColor("#EDEDEE");
    //頭部背景
    private int footerBackgroundColor = Color.parseColor("#EDEDEE");
    //停留時間
    private int duration = 600;
    //幀動畫
    private AnimationDrawable animationDrawable;
    private float movePercent;

    public BrushRequestLayout(@NonNull Context context) {
        super(context);
        initAttrs(context, null);
    }

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

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

    private void initAttrs(Context context, AttributeSet attrs) {
        timerHandler = new TimerHandler();
        inflater = LayoutInflater.from(context);
        header = new BrushHeader().findViews();
        footer = new BrushFooter().findViews();

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BrushRequestLayout);
        headerHeight = typedArray.getDimension(R.styleable.BrushRequestLayout_headerHeight, headerHeight);
        footerHeight = typedArray.getDimension(R.styleable.BrushRequestLayout_footerHeight, footerHeight);
        refreshEnable = typedArray.getBoolean(R.styleable.BrushRequestLayout_refreshEnable, refreshEnable);
        loadEnable = typedArray.getBoolean(R.styleable.BrushRequestLayout_refreshEnable, loadEnable);
        duration = typedArray.getInt(R.styleable.BrushRequestLayout_duration, duration);
    }

    @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.CYAN);
        headerView.setBackgroundColor(headerBackgroundColor);
        addView(headerView);
        //腳部
        LayoutParams footerParams = new LayoutParams(LayoutParams.MATCH_PARENT, (int) footerHeight);
        footerParams.gravity = Gravity.BOTTOM;
        footerView.setLayoutParams(footerParams);
        footerView.setBackgroundColor(footerBackgroundColor);
        addView(footerView);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        headerView.getLayoutParams().height = ((int) refreshRemainY + (int) refreshMoveY);
        footerView.getLayoutParams().height = Math.abs((int) loadRemainY + (int) loadMoveY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.e("Life", "onMeasure");
    }

    @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 + (int) refreshMoveY + (int) refreshRemainY + (int) loadRemainY + (int) loadMoveY;
            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);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                if (isRefreshing) {
                    //刷新可用就停留,不可用就不停留
                    if (refreshListener != null && refreshMoveY > 0 && refreshEnable) {
                        refreshListener.onRefresh();
                    }
                    if (refreshEnable) {
                        refreshRemainY = headerHeight;
                        refreshMoveY = 0;
                        isRefreshing = true;
                        showBrushViewState(BRUSH_REMAIN, true, false);
                        requestLayout();
                    } else {
                        refreshMoveY = 0;
                        refreshRemainY = 0;
                        showBrushViewState(BRUSH_RECOVERY, true, false);
                        requestLayout();
                    }
                    return true;
                }
                if (isLoading) {
                    loadRemainY = loadEnable ? -footerView.getMeasuredHeight() : 0;
                    if (loadListener != null && loadMoveY < 0 && loadEnable) {
                        loadListener.onLoad();
                    }
                    if (loadEnable) {
                        loadRemainY = -footerHeight;
                        loadMoveY = 0;
                        isLoading = true;
                        showBrushViewState(BRUSH_REMAIN, false, true);
                        requestLayout();
                    } else {
                        loadMoveY = 0;
                        loadRemainY = 0;
                        showBrushViewState(BRUSH_RECOVERY, false, true);
                        requestLayout();
                    }
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float moveY = event.getY() - downY;
                //正在刷新不能上拉加載,正在加載不能下拉刷新
                if (isRefreshing && moveY < 0 || isLoading && moveY > 0) {
                    break;
                }
                //利用屏幕的1/5做界限
                int each = 5;
                movePercent = (moveY) / (getMeasuredHeight() / each);
                if (Math.abs(movePercent) > each) {
                    break;
                }
                if (moveY > 0) {//下滑
                    headerView.setScaleX(1);
                    headerView.setScaleY(1);
                    refreshEnable = Math.abs(movePercent) >= 1;
                    refreshMoveY = moveY;
                    isRefreshing = true;
                    isLoading = false;
                    showBrushViewState(movePercent >= 1 ? BRUSH_BOUND : BRUSH_START, true, false);
                } else {//上滑
                    footerView.setScaleX(1);
                    footerView.setScaleY(1);
                    loadEnable = Math.abs(movePercent) >= 1;
                    loadMoveY = moveY;
                    isRefreshing = false;
                    isLoading = true;
                    showBrushViewState(Math.abs(movePercent) >= 1 ? BRUSH_BOUND : BRUSH_START, false, true);
                }
                if (refreshEnable || loadEnable) {
                    requestLayout();
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (timerHandler != null) {
            timerHandler.removeCallbacksAndMessages(null);
        }
        animationDrawable.stop();
        animationDrawable = null;
    }

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

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

    public interface OnRefreshListener {
        void onRefresh();
    }

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

    public interface OnLoadListener {
        void onLoad();
    }

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

    /**
     * 設置正在刷新
     *
     * @param isRefreshing 是否開始刷新
     */
    public void setRefreshing(boolean isRefreshing) {
        this.isRefreshing = isRefreshing;
        if (!isRefreshing) {
            timerHandler.sendEmptyMessageDelayed(BRUSH_COMPLETE, duration);
        } else {
            refreshRemainY = headerHeight;
            if (refreshListener != null) {
                refreshListener.onRefresh();
            }
        }
    }

    /**
     * 設置正在加載
     *
     * @param isLoading 是否開始加載
     */
    public void setLoading(boolean isLoading) {
        this.isLoading = isLoading;
        if (!isLoading) {
            timerHandler.sendEmptyMessageDelayed(BRUSH_COMPLETE, 500);
        } else {
            loadRemainY = -footerHeight;
            if (loadListener != null) {
                loadListener.onLoad();
            }
        }
    }

    /**
     * 顯示狀態視圖
     *
     * @param state     狀態
     * @param isRefresh 是否是下拉刷新
     * @param isLoad    是否是上拉加載
     */
    private void showBrushViewState(int state, boolean isRefresh, boolean isLoad) {
        switch (state) {
            case BRUSH_START:
                if (isRefresh) {
                    header.iv_refresh_arrow.setRotation(360F);
                    header.tv_refresh_state.setText("下拉刷新");
                }
                if (isLoad) {
                    footer.iv_load_arrow.setRotation(360F);
                    footer.tv_load_state.setText("上拉加載");
                }
                break;
            case BRUSH_BOUND:
                if (isRefresh) {
                    header.iv_refresh_arrow.setRotation(180F);
                    header.tv_refresh_state.setText("釋放刷新");
                }
                if (isLoad) {
                    footer.iv_load_arrow.setRotation(180F);
                    footer.tv_load_state.setText("釋放加載");
                }
                break;
            case BRUSH_REMAIN:
                if (isRefresh) {
                    header.iv_refresh_arrow.setVisibility(GONE);
                    header.iv_refresh_state.setVisibility(GONE);
                    header.iv_refresh_loading.setVisibility(VISIBLE);
                    animationDrawable = (AnimationDrawable) header.iv_refresh_loading.getBackground();
                    animationDrawable.start();
                    header.tv_refresh_state.setText("刷新數據");
                }
                if (isLoad) {
                    footer.iv_load_arrow.setVisibility(GONE);
                    footer.iv_load_state.setVisibility(GONE);
                    footer.iv_load_loading.setVisibility(VISIBLE);
                    AnimationDrawable animationDrawable = (AnimationDrawable) footer.iv_load_loading.getBackground();
                    animationDrawable.start();
                    footer.tv_load_state.setText("刷新數據");
                }
                break;
            case BRUSH_COMPLETE:
                if (isRefresh) {
                    header.iv_refresh_arrow.setVisibility(GONE);
                    header.iv_refresh_state.setVisibility(VISIBLE);
                    header.iv_refresh_loading.setVisibility(GONE);
                    header.tv_refresh_state.setText("刷新成功");
                }
                if (isLoad) {
                    footer.iv_load_arrow.setVisibility(GONE);
                    footer.iv_load_state.setVisibility(VISIBLE);
                    footer.iv_load_loading.setVisibility(GONE);
                    header.tv_refresh_state.setText("加載成功");
                }
                break;
            case BRUSH_RECOVERY:
                refreshRemainY = 0;
                loadRemainY = 0;
                refreshMoveY = 0;
                loadMoveY = 0;
                isRefreshing = false;
                isLoading = false;
                refreshEnable = true;
                loadEnable = true;
                if (isRefresh) {
                    header.iv_refresh_arrow.setVisibility(VISIBLE);
                    header.iv_refresh_loading.setVisibility(GONE);
                    header.iv_refresh_state.setVisibility(GONE);
                }
                if (isLoad) {
                    footer.iv_load_arrow.setVisibility(VISIBLE);
                    footer.iv_load_state.setVisibility(GONE);
                    footer.iv_load_loading.setVisibility(GONE);
                }
                break;
        }
        requestLayout();
    }

    /**
     * 內容去是否可以刷新
     *
     * @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;
    }

    class BrushHeader {

        ImageView iv_refresh_arrow;
        ImageView iv_refresh_loading;
        ImageView iv_refresh_state;
        TextView tv_refresh_state;

        public BrushHeader() {
            headerView = inflater.inflate(R.layout.android_brush_header, null);
        }

        public BrushHeader findViews() {
            iv_refresh_arrow = headerView.findViewById(R.id.iv_refresh_arrow);
            iv_refresh_loading = headerView.findViewById(R.id.iv_refresh_loading);
            iv_refresh_state = headerView.findViewById(R.id.iv_refresh_state);
            tv_refresh_state = headerView.findViewById(R.id.tv_refresh_state);
            return this;
        }

        public View view() {
            return headerView;
        }

    }

    class BrushFooter {

        ImageView iv_load_arrow;
        ImageView iv_load_loading;
        ImageView iv_load_state;
        TextView tv_load_state;

        public BrushFooter() {
            footerView = inflater.inflate(R.layout.android_brush_footer, null);
        }

        public BrushFooter findViews() {
            iv_load_arrow = footerView.findViewById(R.id.iv_load_arrow);
            iv_load_loading = footerView.findViewById(R.id.iv_load_loading);
            iv_load_state = footerView.findViewById(R.id.iv_load_state);
            tv_load_state = footerView.findViewById(R.id.tv_load_state);
            return this;
        }

        public View view() {
            return footerView;
        }

    }

    class TimerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case BRUSH_COMPLETE:
                    if (isRefreshing) {
                        showBrushViewState(BRUSH_COMPLETE, true, false);
                    }
                    if (isLoading) {
                        showBrushViewState(BRUSH_COMPLETE, false, true);
                    }
                    timerHandler.sendEmptyMessageDelayed(BRUSH_RECOVERY, duration);
                    break;
                case BRUSH_RECOVERY:
                    if (isRefreshing) {
                        showBrushViewState(BRUSH_RECOVERY, true, false);
                    }
                    if (isLoading) {
                        showBrushViewState(BRUSH_RECOVERY, false, true);
                    }
                    break;
            }
        }
    }


    public void setHeaderHeight(float headerHeight) {
        this.headerHeight = dpToPx(headerHeight);
    }

    public void setFooterHeight(float footerHeight) {
        this.footerHeight = dpToPx(footerHeight);
    }

    public void setRefreshEnable(boolean refreshEnable) {
        this.refreshEnable = refreshEnable;
    }

    public void setLoadEnable(boolean loadEnable) {
        this.loadEnable = loadEnable;
    }

    public void setHeaderBackgroundColor(int headerBackgroundColor) {
        this.headerBackgroundColor = headerBackgroundColor;
    }

    public void setFooterBackgroundColor(int footerBackgroundColor) {
        this.footerBackgroundColor = footerBackgroundColor;
    }

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

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