/*******************************************************************************
* 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.saikali.android_skwissh.widgets.pulltorefresh;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.saikali.android_skwissh.R;
import com.saikali.android_skwissh.widgets.pulltorefresh.internal.EmptyViewMethodAccessor;
import com.saikali.android_skwissh.widgets.pulltorefresh.internal.LoadingLayout;
public class PullToRefreshListView extends PullToRefreshAdapterViewBase<ListView> {
private LoadingLayout mHeaderLoadingView;
private LoadingLayout mFooterLoadingView;
private FrameLayout mLvFooterLoadingFrame;
public PullToRefreshListView(Context context) {
super(context);
this.setDisableScrollingWhileRefreshing(false);
}
public PullToRefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setDisableScrollingWhileRefreshing(false);
}
public PullToRefreshListView(Context context, Mode mode) {
super(context, mode);
this.setDisableScrollingWhileRefreshing(false);
}
@Override
public ContextMenuInfo getContextMenuInfo() {
return ((InternalListView) this.getRefreshableView()).getContextMenuInfo();
}
@Override
public void setPullLabel(String pullLabel, Mode mode) {
super.setPullLabel(pullLabel, mode);
if (null != this.mHeaderLoadingView && mode.canPullDown()) {
this.mHeaderLoadingView.setPullLabel(pullLabel);
}
if (null != this.mFooterLoadingView && mode.canPullUp()) {
this.mFooterLoadingView.setPullLabel(pullLabel);
}
}
@Override
public void setRefreshingLabel(String refreshingLabel, Mode mode) {
super.setRefreshingLabel(refreshingLabel, mode);
if (null != this.mHeaderLoadingView && mode.canPullDown()) {
this.mHeaderLoadingView.setRefreshingLabel(refreshingLabel);
}
if (null != this.mFooterLoadingView && mode.canPullUp()) {
this.mFooterLoadingView.setRefreshingLabel(refreshingLabel);
}
}
@Override
public void setReleaseLabel(String releaseLabel, Mode mode) {
super.setReleaseLabel(releaseLabel, mode);
if (null != this.mHeaderLoadingView && mode.canPullDown()) {
this.mHeaderLoadingView.setReleaseLabel(releaseLabel);
}
if (null != this.mFooterLoadingView && mode.canPullUp()) {
this.mFooterLoadingView.setReleaseLabel(releaseLabel);
}
}
protected ListView createListView(Context context, AttributeSet attrs) {
final ListView lv;
if (VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {
lv = new InternalListViewSDK9(context, attrs);
} else {
lv = new InternalListView(context, attrs);
}
return lv;
}
@Override
protected final ListView createRefreshableView(Context context, AttributeSet attrs) {
ListView lv = this.createListView(context, attrs);
// Get Styles from attrs
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PullToRefresh);
// Create Loading Views ready for use later
FrameLayout frame = new FrameLayout(context);
this.mHeaderLoadingView = this.createLoadingLayout(context, Mode.PULL_DOWN_TO_REFRESH, a);
frame.addView(this.mHeaderLoadingView, android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
this.mHeaderLoadingView.setVisibility(View.GONE);
lv.addHeaderView(frame, null, false);
this.mLvFooterLoadingFrame = new FrameLayout(context);
this.mFooterLoadingView = this.createLoadingLayout(context, Mode.PULL_UP_TO_REFRESH, a);
this.mLvFooterLoadingFrame.addView(this.mFooterLoadingView, android.view.ViewGroup.LayoutParams.MATCH_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
this.mFooterLoadingView.setVisibility(View.GONE);
a.recycle();
// Set it to this so it can be used in ListActivity/ListFragment
lv.setId(android.R.id.list);
return lv;
}
@Override
protected void resetHeader() {
// If we're not showing the Refreshing view, or the list is empty, then
// the header/footer views won't show so we use the
// normal method
ListAdapter adapter = this.mRefreshableView.getAdapter();
if (!this.getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) {
super.resetHeader();
return;
}
LoadingLayout originalLoadingLayout;
LoadingLayout listViewLoadingLayout;
int scrollToHeight = this.getHeaderHeight();
int selection;
boolean scroll;
switch (this.getCurrentMode()) {
case PULL_UP_TO_REFRESH:
originalLoadingLayout = this.getFooterLayout();
listViewLoadingLayout = this.mFooterLoadingView;
selection = this.mRefreshableView.getCount() - 1;
scroll = this.mRefreshableView.getLastVisiblePosition() == selection;
break;
case PULL_DOWN_TO_REFRESH:
default:
originalLoadingLayout = this.getHeaderLayout();
listViewLoadingLayout = this.mHeaderLoadingView;
scrollToHeight *= -1;
selection = 0;
scroll = this.mRefreshableView.getFirstVisiblePosition() == selection;
break;
}
// Set our Original View to Visible
originalLoadingLayout.setVisibility(View.VISIBLE);
/**
* Scroll so the View is at the same Y as the ListView header/footer,
* but only scroll if we've pulled to refresh and it's positioned
* correctly
*/
if (scroll && this.getState() != MANUAL_REFRESHING) {
this.mRefreshableView.setSelection(selection);
this.setHeaderScroll(scrollToHeight);
}
// Hide the ListView Header/Footer
listViewLoadingLayout.setVisibility(View.GONE);
super.resetHeader();
}
@Override
protected void setRefreshingInternal(boolean doScroll) {
// If we're not showing the Refreshing view, or the list is empty, then
// the header/footer views won't show so we use the
// normal method
ListAdapter adapter = this.mRefreshableView.getAdapter();
if (!this.getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) {
super.setRefreshingInternal(doScroll);
return;
}
super.setRefreshingInternal(false);
final LoadingLayout originalLoadingLayout, listViewLoadingLayout;
final int selection, scrollToY;
switch (this.getCurrentMode()) {
case PULL_UP_TO_REFRESH:
originalLoadingLayout = this.getFooterLayout();
listViewLoadingLayout = this.mFooterLoadingView;
selection = this.mRefreshableView.getCount() - 1;
scrollToY = this.getScrollY() - this.getHeaderHeight();
break;
case PULL_DOWN_TO_REFRESH:
default:
originalLoadingLayout = this.getHeaderLayout();
listViewLoadingLayout = this.mHeaderLoadingView;
selection = 0;
scrollToY = this.getScrollY() + this.getHeaderHeight();
break;
}
if (doScroll) {
// We scroll slightly so that the ListView's header/footer is at the
// same Y position as our normal header/footer
this.setHeaderScroll(scrollToY);
}
// Hide our original Loading View
originalLoadingLayout.setVisibility(View.INVISIBLE);
// Show the ListView Loading View and set it to refresh
listViewLoadingLayout.setVisibility(View.VISIBLE);
listViewLoadingLayout.refreshing();
if (doScroll) {
// Make sure the ListView is scrolled to show the loading
// header/footer
this.mRefreshableView.setSelection(selection);
// Smooth scroll as normal
this.smoothScrollTo(0);
}
}
protected class InternalListView extends ListView implements EmptyViewMethodAccessor {
private boolean mAddedLvFooter = false;
public InternalListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void draw(Canvas canvas) {
/**
* This is a bit hacky, but ListView has got a bug in it when using
* Header/Footer Views and the list is empty. This masks the issue
* so that it doesn't cause an FC. See Issue #66.
*/
try {
super.draw(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public ContextMenuInfo getContextMenuInfo() {
return super.getContextMenuInfo();
}
@Override
public void setAdapter(ListAdapter adapter) {
// Add the Footer View at the last possible moment
if (!this.mAddedLvFooter) {
this.addFooterView(PullToRefreshListView.this.mLvFooterLoadingFrame, null, false);
this.mAddedLvFooter = true;
}
super.setAdapter(adapter);
}
@Override
public void setEmptyView(View emptyView) {
PullToRefreshListView.this.setEmptyView(emptyView);
}
@Override
public void setEmptyViewInternal(View emptyView) {
super.setEmptyView(emptyView);
}
}
@TargetApi(9)
final class InternalListViewSDK9 extends InternalListView {
public InternalListViewSDK9(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(PullToRefreshListView.this, deltaY, scrollY, isTouchEvent);
return returnValue;
}
}
}