大家在各自的项目中肯定会使用到到各种各样的自己定义的view,最常见的是自定义listview啊,gridview啊等视图空间,在自定义这些控件以后,大家会发现定义的好的视图空间需要上拉刷新下拉加载的功能:
大家先看代码:
public class PullToRefreshListViewCompantPlan extends PullToRefreshAdapterViewBase<ListViewCompatPlan>{ public PullToRefreshListViewCompantPlan(Context context) { super(context); } public PullToRefreshListViewCompantPlan(Context context, AttributeSet attrs) { super(context,attrs); } public PullToRefreshListViewCompantPlan(Context context, Mode mode) { super(context, mode); } public PullToRefreshListViewCompantPlan(Context context, Mode mode, AnimationStyle style) { super(context, mode, style); } @Override public Orientation getPullToRefreshScrollDirection() { return Orientation.VERTICAL; } @Override protected ListViewCompatPlan createRefreshableView(Context context, AttributeSet attrs) { final ListViewCompatPlan lv; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) { lv = new InternalExpandableListViewSDK9(context, attrs); } else { lv = new InternalExpandableListView(context, attrs); } // Set it to this so it can be used in ListActivity/ListFragment lv.setId(android.R.id.list); return lv; } class InternalExpandableListView extends ListViewCompatPlan implements EmptyViewMethodAccessor { public InternalExpandableListView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void setEmptyView(View emptyView) { PullToRefreshListViewCompantPlan.this.setEmptyView(emptyView); } @Override public void setEmptyViewInternal(View emptyView) { super.setEmptyView(emptyView); } } @TargetApi(9) final class InternalExpandableListViewSDK9 extends InternalExpandableListView { public InternalExpandableListViewSDK9(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { final boolean returnValue = super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); // Does all of the hard work... OverscrollHelper.overScrollBy(PullToRefreshListViewCompantPlan.this, deltaX, scrollX, deltaY, scrollY, isTouchEvent); return returnValue; } } }其中1.<ListViewCompantPlan>中的泛型是大家自己自定义的view;
2.getPullToRefreshScrollDirection()方法标明刷新的方向,返回的是Oriention.VERTICAL:标明是垂直方向上下刷新
3.createRefreshableView 创建刷新的view,其中setEmptyView给自定义的view设置空view;overScrollBy创建出刷新的view。
继承与PullToRefreshAdapterViewBase<T extends AbsListView> 代码如下:
public abstract class PullToRefreshAdapterViewBase<T extends AbsListView> extends PullToRefreshBase<T> implements OnScrollListener { private static FrameLayout.LayoutParams convertEmptyViewLayoutParams(ViewGroup.LayoutParams lp) { FrameLayout.LayoutParams newLp = null; if (null != lp) { newLp = new FrameLayout.LayoutParams(lp); if (lp instanceof LayoutParams) { newLp.gravity = ((LayoutParams) lp).gravity; } else { newLp.gravity = Gravity.CENTER; } } return newLp; } private boolean mLastItemVisible; private OnScrollListener mOnScrollListener; private OnLastItemVisibleListener mOnLastItemVisibleListener; private View mEmptyView; private IndicatorLayout mIndicatorIvTop; private IndicatorLayout mIndicatorIvBottom; private boolean mShowIndicator; private boolean mScrollEmptyView = true; public PullToRefreshAdapterViewBase(Context context) { super(context); mRefreshableView.setOnScrollListener(this); } public PullToRefreshAdapterViewBase(Context context, AttributeSet attrs) { super(context, attrs); mRefreshableView.setOnScrollListener(this); } public PullToRefreshAdapterViewBase(Context context, Mode mode) { super(context, mode); mRefreshableView.setOnScrollListener(this); } public PullToRefreshAdapterViewBase(Context context, Mode mode, AnimationStyle animStyle) { super(context, mode, animStyle); mRefreshableView.setOnScrollListener(this); } /** * Gets whether an indicator graphic should be displayed when the View is in * a state where a Pull-to-Refresh can happen. An example of this state is * when the Adapter View is scrolled to the top and the mode is set to * {@link Mode#PULL_FROM_START}. The default value is <var>true</var> if * {@link PullToRefreshBase#isPullToRefreshOverScrollEnabled() * isPullToRefreshOverScrollEnabled()} returns false. * * @return true if the indicators will be shown */ public boolean getShowIndicator() { return mShowIndicator; } public final void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { if (DEBUG) { Log.d(LOG_TAG, "First Visible: " + firstVisibleItem + ". Visible Count: " + visibleItemCount + ". Total Items:" + totalItemCount); } /** * Set whether the Last Item is Visible. lastVisibleItemIndex is a * zero-based index, so we minus one totalItemCount to check */ if (null != mOnLastItemVisibleListener) { mLastItemVisible = (totalItemCount > 0) && (firstVisibleItem + visibleItemCount >= totalItemCount - 1); } // If we're showing the indicator, check positions... if (getShowIndicatorInternal()) { updateIndicatorViewsVisibility(); } // Finally call OnScrollListener if we have one if (null != mOnScrollListener) { mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } } public final void onScrollStateChanged(final AbsListView view, final int state) { /** * Check that the scrolling has stopped, and that the last item is * visible. */ if (state == OnScrollListener.SCROLL_STATE_IDLE && null != mOnLastItemVisibleListener && mLastItemVisible) { mOnLastItemVisibleListener.onLastItemVisible(); } if (null != mOnScrollListener) { mOnScrollListener.onScrollStateChanged(view, state); } } /** * Pass-through method for {@link PullToRefreshBase#getRefreshableView() * getRefreshableView()}. * {@link AdapterView#setAdapter(Adapter)} * setAdapter(adapter)}. This is just for convenience! * * @param adapter - Adapter to set */ public void setAdapter(ListAdapter adapter) { ((AdapterView<ListAdapter>) mRefreshableView).setAdapter(adapter); } /** * Sets the Empty View to be used by the Adapter View. * <p/> * We need it handle it ourselves so that we can Pull-to-Refresh when the * Empty View is shown. * <p/> * Please note, you do <strong>not</strong> usually need to call this method * yourself. Calling setEmptyView on the AdapterView will automatically call * this method and set everything up. This includes when the Android * Framework automatically sets the Empty View based on it's ID. * * @param newEmptyView - Empty View to be used */ public final void setEmptyView(View newEmptyView) { FrameLayout refreshableViewWrapper = getRefreshableViewWrapper(); if (null != newEmptyView) { // New view needs to be clickable so that Android recognizes it as a // target for Touch Events newEmptyView.setClickable(true); ViewParent newEmptyViewParent = newEmptyView.getParent(); if (null != newEmptyViewParent && newEmptyViewParent instanceof ViewGroup) { ((ViewGroup) newEmptyViewParent).removeView(newEmptyView); } // We need to convert any LayoutParams so that it works in our // FrameLayout FrameLayout.LayoutParams lp = convertEmptyViewLayoutParams(newEmptyView.getLayoutParams()); if (null != lp) { refreshableViewWrapper.addView(newEmptyView, lp); } else { refreshableViewWrapper.addView(newEmptyView); } } if (mRefreshableView instanceof EmptyViewMethodAccessor) { ((EmptyViewMethodAccessor) mRefreshableView).setEmptyViewInternal(newEmptyView); } else { mRefreshableView.setEmptyView(newEmptyView); } mEmptyView = newEmptyView; } /** * Pass-through method for {@link PullToRefreshBase#getRefreshableView() * getRefreshableView()}. * {@link AdapterView#setOnItemClickListener(OnItemClickListener) * setOnItemClickListener(listener)}. This is just for convenience! * * @param listener - OnItemClickListener to use */ public void setOnItemClickListener(OnItemClickListener listener) { mRefreshableView.setOnItemClickListener(listener); } public final void setOnLastItemVisibleListener(OnLastItemVisibleListener listener) { mOnLastItemVisibleListener = listener; } public final void setOnScrollListener(OnScrollListener listener) { mOnScrollListener = listener; } public final void setScrollEmptyView(boolean doScroll) { mScrollEmptyView = doScroll; } /** * Sets whether an indicator graphic should be displayed when the View is in * a state where a Pull-to-Refresh can happen. An example of this state is * when the Adapter View is scrolled to the top and the mode is set to * {@link Mode#PULL_FROM_START} * * @param showIndicator - true if the indicators should be shown. */ public void setShowIndicator(boolean showIndicator) { mShowIndicator = showIndicator; if (getShowIndicatorInternal()) { // If we're set to Show Indicator, add/update them addIndicatorViews(); } else { // If not, then remove then removeIndicatorViews(); } } ; @Override protected void onPullToRefresh() { super.onPullToRefresh(); if (getShowIndicatorInternal()) { switch (getCurrentMode()) { case PULL_FROM_END: mIndicatorIvBottom.pullToRefresh(); break; case PULL_FROM_START: mIndicatorIvTop.pullToRefresh(); break; default: // NO-OP break; } } } protected void onRefreshing(boolean doScroll) { super.onRefreshing(doScroll); if (getShowIndicatorInternal()) { updateIndicatorViewsVisibility(); } } @Override protected void onReleaseToRefresh() { super.onReleaseToRefresh(); if (getShowIndicatorInternal()) { switch (getCurrentMode()) { case PULL_FROM_END: mIndicatorIvBottom.releaseToRefresh(); break; case PULL_FROM_START: mIndicatorIvTop.releaseToRefresh(); break; default: // NO-OP break; } } } @Override protected void onReset() { super.onReset(); if (getShowIndicatorInternal()) { updateIndicatorViewsVisibility(); } } @Override protected void handleStyledAttributes(TypedArray a) { // Set Show Indicator to the XML value, or default value mShowIndicator = a.getBoolean(R.styleable.PullToRefresh_ptrShowIndicator, !isPullToRefreshOverScrollEnabled()); } protected boolean isReadyForPullStart() { return isFirstItemVisible(); } protected boolean isReadyForPullEnd() { return isLastItemVisible(); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (null != mEmptyView && !mScrollEmptyView) { mEmptyView.scrollTo(-l, -t); } } @Override protected void updateUIForMode() { super.updateUIForMode(); // Check Indicator Views consistent with new Mode if (getShowIndicatorInternal()) { addIndicatorViews(); } else { removeIndicatorViews(); } } private void addIndicatorViews() { Mode mode = getMode(); FrameLayout refreshableViewWrapper = getRefreshableViewWrapper(); if (mode.showHeaderLoadingLayout() && null == mIndicatorIvTop) { // If the mode can pull down, and we don't have one set already mIndicatorIvTop = new IndicatorLayout(getContext(), Mode.PULL_FROM_START); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding); params.gravity = Gravity.TOP | Gravity.RIGHT; refreshableViewWrapper.addView(mIndicatorIvTop, params); } else if (!mode.showHeaderLoadingLayout() && null != mIndicatorIvTop) { // If we can't pull down, but have a View then remove it refreshableViewWrapper.removeView(mIndicatorIvTop); mIndicatorIvTop = null; } if (mode.showFooterLoadingLayout() && null == mIndicatorIvBottom) { // If the mode can pull down, and we don't have one set already mIndicatorIvBottom = new IndicatorLayout(getContext(), Mode.PULL_FROM_END); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.rightMargin = getResources().getDimensionPixelSize(R.dimen.indicator_right_padding); params.gravity = Gravity.BOTTOM | Gravity.RIGHT; refreshableViewWrapper.addView(mIndicatorIvBottom, params); } else if (!mode.showFooterLoadingLayout() && null != mIndicatorIvBottom) { // If we can't pull down, but have a View then remove it refreshableViewWrapper.removeView(mIndicatorIvBottom); mIndicatorIvBottom = null; } } private boolean getShowIndicatorInternal() { return mShowIndicator && isPullToRefreshEnabled(); } private boolean isFirstItemVisible() { final Adapter adapter = mRefreshableView.getAdapter(); if (null == adapter || adapter.isEmpty()) { if (DEBUG) { Log.d(LOG_TAG, "isFirstItemVisible. Empty View."); } return true; } else { /** * This check should really just be: * mRefreshableView.getFirstVisiblePosition() == 0, but PtRListView * internally use a HeaderView which messes the positions up. For * now we'll just add one to account for it and rely on the inner * condition which checks getTop(). */ if (mRefreshableView.getFirstVisiblePosition() <= 1) { final View firstVisibleChild = mRefreshableView.getChildAt(0); if (firstVisibleChild != null) { return firstVisibleChild.getTop() >= mRefreshableView.getTop(); } } } return false; } private boolean isLastItemVisible() { final Adapter adapter = mRefreshableView.getAdapter(); if (null == adapter || adapter.isEmpty()) { if (DEBUG) { Log.d(LOG_TAG, "isLastItemVisible. Empty View."); } return true; } else { final int lastItemPosition = mRefreshableView.getCount() - 1; final int lastVisiblePosition = mRefreshableView.getLastVisiblePosition(); if (DEBUG) { Log.d(LOG_TAG, "isLastItemVisible. Last Item Position: " + lastItemPosition + " Last Visible Pos: " + lastVisiblePosition); } /** * This check should really just be: lastVisiblePosition == * lastItemPosition, but PtRListView internally uses a FooterView * which messes the positions up. For me we'll just subtract one to * account for it and rely on the inner condition which checks * getBottom(). */ if (lastVisiblePosition >= lastItemPosition - 1) { final int childIndex = lastVisiblePosition - mRefreshableView.getFirstVisiblePosition(); final View lastVisibleChild = mRefreshableView.getChildAt(childIndex); if (lastVisibleChild != null) { return lastVisibleChild.getBottom() <= mRefreshableView.getBottom(); } } } return false; } private void removeIndicatorViews() { if (null != mIndicatorIvTop) { getRefreshableViewWrapper().removeView(mIndicatorIvTop); mIndicatorIvTop = null; } if (null != mIndicatorIvBottom) { getRefreshableViewWrapper().removeView(mIndicatorIvBottom); mIndicatorIvBottom = null; } } private void updateIndicatorViewsVisibility() { if (null != mIndicatorIvTop) { if (!isRefreshing() && isReadyForPullStart()) { if (!mIndicatorIvTop.isVisible()) { mIndicatorIvTop.show(); } } else { if (mIndicatorIvTop.isVisible()) { mIndicatorIvTop.hide(); } } } if (null != mIndicatorIvBottom) { if (!isRefreshing() && isReadyForPullEnd()) { if (!mIndicatorIvBottom.isVisible()) { mIndicatorIvBottom.show(); } } else { if (mIndicatorIvBottom.isVisible()) { mIndicatorIvBottom.hide(); } } } } public final void setSelection(int scrollTop) { mRefreshableView.setSelection(scrollTop); } public final int getFirstVisiblePosition() { return mRefreshableView.getFirstVisiblePosition(); } }其中PullToRefreshBase<T extends View>代码放在附件里面了;
大家想要实现上啦刷新下拉加载的功能只需要在第一个累中把泛型里面的换成自己想用的view即可:
使用方法如下:
lv_product.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener2<ListViewCompatProduct>() { @Override public void onPullDownToRefresh(PullToRefreshBase<ListViewCompatProduct> refreshView) { } @Override public void onPullUpToRefresh(PullToRefreshBase<ListViewCompatProduct> refreshView) { if (CommonUtils.getAPNType(AppApplication.getContext()) == 0) { // 没有网络 Toast.makeText(MyAttentionActivity.this,"断网了,请连接网络!",Toast.LENGTH_SHORT).show(); } else{ getData(uid,"3116",""+indexs,true); } } });上啦和下拉的方法都在上面,在请求加载完毕以后呢,需要使用onRefreshComplete()方法关闭刷新的视图,判断加载的数据有没有返回完整的数据,判断时候还还有多余的数据,假设每次加载都返回10条数据,举例如下:
if(myAttentions.getCmndity()!=null&&myAttentions.getCmndity().size()<10){ lv_product.setMode(PullToRefreshBase.Mode.DISABLED); if (indexs != 0) { Toast.makeText(MyAttentionActivity.this, "没有更多数据了", Toast.LENGTH_SHORT).show(); } }else{ lv_product.setMode(PullToRefreshBase.Mode.PULL_FROM_END); indexs++; }好啦,这就完成啦,写的不好大家不要喷我。。。谢谢