Java tutorial
/* * 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); } }