com.recyclerviewpulldownrefresh.view.refresh.AbPullToRefreshView.java Source code

Java tutorial

Introduction

Here is the source code for com.recyclerviewpulldownrefresh.view.refresh.AbPullToRefreshView.java

Source

/*
 * Copyright (C) 2012 www.amsoft.cn
 * 
 * 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
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */
package com.recyclerviewpulldownrefresh.view.refresh;

import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.WebView;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.ScrollView;

/**
 *  2012 amsoft.cn
 * ??AbPullToRefreshView.java
 * ??View.
 *
 */
public class AbPullToRefreshView extends LinearLayout {

    /**
     * .
     */
    private Context mContext = null;

    /**
     * .
     */
    private boolean mEnablePullRefresh = true;

    /**
     * .
     */
    private boolean mEnableLoadMore = true;

    /**
     * x?.
     */
    private int mLastMotionX;

    /**
     * y?.
     */
    private int mLastMotionY;

    /**
     * header view.
     */
    private AbListViewHeader mHeaderView;

    /**
     * footer view.
     */
    private AbListViewFooter mFooterView;

    /**
     * list or grid.
     */
    private AdapterView<?> mAdapterView;

    /**
     * Scrollview.
     */
    private ScrollView mScrollView;

    /**
     * RecyclerView.
     */
    private RecyclerView recyclerView;

    private View mTarget;

    /**
     * header view .
     */
    private int mHeaderViewHeight;

    /**
     * footer view .
     */
    private int mFooterViewHeight;

    /**
     * ?.
     */
    private int mPullState;

    /**
     * .
     */
    private static final int PULL_UP_STATE = 0;

    /**
     * .
     */
    private static final int PULL_DOWN_STATE = 1;

    /**
     * .
     */
    private boolean mPullRefreshing = false;

    /**
     * .
     */
    private boolean mPullLoading = false;

    /**
     * Footer?.
     */
    private OnFooterRefreshListener mOnFooterLoadListener;

    /**
     * Header?.
     */
    private OnHeaderRefreshListener mOnHeaderRefreshListener;

    /**
     * .
     *
     * @param context the context
     * @param attrs   the attrs
     */
    public AbPullToRefreshView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    /**
     * .
     *
     * @param context the context
     */
    public AbPullToRefreshView(Context context) {
        super(context);
        init(context);
    }

    /**
     * ?View.
     *
     * @param context the context
     */
    private void init(Context context) {
        mContext = context;
        this.setOrientation(LinearLayout.VERTICAL);
        // HeaderView
        addHeaderView();
    }

