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