com.steve.wanqureader.presentation.ui.CanRefreshLayout.java Source code

Java tutorial

Introduction

Here is the source code for com.steve.wanqureader.presentation.ui.CanRefreshLayout.java

Source

package com.steve.wanqureader.presentation.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.FloatRange;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.Scroller;

import com.steve.wanqureader.R;

/**
 * Copyright 2016 canyinghao
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
public class CanRefreshLayout extends ViewGroup {

    public static String TAG = CanRefreshLayout.class.getSimpleName();

    //  
    private static final int DEFAULT_DURATION = 300;

    //  
    private static final int DEFAULT_AUTO_DURATION = 100;
    //    
    private static final float DEFAULT_FRICTION = 0.5f;
    //   ????  CLASSIC
    private static final int DEFAULT_SMOOTH_LENGTH = 50;
    //   ??? CLASSIC
    private static final int DEFAULT_SMOOTH_DURATION = 3;

    //  ?
    private static byte NO_SCROLL = 0;
    private static byte NO_SCROLL_UP = 1;
    private static byte NO_SCROLL_DOWN = 2;

    // 
    public static final byte CLASSIC = 0;
    public static final byte UPPER = 1;
    public static final byte LOWER = 2;
    public static final byte MID = 3;

    //  
    protected View mHeaderView;
    //  
    protected View mFooterView;
    //    
    protected View mContentView;
    //    
    protected int mHeaderHeight;
    //    
    protected int mFooterHeight;

    private boolean isSetHeaderHeight;
    private boolean isSetFooterHeight;

    //    
    private float mFriction = DEFAULT_FRICTION;
    //    ??
    private boolean mRefreshEnabled = true;
    //    ??
    private boolean mLoadMoreEnabled = true;
    //   ?
    protected OnRefreshListener mOnRefreshListener;
    //    ?
    protected OnLoadMoreListener mOnLoadMoreListener;
    //  
    private int mHeadStyle = CLASSIC;
    //    
    private int mFootStyle = CLASSIC;

    //    
    private int mDuration = DEFAULT_DURATION;
    //    ???  CLASSIC
    private int mSmoothLength = DEFAULT_SMOOTH_LENGTH;
    //    ?? CLASSIC
    private int mSmoothDuration = DEFAULT_SMOOTH_DURATION;

    //  ??view?
    private int isUpOrDown = NO_SCROLL;
    //  y?
    float directionX;
    //   x?
    float directionY;
    //   ??
    private int mHeadOffY;
    //    ??
    private int mFootOffY;
    //    ??
    private int mContentOffY;
    //  ??
    private float lastY;
    //  ??
    private int currentOffSetY;
    //  ?
    private int offsetSum;
    //    ?
    private int scrollSum;
    //  
    private int tempY;

    private Scroller mScroller = new Scroller(getContext());

    public CanRefreshLayout(Context context) {
        this(context, null);
    }

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

    public CanRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CanRefreshLayout, defStyleAttr, 0);

        try {
            final int N = a.getIndexCount();
            for (int i = 0; i < N; i++) {
                int attr = a.getIndex(i);
                if (attr == R.styleable.CanRefreshLayout_can_enabled_up) {
                    setRefreshEnabled(a.getBoolean(attr, true));

                } else if (attr == R.styleable.CanRefreshLayout_can_enabled_down) {
                    setLoadMoreEnabled(a.getBoolean(attr, true));

                } else if (attr == R.styleable.CanRefreshLayout_can_style_up) {
                    mHeadStyle = a.getInt(attr, CLASSIC);

                } else if (attr == R.styleable.CanRefreshLayout_can_style_down) {
                    mFootStyle = a.getInt(attr, CLASSIC);

                } else if (attr == R.styleable.CanRefreshLayout_can_friction) {
                    setFriction(a.getFloat(attr, DEFAULT_FRICTION));

                } else if (attr == R.styleable.CanRefreshLayout_can_duration) {
                    mDuration = a.getInt(attr, DEFAULT_DURATION);

                } else if (attr == R.styleable.CanRefreshLayout_can_smooth_duration) {
                    mSmoothDuration = a.getInt(attr, DEFAULT_SMOOTH_DURATION);

                } else if (attr == R.styleable.CanRefreshLayout_can_smooth_length) {
                    mSmoothLength = a.getInt(attr, DEFAULT_SMOOTH_LENGTH);
                }
            }
        } finally {
            a.recycle();
        }
    }

    /**
     * ??
     *
     * @param mHeaderHeight
     */
    public void setHeaderHeight(int mHeaderHeight) {
        this.mHeaderHeight = mHeaderHeight;
        isSetHeaderHeight = true;
    }

    /**
     * ??
     *
     * @param mFooterHeight
     */
    public void setFooterHeight(int mFooterHeight) {
        this.mFooterHeight = mFooterHeight;
        isSetFooterHeight = true;
    }

    /**
     * ??
     *
     * @param enable
     */
    public void setRefreshEnabled(boolean enable) {
        this.mRefreshEnabled = enable;
    }

    /**
     * ??
     *
     * @param enable
     */
    public void setLoadMoreEnabled(boolean enable) {
        this.mLoadMoreEnabled = enable;
    }

    /**
     * ?
     *
     * @param mOnRefreshListener
     */
    public void setOnRefreshListener(@NonNull OnRefreshListener mOnRefreshListener) {
        this.mOnRefreshListener = mOnRefreshListener;
    }

    /**
     * ?
     *
     * @param mOnLoadMoreListener
     */
    public void setOnLoadMoreListener(@NonNull OnLoadMoreListener mOnLoadMoreListener) {
        this.mOnLoadMoreListener = mOnLoadMoreListener;
    }

    /**
     * 
     *
     * @param mFriction
     */
    public void setFriction(@FloatRange(from = 0.0, to = 1.0) float mFriction) {

        this.mFriction = mFriction;

    }

    /**
     * 
     *
     * @param mDuration
     */
    public void setDuration(int mDuration) {
        this.mDuration = mDuration;
    }

    /**
     * ???
     *
     * @param mSmoothLength
     */
    public void setSmoothLength(int mSmoothLength) {
        this.mSmoothLength = mSmoothLength;
    }

    /**
     * ??
     *
     * @param mSmoothDuration
     */
    public void setSmoothDuration(int mSmoothDuration) {
        this.mSmoothDuration = mSmoothDuration;
    }

    /**
     * 
     *
     * @param headStyle
     * @param footStyle
     */
    public void setStyle(@IntRange(from = 0, to = 3) int headStyle, @IntRange(from = 0, to = 3) int footStyle) {
        this.mHeadStyle = headStyle;
        this.mFootStyle = footStyle;

        if (mHeadStyle == LOWER || mHeadStyle == MID) {
            mContentView.bringToFront();
        }

        if (mFootStyle == LOWER || mFootStyle == MID) {
            mContentView.bringToFront();
        }

        if (mHeaderView != null && mHeadStyle == CLASSIC || mHeadStyle == UPPER) {
            mHeaderView.bringToFront();
        }

        if (mFooterView != null && mFootStyle == CLASSIC || mFootStyle == UPPER) {
            mFooterView.bringToFront();
        }
    }

    /**
     * idview
     */
    @Override
    protected void onFinishInflate() {
        final int childCount = getChildCount();

        if (childCount > 0) {
            mHeaderView = findViewById(R.id.can_refresh_header);
            mContentView = findViewById(R.id.can_content_view);
            mFooterView = findViewById(R.id.can_refresh_footer);
        }
        if (mContentView == null) {
            throw new IllegalStateException("error");
        }
        if (mHeaderView != null && !(mHeaderView instanceof CanRefresh)) {
            throw new IllegalStateException("error");
        }
        if (mFooterView != null && !(mFooterView instanceof CanRefresh)) {
            throw new IllegalStateException("error");
        }
        if (mHeaderView != null) {
            getHeaderInterface().setIsHeaderOrFooter(true);
        }
        if (mFooterView != null) {
            getFooterInterface().setIsHeaderOrFooter(false);
        }
        super.onFinishInflate();
        setStyle(mHeadStyle, mFootStyle);
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        childLayout();
    }

    /**
     * view?
     */
    private void childLayout() {

        int paddingLeft = getPaddingLeft();
        int paddingTop = getPaddingTop();

        if (mHeaderView != null) {
            MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();
            final int left = paddingLeft + lp.leftMargin;
            final int top = paddingTop + lp.topMargin - mHeaderHeight + mHeadOffY;
            final int right = left + mHeaderView.getMeasuredWidth();
            final int bottom = top + mHeaderView.getMeasuredHeight();

            mHeaderView.layout(left, top, right, bottom);
        }

        if (mFooterView != null) {
            MarginLayoutParams lp = (MarginLayoutParams) mFooterView.getLayoutParams();
            final int left = paddingLeft + lp.leftMargin;
            final int top = getMeasuredHeight() + paddingTop + lp.topMargin - mFootOffY;
            final int right = left + mFooterView.getMeasuredWidth();
            final int bottom = top + mFooterView.getMeasuredHeight();
            mFooterView.layout(left, top, right, bottom);
        }
        if (mContentView != null) {
            MarginLayoutParams lp = (MarginLayoutParams) mContentView.getLayoutParams();
            final int left = paddingLeft + lp.leftMargin;
            final int top = paddingTop + lp.topMargin + mContentOffY;
            final int right = left + mContentView.getMeasuredWidth();
            final int bottom = top + mContentView.getMeasuredHeight();

            mContentView.layout(left, top, right, bottom);
        }
    }

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

        if (mHeaderView != null) {
            measureChildWithMargins(mHeaderView, widthMeasureSpec, 0, heightMeasureSpec, 0);
            MarginLayoutParams lp = (MarginLayoutParams) mHeaderView.getLayoutParams();

            if (!isSetHeaderHeight) {
                mHeaderHeight = mHeaderView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            }
        }

        if (mFooterView != null) {
            measureChildWithMargins(mFooterView, widthMeasureSpec, 0, heightMeasureSpec, 0);
            MarginLayoutParams lp = (MarginLayoutParams) mFooterView.getLayoutParams();

            if (!isSetFooterHeight) {
                mFooterHeight = mFooterView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            }
        }
        if (mContentView != null) {
            measureChildWithMargins(mContentView, widthMeasureSpec, 0, heightMeasureSpec, 0);
        }
    }

    /**
     * ?
     *
     * @return
     */
    private boolean canRefresh() {
        return mRefreshEnabled && mHeaderView != null && !canChildScrollUp();
    }

    /**
     * ?
     *
     * @return
     */
    private boolean canLoadMore() {
        return mLoadMoreEnabled && mFooterView != null && !canChildScrollDown();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent e) {
        return super.dispatchTouchEvent(e);

    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        switch (e.getAction()) {
        case MotionEvent.ACTION_DOWN:
            directionY = e.getY();
            directionX = e.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            if (directionY <= 0 || directionX <= 0) {
                break;
            }

            float eventY = e.getY();
            float eventX = e.getX();

            float offY = eventY - directionY;
            float offX = eventX - directionX;

            directionY = eventY;
            directionX = eventX;

            boolean moved = Math.abs(offY) > Math.abs(offX);

            if (offY > 0 && moved && canRefresh()) {
                isUpOrDown = NO_SCROLL_UP;
            } else if (offY < 0 && moved && canLoadMore()) {

                isUpOrDown = NO_SCROLL_DOWN;
            } else {
                isUpOrDown = NO_SCROLL;
            }

            if (isUpOrDown == NO_SCROLL_DOWN || isUpOrDown == NO_SCROLL_UP) {

                return true;
            }

            break;

        }

        return super.onInterceptTouchEvent(e);

    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {

        //     ??view
        if (!canChildScrollDown() && !canChildScrollUp()) {

            if (isUpOrDown == NO_SCROLL_UP) {
                if (canRefresh()) {

                    return touch(e, true);

                }
            } else if (isUpOrDown == NO_SCROLL_DOWN) {
                if (canLoadMore()) {

                    return touch(e, false);
                }

            } else {

                switch (e.getAction()) {

                case MotionEvent.ACTION_DOWN:

                    directionY = e.getY();
                    directionX = e.getX();

                    break;

                case MotionEvent.ACTION_MOVE:
                    if (directionY <= 0 || directionX <= 0) {

                        break;
                    }

                    float eventY = e.getY();
                    float eventX = e.getX();

                    float offY = eventY - directionY;
                    float offX = eventX - directionX;

                    directionY = eventY;
                    directionX = eventX;

                    boolean moved = Math.abs(offY) > Math.abs(offX);

                    if (offY > 0 && moved && canRefresh()) {
                        isUpOrDown = NO_SCROLL_UP;
                    } else if (offY < 0 && moved && canLoadMore()) {

                        isUpOrDown = NO_SCROLL_DOWN;
                    } else {
                        isUpOrDown = NO_SCROLL;
                    }

                    break;

                }

                return true;

            }

        } else {

            if (canRefresh()) {
                return touch(e, true);

            } else if (canLoadMore()) {

                return touch(e, false);
            }
        }

        return super.onTouchEvent(e);
    }

    /**
     * ?
     *
     * @param e
     * @param isHead
     * @return
     */
    private boolean touch(MotionEvent e, boolean isHead) {

        switch (e.getAction()) {

        case MotionEvent.ACTION_DOWN:

            lastY = e.getY();
            return true;

        case MotionEvent.ACTION_MOVE:

            if (lastY > 0) {
                currentOffSetY = (int) (e.getY() - lastY);
                offsetSum += currentOffSetY;
            }
            lastY = e.getY();

            boolean isCanMove;
            if (isHead) {
                isCanMove = offsetSum > 0;
            } else {
                isCanMove = offsetSum < 0;
            }

            if (isCanMove) {

                float ratio = getRatio();

                if (ratio < 0) {
                    ratio = 0;
                }

                int scrollNum = -((int) (currentOffSetY * ratio));

                scrollSum += scrollNum;

                if (isHead) {

                    smoothMove(true, true, scrollNum, scrollSum);

                    if (Math.abs(scrollSum) > mHeaderHeight) {

                        getHeaderInterface().onPrepare();
                    }

                    getHeaderInterface().onPositionChange(Math.abs(scrollSum) / (float) mHeaderHeight);
                } else {

                    smoothMove(false, true, scrollNum, scrollSum);

                    if (Math.abs(scrollSum) > mFooterHeight) {

                        getFooterInterface().onPrepare();
                    }

                    getFooterInterface().onPositionChange(Math.abs(scrollSum) / (float) mFooterHeight);

                }

            }

            return true;

        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:

            if (Math.abs(scrollSum) > 3) {

                if (isHead) {
                    if (Math.abs(scrollSum) > mHeaderHeight) {

                        smoothMove(true, false, -mHeaderHeight, mHeaderHeight);
                        getHeaderInterface().onRelease();
                        refreshing();
                    } else {

                        smoothMove(true, false, 0, 0);
                    }

                } else {

                    if (Math.abs(scrollSum) > mFooterHeight) {

                        smoothMove(false, false,
                                mContentView.getMeasuredHeight() - getMeasuredHeight() + mFooterHeight,
                                mFooterHeight);
                        getFooterInterface().onRelease();
                        loadingMore();
                    } else {

                        smoothMove(false, false, mContentView.getMeasuredHeight() - getMeasuredHeight(), 0);

                    }

                }

            }

            resetParameter();

            break;

        }

        return super.onTouchEvent(e);

    }

    /**
     * ??
     *
     * @return
     */
    private float getRatio() {

        return 1 - (Math.abs(offsetSum) / (float) getMeasuredHeight()) - 0.3f * mFriction;

    }

    /**
     * ??
     */
    private void resetParameter() {

        directionX = 0;
        directionY = 0;
        isUpOrDown = NO_SCROLL;
        lastY = 0;
        offsetSum = 0;
        scrollSum = 0;

    }

    /**
     * * 
     *
     * @param isHeader
     * @param isMove      ?
     * @param moveScrollY
     * @param moveY
     */
    private void smoothMove(boolean isHeader, boolean isMove, int moveScrollY, int moveY) {

        moveY = Math.abs(moveY);
        if (isHeader) {

            if (mHeadStyle == CLASSIC) {

                if (isMove) {
                    smoothScrollBy(0, moveScrollY);
                } else {

                    smoothScrollTo(0, moveScrollY);
                }
            } else {

                layoutMove(isHeader, isMove, moveY);
            }

        } else {

            if (mFootStyle == CLASSIC) {

                if (isMove) {
                    smoothScrollBy(0, moveScrollY);
                } else {

                    smoothScrollTo(0, moveScrollY);
                }
            } else {

                layoutMove(isHeader, isMove, moveY);
            }

        }

    }

    /**
     * ?
     *
     * @param fx
     * @param fy
     */
    public void smoothScrollTo(int fx, int fy) {
        int dx = fx - mScroller.getFinalX();
        int dy = fy - mScroller.getFinalY();
        smoothScrollBy(dx, dy);
    }

    /**
     * ??
     *
     * @param dx
     * @param dy
     */
    public void smoothScrollBy(int dx, int dy) {

        mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), dx, dy);
        invalidate();

    }

    /**
     * ???
     *
     * @param isHeader
     * @param isMove
     * @param moveY
     */
    private void layoutMove(boolean isHeader, boolean isMove, int moveY) {

        if (isMove) {

            if (isHeader) {

                if (mHeadStyle == UPPER) {

                    mHeadOffY = moveY;

                } else if (mHeadStyle == LOWER) {

                    mHeadOffY = mHeaderHeight;
                    mContentOffY = moveY;

                } else if (mHeadStyle == MID) {

                    mHeadOffY = moveY / 2 + mHeaderHeight / 2;
                    mContentOffY = moveY;

                }

            } else {

                if (mFootStyle == UPPER) {

                    mFootOffY = moveY;

                } else if (mFootStyle == LOWER) {

                    mFootOffY = mFooterHeight;
                    mContentOffY = -moveY;

                } else if (mFootStyle == MID) {

                    mFootOffY = moveY / 2 + mFooterHeight / 2;
                    mContentOffY = -moveY;

                }

            }

        } else {

            if (isHeader) {
                layoutMoveSmooth(isHeader, moveY, mHeaderHeight);

            } else {

                layoutMoveSmooth(isHeader, moveY, mFooterHeight);
            }

        }

        requestLayout();
    }

    private void layoutMoveSmooth(boolean isHeader, int moveY, int mHeight) {

        if (moveY == mHeight) {

            tempY = Math.abs(scrollSum);
            layoutSmoothMove(isHeader, moveY);

        } else if (moveY == 0) {

            tempY = mHeight;
            layoutSmoothMove(isHeader, moveY);

        }

    }

    private void layoutSmoothMove(final boolean isHeader, final int moveY) {

        tempY -= mSmoothLength;

        if (tempY <= moveY) {

            layoutMove(isHeader, true, moveY);
            return;
        }

        layoutMove(isHeader, true, tempY);

        postDelayed(new Runnable() {
            @Override
            public void run() {
                layoutSmoothMove(isHeader, moveY);
            }
        }, mSmoothDuration);

    }

    /**
     * ?
     */
    public void refreshComplete() {

        postDelayed(new Runnable() {
            @Override
            public void run() {
                smoothMove(true, false, 0, 0);
                getHeaderInterface().onComplete();
                getHeaderInterface().onReset();
            }
        }, mDuration);

    }

    /**
     * ?
     */
    public void loadMoreComplete() {

        postDelayed(new Runnable() {
            @Override
            public void run() {
                smoothMove(false, false, mContentView.getMeasuredHeight() - getMeasuredHeight(), 0);
                getFooterInterface().onComplete();
                getFooterInterface().onReset();
            }
        }, mDuration);

    }

    /**
     * 
     */
    public void autoRefresh() {

        if (mHeaderView != null) {
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    smoothMove(true, false, -mHeaderHeight, -mHeaderHeight);
                    getHeaderInterface().onRelease();
                    refreshing();
                }
            }, DEFAULT_AUTO_DURATION);

        }

    }

    private void refreshing() {
        if (mOnRefreshListener != null) {
            mOnRefreshListener.onRefresh();
        }

    }

    private void loadingMore() {
        if (mOnLoadMoreListener != null) {
            mOnLoadMoreListener.onLoadMore();
        }

    }

    @Override
    public void computeScroll() {

        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }

        super.computeScroll();
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p != null && p instanceof LayoutParams;
    }

    @Override
    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    @Override
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    public static class LayoutParams extends MarginLayoutParams {

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        @SuppressWarnings({ "unused" })
        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }
    }

    private CanRefresh getHeaderInterface() {
        return (CanRefresh) mHeaderView;
    }

    private CanRefresh getFooterInterface() {
        return (CanRefresh) mFooterView;
    }

    /**
     * ?
     *
     * @return
     */
    protected boolean canChildScrollUp() {
        if (android.os.Build.VERSION.SDK_INT < 14) {
            if (mContentView instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) mContentView;
                return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0
                        || absListView.getChildAt(0).getTop() < absListView.getPaddingTop());
            } else {
                return ViewCompat.canScrollVertically(mContentView, -1) || mContentView.getScrollY() > 0;
            }
        } else {
            return ViewCompat.canScrollVertically(mContentView, -1);
        }
    }

    /**
     * ?
     *
     * @return
     */
    protected boolean canChildScrollDown() {
        if (android.os.Build.VERSION.SDK_INT < 14) {
            if (mContentView instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) mContentView;
                return absListView.getChildCount() > 0
                        && (absListView.getLastVisiblePosition() < absListView.getChildCount() - 1
                                || absListView.getChildAt(absListView.getChildCount() - 1).getBottom() > absListView
                                        .getPaddingBottom());
            } else {
                return ViewCompat.canScrollVertically(mContentView, 1) || mContentView.getScrollY() < 0;
            }
        } else {
            return ViewCompat.canScrollVertically(mContentView, 1);
        }
    }

    public interface OnLoadMoreListener {
        void onLoadMore();
    }

    public interface OnRefreshListener {
        void onRefresh();
    }
}