    /**
     * add HeaderView.
     */
    private void addHeaderView() {
        mHeaderView = new AbListViewHeader(mContext);
        mHeaderViewHeight = mHeaderView.getHeaderHeight();
        mHeaderView.setGravity(Gravity.BOTTOM);

        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeaderViewHeight);
        // topMarginheader View,???
        params.topMargin = -mHeaderViewHeight;
        addView(mHeaderView, params);
    }

    /**
     * add FooterView.
     */
    private void addFooterView() {
        mFooterView = new AbListViewFooter(mContext);
        mFooterViewHeight = mFooterView.getFooterHeight();

        LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mFooterViewHeight);
        addView(mFooterView, params);
    }

    /**
     * footer view??linearlayout?.
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        addFooterView();
        initContentAdapterView();
    }

    /**
     * init AdapterView like ListView,
     * GridView and so on;
     * or init ScrollView.
     */
    private void initContentAdapterView() {
        int count = getChildCount();
        if (count < 3) {
            throw new IllegalArgumentException(
                    "this layout must contain 3 child views,and AdapterView or ScrollView must in the second position!");
        }
        View view;
        for (int i = 0; i < count - 1; ++i) {
            view = getChildAt(i);

            if (view instanceof AdapterView<?>) {
                mAdapterView = (AdapterView<?>) view;
                mTarget = view;
            } else if (view instanceof ScrollView) {
                // finish later
                mScrollView = (ScrollView) view;
                mTarget = view;
            } else if (view instanceof RecyclerView) {
                // finish later
                recyclerView = (RecyclerView) view;
                mTarget = view;
            } else if (view instanceof WebView) {
                mTarget = view;
            }
        }
        if (mAdapterView == null && mScrollView == null && recyclerView == null) {
            //            throw new IllegalArgumentException("must contain a AdapterView or ScrollView in this layout!");
        }
    }

    /* (non-Javadoc)
     * @see android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // down,y??
            mLastMotionX = x;
            mLastMotionY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            // deltaY > 0 ??,< 0??
            int deltaX = x - mLastMotionX;
            int deltaY = y - mLastMotionY;
            //?
            if (Math.abs(deltaX) < Math.abs(deltaY) && Math.abs(deltaY) > 10) {
                if (isRefreshViewScroll(deltaY)) {
                    return true;
                }
            }
            break;
        }
        return super.onInterceptTouchEvent(e);
    }

    /*
     * onInterceptTouchEvent()(?onInterceptTouchEvent() return
     * false)PullToRefreshView ?View??;????(?PullToRefreshView??)
     * @see android.view.View#onTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            break;
        case MotionEvent.ACTION_MOVE:
            int deltaY = y - mLastMotionY;
            if (mPullState == PULL_DOWN_STATE) {
                // 
                headerPrepareToRefresh(deltaY);
            } else if (mPullState == PULL_UP_STATE) {
                // 
                footerPrepareToRefresh(deltaY);
            }
            mLastMotionY = y;
            break;
        //UPCANCEL?
        case MotionEvent.ACTION_UP:
        case MotionEvent.ACTION_CANCEL:
            int topMargin = getHeaderTopMargin();
            if (mPullState == PULL_DOWN_STATE) {
                if (topMargin >= 0) {
                    // 
                    headerRefreshing();
                } else {
                    // ???
                    setHeaderTopMargin(-mHeaderViewHeight);
                }
            } else if (mPullState == PULL_UP_STATE) {
                //
                if (Math.abs(topMargin) >= mHeaderViewHeight + mFooterViewHeight) {
                    // footer 
                    footerLoading();
                } else {
                    // ???
                    setHeaderTopMargin(-mHeaderViewHeight);
                }
            }
            break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * ???.
     *
     * @param deltaY deltaY > 0 ??,< 0??
     * @return true, if is refresh view scroll
     */
    private boolean isRefreshViewScroll(int deltaY) {
        if (mPullRefreshing || mPullLoading) {
            return false;
        }

        if (null == mTarget) {
            return false;
        }

        //
        if (deltaY > 0) {
            boolean canScrollUp = canChildScrollUp();
            if (canScrollUp) {
                mPullState = PULL_DOWN_STATE;
            }
            return canScrollUp;
        } else if (deltaY < 0) { //
            boolean canScrollDown = canChildScrollDown();
            if (canScrollDown) {
                mPullState = PULL_UP_STATE;
            }
            return canScrollDown;
        }
        return false;
    }

    /**
     * ??
     *
     * @return
     */
    public boolean canChildScrollUp() {
        // ???
        if (!mEnablePullRefresh) {
            return false;
        }

        if (mTarget instanceof AdapterView<?>) {
            final AdapterView<?> absListView = (AdapterView<?>) mTarget;
            View child = absListView.getChildAt(0);
            if (child == null) {
                return true;
            }
            if (absListView.getFirstVisiblePosition() == 0 && child.getTop() == 0) {
                return true;
            }

            int top = child.getTop();
            int padding = absListView.getPaddingTop();
            if (absListView.getFirstVisiblePosition() == 0 && Math.abs(top - padding) <= 11) {
                return true;
            }
            return false;
        } else if (mTarget instanceof ScrollView) {
            return mScrollView.getScrollY() == 0;
        } else if (mTarget instanceof WebView) {
            WebView webView = (WebView) mTarget;
            if (webView != null) {
                return webView.getScrollY() == 0;
            }
            return false;
        } else if (mTarget instanceof RecyclerView) {
            int firstVisiblePosition = 0;

            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
            View childView = layoutManager.getChildAt(0);
            if (null == childView) {
                return true;
            }

            if (layoutManager instanceof LinearLayoutManager) {
                firstVisiblePosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
            } else if (layoutManager instanceof GridLayoutManager) {
                firstVisiblePosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                int[] spanPosition = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
                ((StaggeredGridLayoutManager) layoutManager).findFirstVisibleItemPositions(spanPosition);
                firstVisiblePosition = getFirstVisibleItemPosition(spanPosition);
            }

            if (childView.getTop() == 0 && firstVisiblePosition == 0) {
                return true;
            }
            return false;
        } else {
            return ViewCompat.canScrollVertically(mTarget, 1);
        }
    }

    /**
     * ??
     *
     * @return
     */
    public boolean canChildScrollDown() {
        if (!mEnableLoadMore) {
            return false;
        }
        if (mTarget instanceof AdapterView<?>) {
            AdapterView<?> absListView = (AdapterView<?>) mTarget;
            View lastChild = absListView.getChildAt(absListView.getChildCount() - 1);
            if (lastChild == null) {
                return true;
            }
            // ??viewBottom?ViewmAdapterView?view,
            // ViewmAdapterView??
            if (lastChild.getBottom() <= getHeight()
                    && absListView.getLastVisiblePosition() == absListView.getCount() - 1) {
                return true;
            }
        } else if (mTarget instanceof WebView) {
            WebView webview = (WebView) mTarget;
            return webview.getContentHeight() * webview.getScale() <= webview.getHeight() + webview.getScrollY();
        } else if (mTarget instanceof ScrollView) {
            ScrollView scrollView = (ScrollView) mTarget;
            View childView = scrollView.getChildAt(0);
            if (childView != null) {
                return childView.getMeasuredHeight() <= getHeight() + mScrollView.getScrollY();
            }
        } else if (mTarget instanceof RecyclerView) {
            int lastVisiblePosition = 0;
            View childView = null;

            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

            if (layoutManager instanceof LinearLayoutManager) {
                lastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                childView = layoutManager.findViewByPosition(lastVisiblePosition);
            } else if (layoutManager instanceof GridLayoutManager) {
                lastVisiblePosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
                childView = layoutManager.findViewByPosition(lastVisiblePosition);
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                int[] spanPosition = new int[((StaggeredGridLayoutManager) layoutManager).getSpanCount()];
                ((StaggeredGridLayoutManager) layoutManager).findLastVisibleItemPositions(spanPosition);
                lastVisiblePosition = getLastVisibleItemPosition(spanPosition);
                childView = getLastVisibleItemBottomView(layoutManager, spanPosition, lastVisiblePosition);
            }
            if (null == childView) {
                return false;
            }

            if (lastVisiblePosition == layoutManager.getItemCount() - 1 && childView.getBottom()
                    + layoutManager.getBottomDecorationHeight(childView) <= mTarget.getBottom()) {
                return true;
            }
        } else {
            return ViewCompat.canScrollVertically(mTarget, 1);
        }
        return false;
    }

    /**
     * StaggeredGridLayoutManager ? ??position
     *
     * @param spanPosition ?spanposition
     * @return ?position
     */
    private int getLastVisibleItemPosition(int[] spanPosition) {
        if (null == spanPosition || null == mTarget) {
            return 0;
        }

        int maxPosition = 0;

        for (int position : spanPosition) {
            if (maxPosition < position) {
                maxPosition = position;
            }
        }
        return maxPosition;
    }

    private View getLastVisibleItemBottomView(RecyclerView.LayoutManager layoutManager, final int[] spanPosition,
            final int lastVisiblePosition) {
        if (null == spanPosition || null == layoutManager) {
            return null;
        }
        int maxBottom = 0;
        View lastChildView = null;

        for (int position = 0; position < spanPosition.length; position++) {
            if (lastVisiblePosition < position) {
                break;
            }

            View childView = layoutManager.findViewByPosition(lastVisiblePosition - position);
            if (null == childView) {
                continue;
            }

            if (maxBottom < childView.getBottom()) {
                lastChildView = childView;
                maxBottom = childView.getBottom();
            }
        }
        return lastChildView;
    }

    /**
     * StaggeredGridLayoutManager ? ???position<p/>
     * 0
     *
     * @param spanPosition ?spanposition
     * @return ??position
     */
    private int getFirstVisibleItemPosition(int[] spanPosition) {
        if (null == spanPosition) {
            return 0;
        }

        int maxPosition = 0;

        for (int position : spanPosition) {
            if (maxPosition > position) {
                maxPosition = position;
            }
        }
        return maxPosition;
    }

    /**
     * header ,,.
     *
     * @param deltaY ?
     */
    private void headerPrepareToRefresh(int deltaY) {
        if (mPullRefreshing || mPullLoading) {
            return;
        }

        int newTopMargin = updateHeaderViewTopMargin(deltaY);
        // header viewtopMargin>=0header view? ,header view ???
        if (newTopMargin >= 0 && mHeaderView.getState() != AbListViewHeader.STATE_REFRESHING) {
            //???
            mHeaderView.setState(AbListViewHeader.STATE_READY);
        } else if (newTopMargin < 0 && newTopMargin > -mHeaderViewHeight) {
            //??
            mHeaderView.setState(AbListViewHeader.STATE_NORMAL);
        }
    }

    /**
     * footer ,, footer view?header view
     * header viewtopmargin?.
     *
     * @param deltaY ?
     */
    private void footerPrepareToRefresh(int deltaY) {
        if (mPullRefreshing || mPullLoading) {
            return;
        }
        int newTopMargin = updateHeaderViewTopMargin(deltaY);
        // header view topMargin ?header + footer 
        // footer view ?footer view ???
        if (Math.abs(newTopMargin) >= (mHeaderViewHeight + mFooterViewHeight)
                && mFooterView.getState() != AbListViewFooter.STATE_LOADING) {
            mFooterView.setState(AbListViewFooter.STATE_READY);
        } else if (Math.abs(newTopMargin) < (mHeaderViewHeight + mFooterViewHeight)) {
            mFooterView.setState(AbListViewFooter.STATE_NORMAL);
        }
    }

    /**
     * Header view top margin.
     *
     * @param deltaY the delta y
     * @return the int
     */
    private int updateHeaderViewTopMargin(int deltaY) {
        LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
        float newTopMargin = params.topMargin + deltaY * 0.3f;
        // ??,????,?
        // ??,?
        if (deltaY > 0 && mPullState == PULL_UP_STATE && Math.abs(params.topMargin) <= mHeaderViewHeight) {
            return params.topMargin;
        }
        // ?,??,???bug
        if (deltaY < 0 && mPullState == PULL_DOWN_STATE && Math.abs(params.topMargin) >= mHeaderViewHeight) {
            return params.topMargin;
        }
        params.topMargin = (int) newTopMargin;
        mHeaderView.setLayoutParams(params);
        return params.topMargin;
    }

    /**
     * .
     */
    public void headerRefreshing() {
        mPullRefreshing = true;
        mHeaderView.setState(AbListViewHeader.STATE_REFRESHING);
        setHeaderTopMargin(0);
        if (mOnHeaderRefreshListener != null) {
            mOnHeaderRefreshListener.onHeaderRefresh(this);
        }
    }

    /**
     * .
     */
    private void footerLoading() {
        mPullLoading = true;
        mFooterView.setState(AbListViewFooter.STATE_LOADING);
        int top = mHeaderViewHeight + mFooterViewHeight;
        setHeaderTopMargin(-top);
        if (mOnFooterLoadListener != null) {
            mOnFooterLoadListener.onFooterRefresh(this);
        }
    }

    /**
     * header view topMargin.
     *
     * @param topMargin the new header top margin
     */
    private void setHeaderTopMargin(int topMargin) {
        LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
        params.topMargin = topMargin;
        mHeaderView.setLayoutParams(params);
    }

    /**
     * header view ??????.
     */
    public void onHeaderRefreshFinish() {
        setHeaderTopMargin(-mHeaderViewHeight);
        mHeaderView.setState(AbListViewHeader.STATE_SUCCESS);
        mPullRefreshing = false;
    }

    /**
     * footer view ??????.
     */
    public void onFooterLoadFinish() {
        setHeaderTopMargin(-mHeaderViewHeight);
        mHeaderView.setState(AbListViewHeader.STATE_SUCCESS);
        mPullLoading = false;
    }

    /**
     * ??header view topMargin.
     *
     * @return the header top margin
     */
    private int getHeaderTopMargin() {
        LayoutParams params = (LayoutParams) mHeaderView.getLayoutParams();
        return params.topMargin;
    }

    /**
     * ?.
     *
     * @param headerRefreshListener the new on header refresh listener
     */
    public void setOnHeaderRefreshListener(OnHeaderRefreshListener headerRefreshListener) {
        mOnHeaderRefreshListener = headerRefreshListener;
    }

    /**
     * ?.
     *
     * @param footerLoadListener the new on footer load listener
     */
    public void setOnFooterRefreshListener(OnFooterRefreshListener footerLoadListener) {
        mOnFooterLoadListener = footerLoadListener;
    }

    /**
     * .
     *
     * @param enable 
     */
    public void setPullRefreshEnable(boolean enable) {
        mEnablePullRefresh = enable;
    }

    /**
     * .
     *
     * @param enable 
     */
    public void setLoadMoreEnable(boolean enable) {
        mEnableLoadMore = enable;
    }

    /**
     * ?.
     *
     * @return true, if is enable pull refresh
     */
    public boolean isEnablePullRefresh() {
        return mEnablePullRefresh;
    }

    /**
     * ?.
     *
     * @return true, if is enable load more
     */
    public boolean isEnableLoadMore() {
        return mEnableLoadMore;
    }

    /**
     * ???Header View.
     *
     * @return the header view
     */
    public AbListViewHeader getHeaderView() {
        return mHeaderView;
    }

    /**
     * ???Footer View.
     *
     * @return the footer view
     */
    public AbListViewFooter getFooterView() {
        return mFooterView;
    }

    /**
     * Interface definition for a callback to be invoked when list/grid footer
     * view should be refreshed.
     */
    public interface OnFooterRefreshListener {

        /**
         * On footer load.
         *
         * @param view the view
         */
        void onFooterRefresh(AbPullToRefreshView view);
    }

    /**
     * Interface definition for a callback to be invoked when list/grid header
     * view should be refreshed.
     */
    public interface OnHeaderRefreshListener {

        /**
         * On header refresh.
         *
         * @param view the view
         */
        void onHeaderRefresh(AbPullToRefreshView view);
    }
}