/******************************************************************************* * Copyright 2011, 2012 Chris Banes. * * 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.rincliu.library.widget.pulltorefresh; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.Adapter; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.FrameLayout; import android.widget.ListAdapter; import com.rincliu.library.widget.pulltorefresh.EmptyViewMethodAccessor; public abstract class PullToRefreshAdapterViewBase<T extends AbsListView> extends PullToRefreshBase<T> implements OnScrollListener { private int mSavedLastVisibleIndex = -1; private OnScrollListener mOnScrollListener; private OnLastItemVisibleListener mOnLastItemVisibleListener; private View mEmptyView; 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); } // abstract public ContextMenuInfo getContextMenuInfo(); public final void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount, final int totalItemCount) { // If we have a OnItemVisibleListener, do check... if (null != mOnLastItemVisibleListener) { // Detect whether the last visible item has changed final int lastVisibleItemIndex = firstVisibleItem + visibleItemCount; /** * Check that the last item has changed, we have any items, and * that the last item is visible. lastVisibleItemIndex is a * zero-based index, so we add one to it to check against * totalItemCount. */ if (visibleItemCount > 0 && (lastVisibleItemIndex + 1) >= totalItemCount) { if (lastVisibleItemIndex != mSavedLastVisibleIndex) { mSavedLastVisibleIndex = lastVisibleItemIndex; mOnLastItemVisibleListener.onLastItemVisible(); } } } // 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 scrollState) { if (null != mOnScrollListener) { mOnScrollListener.onScrollStateChanged(view, scrollState); } } /** * Pass-through method for {@link PullToRefreshBase#getRefreshableView() * getRefreshableView()}. * {@link AdapterView#setAdapter(android.widget.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 we already have an Empty View, remove it if (null != mEmptyView) { refreshableViewWrapper.removeView(mEmptyView); } 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); } refreshableViewWrapper.addView(newEmptyView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); } 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; } 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); } } private boolean isFirstItemVisible() { final Adapter adapter = mRefreshableView.getAdapter(); if (null == adapter || adapter.isEmpty()) { 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()) { return true; } else { final int lastItemPosition = mRefreshableView.getCount() - 1; final int lastVisiblePosition = mRefreshableView.getLastVisiblePosition(); /** * 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; } }