/*
* Copyright (C) 2013 47 Degrees, LLC
* http://47deg.com
* hello@47deg.com
*
* 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.tiger.quicknews.wedget.swiptlistview;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tiger.quicknews.R;
import java.util.List;
/**
* ListView subclass that provides the swipe functionality
*/
public class SwipeListView extends ListView implements OnScrollListener {
private boolean isDropDownStyle = true;
private boolean isOnBottomStyle = true;
private boolean isAutoLoadOnBottom = false;
private String headerDefaultText;
private String headerPullText;
private String headerReleaseText;
private String headerLoadingText;
private String footerDefaultText;
private String footerLoadingText;
private String footerNoMoreText;
private final Context context;
/** header layout view **/
private RelativeLayout headerLayout;
private ImageView headerImage;
private ProgressBar headerProgressBar;
private TextView headerText;
private TextView headerSecondText;
/** footer layout view **/
private RelativeLayout footerLayout;
private ProgressBar footerProgressBar;
private Button footerButton;
private OnDropDownListener onDropDownListener;
private OnScrollListener onScrollListener;
/** rate about drop down distance and header padding top when drop down **/
private float headerPaddingTopRate = 1.5f;
/** min distance which header can release to loading **/
private int headerReleaseMinDistance;
/** whether bottom listener has more **/
private boolean hasMore = true;
/** whether show footer loading progress bar when loading **/
private boolean isShowFooterProgressBar = true;
private int currentScrollState;
private int currentHeaderStatus;
/** whether reached top, when has reached top, don't show header layout **/
private boolean hasReachedTop = false;
/** image flip animation **/
private RotateAnimation flipAnimation;
/** image reverse flip animation **/
private RotateAnimation reverseFlipAnimation;
/** header layout original height **/
private int headerOriginalHeight;
/** header layout original padding top **/
private int headerOriginalTopPadding;
/** y of point which user touch down **/
private float actionDownPointY;
/** whether is on bottom loading **/
private boolean isOnBottomLoading = false;
/**
* Used when user want change swipe list mode on some rows
*/
public final static int SWIPE_MODE_DEFAULT = -1;
/**
* Disables all swipes
*/
public final static int SWIPE_MODE_NONE = 0;
/**
* Enables both left and right swipe
*/
public final static int SWIPE_MODE_BOTH = 1;
/**
* Enables right swipe
*/
public final static int SWIPE_MODE_RIGHT = 2;
/**
* Enables left swipe
*/
public final static int SWIPE_MODE_LEFT = 3;
/**
* Binds the swipe gesture to reveal a view behind the row (Drawer style)
*/
public final static int SWIPE_ACTION_REVEAL = 0;
/**
* Dismisses the cell when swiped over
*/
public final static int SWIPE_ACTION_DISMISS = 1;
/**
* Marks the cell as checked when swiped and release
*/
public final static int SWIPE_ACTION_CHOICE = 2;
/**
* No action when swiped
*/
public final static int SWIPE_ACTION_NONE = 3;
/**
* Default ids for front view
*/
public final static String SWIPE_DEFAULT_FRONT_VIEW = "swipelist_frontview";
/**
* Default id for back view
*/
public final static String SWIPE_DEFAULT_BACK_VIEW = "swipelist_backview";
/**
* Indicates no movement
*/
private final static int TOUCH_STATE_REST = 0;
/**
* State scrolling x position
*/
private final static int TOUCH_STATE_SCROLLING_X = 1;
/**
* State scrolling y position
*/
private final static int TOUCH_STATE_SCROLLING_Y = 2;
private int touchState = TOUCH_STATE_REST;
private float lastMotionX;
private float lastMotionY;
private int touchSlop;
int swipeFrontView = 0;
int swipeBackView = 0;
/**
* Internal listener for common swipe events
*/
private SwipeListViewListener swipeListViewListener;
/**
* Internal touch listener
*/
private SwipeListViewTouchListener touchListener;
/**
* If you create a View programmatically you need send back and front
* identifier
*
* @param context Context
* @param swipeBackView Back Identifier
* @param swipeFrontView Front Identifier
*/
public SwipeListView(Context context, int swipeBackView, int swipeFrontView) {
super(context);
this.context = context;
this.swipeFrontView = swipeFrontView;
this.swipeBackView = swipeBackView;
init(null);
}
/**
* @see android.widget.ListView#ListView(android.content.Context,
* android.util.AttributeSet)
*/
public SwipeListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(attrs);
}
/**
* @see android.widget.ListView#ListView(android.content.Context,
* android.util.AttributeSet, int)
*/
public SwipeListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
init(attrs);
}
/**
* Init ListView
*
* @param attrs AttributeSet
*/
private void init(AttributeSet attrs) {
int swipeMode = SWIPE_MODE_BOTH;
boolean swipeOpenOnLongPress = true;
boolean swipeCloseAllItemsWhenMoveList = true;
long swipeAnimationTime = 0;
float swipeOffsetLeft = 0;
float swipeOffsetRight = 0;
int swipeDrawableChecked = 0;
int swipeDrawableUnchecked = 0;
int swipeActionLeft = SWIPE_ACTION_REVEAL;
int swipeActionRight = SWIPE_ACTION_REVEAL;
if (attrs != null) {
TypedArray styled = getContext().obtainStyledAttributes(attrs,
R.styleable.SwipeListView);
swipeMode = styled.getInt(R.styleable.SwipeListView_swipeMode, SWIPE_MODE_BOTH);
swipeActionLeft = styled.getInt(R.styleable.SwipeListView_swipeActionLeft,
SWIPE_ACTION_REVEAL);
swipeActionRight = styled.getInt(R.styleable.SwipeListView_swipeActionRight,
SWIPE_ACTION_REVEAL);
swipeOffsetLeft = styled.getDimension(R.styleable.SwipeListView_swipeOffsetLeft, 0);
swipeOffsetRight = styled.getDimension(R.styleable.SwipeListView_swipeOffsetRight, 0);
swipeOpenOnLongPress = styled.getBoolean(
R.styleable.SwipeListView_swipeOpenOnLongPress, true);
swipeAnimationTime = styled.getInteger(R.styleable.SwipeListView_swipeAnimationTime, 0);
swipeCloseAllItemsWhenMoveList = styled.getBoolean(
R.styleable.SwipeListView_swipeCloseAllItemsWhenMoveList, true);
swipeDrawableChecked = styled.getResourceId(
R.styleable.SwipeListView_swipeDrawableChecked, 0);
swipeDrawableUnchecked = styled.getResourceId(
R.styleable.SwipeListView_swipeDrawableUnchecked, 0);
swipeFrontView = styled.getResourceId(R.styleable.SwipeListView_swipeFrontView, 0);
swipeBackView = styled.getResourceId(R.styleable.SwipeListView_swipeBackView, 0);
isDropDownStyle = styled.getBoolean(R.styleable.SwipeListView_swipeIsDropDownStyle,
false);
isOnBottomStyle = styled.getBoolean(R.styleable.SwipeListView_swipeIsOnBottomStyle,
false);
isAutoLoadOnBottom = styled.getBoolean(
R.styleable.SwipeListView_swipeIsAutoLoadOnBottom,
false);
}
initOnBottomStyle();
// should set, to run onScroll method and so on
super.setOnScrollListener(this);
if (swipeFrontView == 0 || swipeBackView == 0) {
swipeFrontView = getContext().getResources().getIdentifier(SWIPE_DEFAULT_FRONT_VIEW,
"id", getContext().getPackageName());
swipeBackView = getContext().getResources().getIdentifier(SWIPE_DEFAULT_BACK_VIEW,
"id", getContext().getPackageName());
if (swipeFrontView == 0 || swipeBackView == 0) {
throw new RuntimeException(
String.format(
"You forgot the attributes swipeFrontView or swipeBackView. You can add this attributes or use '%s' and '%s' identifiers",
SWIPE_DEFAULT_FRONT_VIEW, SWIPE_DEFAULT_BACK_VIEW));
}
}
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
touchListener = new SwipeListViewTouchListener(this, swipeFrontView, swipeBackView);
if (swipeAnimationTime > 0) {
touchListener.setAnimationTime(swipeAnimationTime);
}
touchListener.setRightOffset(swipeOffsetRight);
touchListener.setLeftOffset(swipeOffsetLeft);
touchListener.setSwipeActionLeft(swipeActionLeft);
touchListener.setSwipeActionRight(swipeActionRight);
touchListener.setSwipeMode(swipeMode);
touchListener.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList);
touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress);
touchListener.setSwipeDrawableChecked(swipeDrawableChecked);
touchListener.setSwipeDrawableUnchecked(swipeDrawableUnchecked);
setOnTouchListener(touchListener);
setOnScrollListener(touchListener.makeScrollListener());
}
/**
* init on bottom style, only init once
*/
private void initOnBottomStyle() {
if (footerLayout != null) {
if (isOnBottomStyle) {
addFooterView(footerLayout);
} else {
removeFooterView(footerLayout);
}
return;
}
if (!isOnBottomStyle) {
return;
}
footerDefaultText = context.getString(R.string.drop_down_list_footer_default_text);
footerLoadingText = context.getString(R.string.drop_down_list_footer_loading_text);
footerNoMoreText = context.getString(R.string.drop_down_list_footer_no_more_text);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
footerLayout = (RelativeLayout) inflater.inflate(R.layout.drop_down_list_footer, this,
false);
footerButton = (Button) footerLayout.findViewById(R.id.drop_down_list_footer_button);
footerButton.setDrawingCacheBackgroundColor(0);
footerButton.setEnabled(true);
footerProgressBar = (ProgressBar) footerLayout
.findViewById(R.id.drop_down_list_footer_progress_bar);
addFooterView(footerLayout);
}
/**
* @return isDropDownStyle
*/
public boolean isDropDownStyle() {
return isDropDownStyle;
}
/**
* @param isDropDownStyle
*/
public void setDropDownStyle(boolean isDropDownStyle) {
if (this.isDropDownStyle != isDropDownStyle) {
this.isDropDownStyle = isDropDownStyle;
}
}
/**
* @return isOnBottomStyle
*/
public boolean isOnBottomStyle() {
return isOnBottomStyle;
}
/**
* @param isOnBottomStyle
*/
public void setOnBottomStyle(boolean isOnBottomStyle) {
if (this.isOnBottomStyle != isOnBottomStyle) {
this.isOnBottomStyle = isOnBottomStyle;
initOnBottomStyle();
}
}
/**
* @return isAutoLoadOnBottom
*/
public boolean isAutoLoadOnBottom() {
return isAutoLoadOnBottom;
}
/**
* set whether auto load when on bottom
*
* @param isAutoLoadOnBottom
*/
public void setAutoLoadOnBottom(boolean isAutoLoadOnBottom) {
this.isAutoLoadOnBottom = isAutoLoadOnBottom;
}
/**
* get whether show footer loading progress bar when loading
*
* @return the isShowFooterProgressBar
*/
public boolean isShowFooterProgressBar() {
return isShowFooterProgressBar;
}
/**
* set whether show footer loading progress bar when loading
*
* @param isShowFooterProgressBar
*/
public void setShowFooterProgressBar(boolean isShowFooterProgressBar) {
this.isShowFooterProgressBar = isShowFooterProgressBar;
}
/**
* get footer button
*
* @return
*/
public Button getFooterButton() {
return footerButton;
}
/**
* Recycle cell. This method should be called from getView in Adapter when
* use SWIPE_ACTION_CHOICE
*
* @param convertView parent view
* @param position position in list
*/
public void recycle(View convertView, int position) {
touchListener.reloadChoiceStateInView(convertView.findViewById(swipeFrontView), position);
}
/**
* Get if item is selected
*
* @param position position in list
* @return
*/
public boolean isChecked(int position) {
return touchListener.isChecked(position);
}
/**
* Get positions selected
*
* @return
*/
public List<Integer> getPositionsSelected() {
return touchListener.getPositionsSelected();
}
/**
* Count selected
*
* @return
*/
public int getCountSelected() {
return touchListener.getCountSelected();
}
/**
* Unselected choice state in item
*/
public void unselectedChoiceStates() {
touchListener.unselectedChoiceStates();
}
/**
* @param onDropDownListener
*/
public void setOnDropDownListener(OnDropDownListener onDropDownListener) {
this.onDropDownListener = onDropDownListener;
}
/**
* @param onBottomListener
*/
public void setOnBottomListener(OnClickListener onBottomListener) {
footerButton.setOnClickListener(onBottomListener);
}
/**
* @see android.widget.ListView#setAdapter(android.widget.ListAdapter)
*/
@Override
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
if (isDropDownStyle) {
setSecondPositionVisible();
}
touchListener.resetItems();
adapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
onListChanged();
touchListener.resetItems();
}
});
}
/**
* Dismiss item
*
* @param position Position that you want open
*/
public void dismiss(int position) {
int height = touchListener.dismiss(position);
if (height > 0) {
touchListener.handlerPendingDismisses(height);
} else {
int[] dismissPositions = new int[1];
dismissPositions[0] = position;
onDismiss(dismissPositions);
touchListener.resetPendingDismisses();
}
}
/**
* Dismiss items selected
*/
public void dismissSelected() {
List<Integer> list = touchListener.getPositionsSelected();
int[] dismissPositions = new int[list.size()];
int height = 0;
for (int i = 0; i < list.size(); i++) {
int position = list.get(i);
dismissPositions[i] = position;
int auxHeight = touchListener.dismiss(position);
if (auxHeight > 0) {
height = auxHeight;
}
}
if (height > 0) {
touchListener.handlerPendingDismisses(height);
} else {
onDismiss(dismissPositions);
touchListener.resetPendingDismisses();
}
touchListener.returnOldActions();
}
/**
* Open ListView's item
*
* @param position Position that you want open
*/
public void openAnimate(int position) {
touchListener.openAnimate(position);
}
/**
* Close ListView's item
*
* @param position Position that you want open
*/
public void closeAnimate(int position) {
touchListener.closeAnimate(position);
}
/**
* Notifies onDismiss
*
* @param reverseSortedPositions All dismissed positions
*/
protected void onDismiss(int[] reverseSortedPositions) {
if (swipeListViewListener != null) {
swipeListViewListener.onDismiss(reverseSortedPositions);
}
}
/**
* Start open item
*
* @param position list item
* @param action current action
* @param right to right
*/
protected void onStartOpen(int position, int action, boolean right) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onStartOpen(position, action, right);
}
}
/**
* Start close item
*
* @param position list item
* @param right
*/
protected void onStartClose(int position, boolean right) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onStartClose(position, right);
}
}
/**
* Notifies onClickFrontView
*
* @param position item clicked
*/
protected void onClickFrontView(int position) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onClickFrontView(position);
}
}
/**
* Notifies onClickBackView
*
* @param position back item clicked
*/
protected void onClickBackView(int position) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onClickBackView(position);
}
}
/**
* Notifies onOpened
*
* @param position Item opened
* @param toRight If should be opened toward the right
*/
protected void onOpened(int position, boolean toRight) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onOpened(position, toRight);
}
}
/**
* Notifies onClosed
*
* @param position Item closed
* @param fromRight If open from right
*/
protected void onClosed(int position, boolean fromRight) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onClosed(position, fromRight);
}
}
/**
* Notifies onChoiceChanged
*
* @param position position that choice
* @param selected if item is selected or not
*/
protected void onChoiceChanged(int position, boolean selected) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onChoiceChanged(position, selected);
}
}
/**
* User start choice items
*/
protected void onChoiceStarted() {
if (swipeListViewListener != null) {
swipeListViewListener.onChoiceStarted();
}
}
/**
* User end choice items
*/
protected void onChoiceEnded() {
if (swipeListViewListener != null) {
swipeListViewListener.onChoiceEnded();
}
}
/**
* User is in first item of list
*/
protected void onFirstListItem() {
if (swipeListViewListener != null) {
swipeListViewListener.onFirstListItem();
}
}
/**
* User is in last item of list
*/
protected void onLastListItem() {
if (swipeListViewListener != null) {
swipeListViewListener.onLastListItem();
}
}
/**
* Notifies onListChanged
*/
protected void onListChanged() {
if (swipeListViewListener != null) {
swipeListViewListener.onListChanged();
}
}
/**
* Notifies onMove
*
* @param position Item moving
* @param x Current position
*/
protected void onMove(int position, float x) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
swipeListViewListener.onMove(position, x);
}
}
protected int changeSwipeMode(int position) {
if (swipeListViewListener != null && position != ListView.INVALID_POSITION) {
return swipeListViewListener.onChangeSwipeMode(position);
}
return SWIPE_MODE_DEFAULT;
}
/**
* Sets the Listener
*
* @param swipeListViewListener Listener
*/
public void setSwipeListViewListener(SwipeListViewListener swipeListViewListener) {
this.swipeListViewListener = swipeListViewListener;
}
/**
* Resets scrolling
*/
public void resetScrolling() {
touchState = TOUCH_STATE_REST;
}
/**
* Set offset on right
*
* @param offsetRight Offset
*/
public void setOffsetRight(float offsetRight) {
touchListener.setRightOffset(offsetRight);
}
/**
* Set offset on left
*
* @param offsetLeft Offset
*/
public void setOffsetLeft(float offsetLeft) {
touchListener.setLeftOffset(offsetLeft);
}
/**
* Set if all items opened will be closed when the user moves the ListView
*
* @param swipeCloseAllItemsWhenMoveList
*/
public void setSwipeCloseAllItemsWhenMoveList(boolean swipeCloseAllItemsWhenMoveList) {
touchListener.setSwipeClosesAllItemsWhenListMoves(swipeCloseAllItemsWhenMoveList);
}
/**
* Sets if the user can open an item with long pressing on cell
*
* @param swipeOpenOnLongPress
*/
public void setSwipeOpenOnLongPress(boolean swipeOpenOnLongPress) {
touchListener.setSwipeOpenOnLongPress(swipeOpenOnLongPress);
}
/**
* Set swipe mode
*
* @param swipeMode
*/
public void setSwipeMode(int swipeMode) {
touchListener.setSwipeMode(swipeMode);
}
/**
* Return action on left
*
* @return Action
*/
public int getSwipeActionLeft() {
return touchListener.getSwipeActionLeft();
}
/**
* Set action on left
*
* @param swipeActionLeft Action
*/
public void setSwipeActionLeft(int swipeActionLeft) {
touchListener.setSwipeActionLeft(swipeActionLeft);
}
/**
* Return action on right
*
* @return Action
*/
public int getSwipeActionRight() {
return touchListener.getSwipeActionRight();
}
/**
* Set action on right
*
* @param swipeActionRight Action
*/
public void setSwipeActionRight(int swipeActionRight) {
touchListener.setSwipeActionRight(swipeActionRight);
}
/**
* Sets animation time when user drops cell
*
* @param animationTime milliseconds
*/
public void setAnimationTime(long animationTime) {
touchListener.setAnimationTime(animationTime);
}
/**
* @see android.widget.ListView#onInterceptTouchEvent(android.view.MotionEvent)
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = MotionEventCompat.getActionMasked(ev);
final float x = ev.getX();
final float y = ev.getY();
if (isEnabled() && touchListener.isSwipeEnabled()) {
if (touchState == TOUCH_STATE_SCROLLING_X) {
return touchListener.onTouch(this, ev);
}
switch (action) {
case MotionEvent.ACTION_MOVE:
checkInMoving(x, y);
return touchState == TOUCH_STATE_SCROLLING_Y;
case MotionEvent.ACTION_DOWN:
touchListener.onTouch(this, ev);
touchState = TOUCH_STATE_REST;
lastMotionX = x;
lastMotionY = y;
return false;
case MotionEvent.ACTION_CANCEL:
touchState = TOUCH_STATE_REST;
break;
case MotionEvent.ACTION_UP:
touchListener.onTouch(this, ev);
return touchState == TOUCH_STATE_SCROLLING_Y;
default:
break;
}
}
return super.onInterceptTouchEvent(ev);
}
/**
* Check if the user is moving the cell
*
* @param x Position X
* @param y Position Y
*/
private void checkInMoving(float x, float y) {
final int xDiff = (int) Math.abs(x - lastMotionX);
final int yDiff = (int) Math.abs(y - lastMotionY);
final int touchSlop = this.touchSlop;
boolean xMoved = xDiff > touchSlop;
boolean yMoved = yDiff > touchSlop;
if (xMoved) {
touchState = TOUCH_STATE_SCROLLING_X;
lastMotionX = x;
lastMotionY = y;
}
if (yMoved) {
touchState = TOUCH_STATE_SCROLLING_Y;
lastMotionX = x;
lastMotionY = y;
}
}
/**
* Close all opened items
*/
public void closeOpenedItems() {
touchListener.closeOpenedItems();
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (isDropDownStyle) {
currentScrollState = scrollState;
if (currentScrollState == SCROLL_STATE_IDLE) {
hasReachedTop = false;
}
}
if (onScrollListener != null) {
onScrollListener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if (isDropDownStyle) {
if (currentScrollState == SCROLL_STATE_TOUCH_SCROLL
&& currentHeaderStatus != HEADER_STATUS_LOADING) {
/**
* when state of ListView is SCROLL_STATE_TOUCH_SCROLL(ListView
* is scrolling and finger is on screen) and header status is
* not HEADER_STATUS_LOADING
* <ul>
* if header layout is visiable,
* <li>if height of header is higher than a fixed value, then
* set header status to HEADER_STATUS_RELEASE_TO_LOAD.</li>
* <li>else set header status to
* HEADER_STATUS_DROP_DOWN_TO_LOAD.</li>
* </ul>
* <ul>
* if header layout is not visiable,
* <li>set header status to HEADER_STATUS_CLICK_TO_LOAD.</li>
* </ul>
*/
if (firstVisibleItem == 0) {
headerImage.setVisibility(View.VISIBLE);
int pointBottom = headerOriginalHeight + headerReleaseMinDistance;
if (headerLayout.getBottom() >= pointBottom) {
setHeaderStatusReleaseToLoad();
} else if (headerLayout.getBottom() < pointBottom) {
setHeaderStatusDropDownToLoad();
}
} else {
setHeaderStatusClickToLoad();
}
} else if (currentScrollState == SCROLL_STATE_FLING && firstVisibleItem == 0
&& currentHeaderStatus != HEADER_STATUS_LOADING) {
/**
* when state of ListView is SCROLL_STATE_FLING(ListView is
* scrolling but finger has leave screen) and first item(header
* layout) is visible and header status is not
* HEADER_STATUS_LOADING, then hide first item, set second item
* visible and set hasReachedTop true.
*/
setSecondPositionVisible();
hasReachedTop = true;
} else if (currentScrollState == SCROLL_STATE_FLING && hasReachedTop) {
/**
* when state of ListView is SCROLL_STATE_FLING(ListView is
* scrolling but finger has leave screen) and hasReachedTop is
* true(it's because flick back), then hide first item, set
* second item visible.
*/
setSecondPositionVisible();
}
}
// if isOnBottomStyle and isAutoLoadOnBottom and hasMore, then call
// onBottom function auto
if (isOnBottomStyle && isAutoLoadOnBottom && hasMore) {
if (firstVisibleItem > 0 && totalItemCount > 0
&& (firstVisibleItem + visibleItemCount == totalItemCount)) {
onBottom();
}
}
if (onScrollListener != null) {
onScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
}
@Override
public void setOnScrollListener(OnScrollListener l) {
// TODO Auto-generated method stub
onScrollListener = l;
}
/**
* drop down begin, adjust view status
*/
private void onDropDownBegin() {
if (isDropDownStyle) {
setHeaderStatusLoading();
}
}
/**
* on drop down loading, you can call it by manual, but you should manual
* call onBottomComplete at the same time.
*/
public void onDropDown() {
if (currentHeaderStatus != HEADER_STATUS_LOADING && isDropDownStyle
&& onDropDownListener != null) {
onDropDownBegin();
onDropDownListener.onDropDown();
}
}
/**
* drop down complete, restore view status
*
* @param secondText display below header text, if null, not display
*/
public void onDropDownComplete(CharSequence secondText) {
if (isDropDownStyle) {
setHeaderSecondText(secondText);
onDropDownComplete();
}
}
/**
* set header second text
*
* @param secondText secondText display below header text, if null, not
* display
*/
public void setHeaderSecondText(CharSequence secondText) {
if (isDropDownStyle) {
if (secondText == null) {
headerSecondText.setVisibility(View.GONE);
} else {
headerSecondText.setVisibility(View.VISIBLE);
headerSecondText.setText(secondText);
}
}
}
/**
* drop down complete, restore view status
*/
public void onDropDownComplete() {
if (isDropDownStyle) {
setHeaderStatusClickToLoad();
if (headerLayout.getBottom() > 0) {
invalidateViews();
setSecondPositionVisible();
}
}
}
/**
* on bottom begin, adjust view status
*/
private void onBottomBegin() {
if (isOnBottomStyle) {
if (isShowFooterProgressBar) {
footerProgressBar.setVisibility(View.VISIBLE);
}
footerButton.setText(footerLoadingText);
footerButton.setEnabled(false);
}
}
/**
* on bottom loading, you can call it by manual, but you should manual call
* onBottomComplete at the same time.
*/
public void onBottom() {
if (isOnBottomStyle && !isOnBottomLoading) {
isOnBottomLoading = true;
onBottomBegin();
footerButton.performClick();
}
}
/**
* on bottom load complete, restore view status
*/
public void onBottomComplete() {
if (isOnBottomStyle) {
if (isShowFooterProgressBar) {
footerProgressBar.setVisibility(View.GONE);
}
footerButton.setEnabled(true);
if (!hasMore) {
footerButton.setText(footerNoMoreText);
} else {
footerButton.setText(footerDefaultText);
}
isOnBottomLoading = false;
}
}
/**
* OnDropDownListener, called when header released
*
* @author <a href="http://www.trinea.cn" target="_blank">Trinea</a>
* 2012-5-31
*/
public interface OnDropDownListener {
/**
* called when header released
*/
public void onDropDown();
}
/**
* set second position visible(index is 1), because first position is header
* layout
*/
public void setSecondPositionVisible() {
if (getAdapter() != null && getAdapter().getCount() > 0 && getFirstVisiblePosition() == 0) {
setSelection(1);
}
}
/**
* set whether has more. if hasMore is false, onBottm will not be called
* when listView scroll to bottom
*
* @param hasMore
*/
public void setHasMore(boolean hasMore) {
this.hasMore = hasMore;
}
/**
* get whether has more
*
* @return
*/
public boolean isHasMore() {
return hasMore;
}
/**
* get header layout view
*
* @return
*/
public RelativeLayout getHeaderLayout() {
return headerLayout;
}
/**
* get footer layout view
*
* @return
*/
public RelativeLayout getFooterLayout() {
return footerLayout;
}
/**
* get rate about drop down distance and header padding top when drop down
*
* @return headerPaddingTopRate
*/
public float getHeaderPaddingTopRate() {
return headerPaddingTopRate;
}
/**
* set rate about drop down distance and header padding top when drop down
*
* @param headerPaddingTopRate
*/
public void setHeaderPaddingTopRate(float headerPaddingTopRate) {
this.headerPaddingTopRate = headerPaddingTopRate;
}
/**
* get min distance which header can release to loading
*
* @return headerReleaseMinDistance
*/
public int getHeaderReleaseMinDistance() {
return headerReleaseMinDistance;
}
/**
* set min distance which header can release to loading
*
* @param headerReleaseMinDistance
*/
public void setHeaderReleaseMinDistance(int headerReleaseMinDistance) {
this.headerReleaseMinDistance = headerReleaseMinDistance;
}
/***
* get header default text, default is
* R.string.drop_down_list_header_default_text
*
* @return
*/
public String getHeaderDefaultText() {
return headerDefaultText;
}
/**
* set header default text, default is
* R.string.drop_down_list_header_default_text
*
* @param headerDefaultText
*/
public void setHeaderDefaultText(String headerDefaultText) {
this.headerDefaultText = headerDefaultText;
if (headerText != null && currentHeaderStatus == HEADER_STATUS_CLICK_TO_LOAD) {
headerText.setText(headerDefaultText);
}
}
/**
* get header pull text, default is R.string.drop_down_list_header_pull_text
*
* @return
*/
public String getHeaderPullText() {
return headerPullText;
}
/**
* set header pull text, default is R.string.drop_down_list_header_pull_text
*
* @param headerPullText
*/
public void setHeaderPullText(String headerPullText) {
this.headerPullText = headerPullText;
}
/**
* get header release text, default is
* R.string.drop_down_list_header_release_text
*
* @return
*/
public String getHeaderReleaseText() {
return headerReleaseText;
}
/**
* set header release text, default is
* R.string.drop_down_list_header_release_text
*
* @param headerReleaseText
*/
public void setHeaderReleaseText(String headerReleaseText) {
this.headerReleaseText = headerReleaseText;
}
/**
* get header loading text, default is
* R.string.drop_down_list_header_loading_text
*
* @return
*/
public String getHeaderLoadingText() {
return headerLoadingText;
}
/**
* set header loading text, default is
* R.string.drop_down_list_header_loading_text
*
* @param headerLoadingText
*/
public void setHeaderLoadingText(String headerLoadingText) {
this.headerLoadingText = headerLoadingText;
}
/**
* get footer default text, default is
* R.string.drop_down_list_footer_default_text
*
* @return
*/
public String getFooterDefaultText() {
return footerDefaultText;
}
/**
* set footer default text, default is
* R.string.drop_down_list_footer_default_text
*
* @param footerDefaultText
*/
public void setFooterDefaultText(String footerDefaultText) {
this.footerDefaultText = footerDefaultText;
if (footerButton != null && footerButton.isEnabled()) {
footerButton.setText(footerDefaultText);
}
}
/**
* get footer loading text, default is
* R.string.drop_down_list_footer_loading_text
*
* @return
*/
public String getFooterLoadingText() {
return footerLoadingText;
}
/**
* set footer loading text, default is
* R.string.drop_down_list_footer_loading_text
*
* @param footerLoadingText
*/
public void setFooterLoadingText(String footerLoadingText) {
this.footerLoadingText = footerLoadingText;
}
/**
* get footer no more text, default is
* R.string.drop_down_list_footer_no_more_text
*
* @return
*/
public String getFooterNoMoreText() {
return footerNoMoreText;
}
/**
* set footer no more text, default is
* R.string.drop_down_list_footer_no_more_text
*
* @param footerNoMoreText
*/
public void setFooterNoMoreText(String footerNoMoreText) {
this.footerNoMoreText = footerNoMoreText;
}
/** status which you can click to load, init satus **/
public static final int HEADER_STATUS_CLICK_TO_LOAD = 1;
/**
* status which you can drop down and then release to excute
* onDropDownListener, when height of header layout lower than a value
**/
public static final int HEADER_STATUS_DROP_DOWN_TO_LOAD = 2;
/**
* status which you can release to excute onDropDownListener, when height of
* header layout higher than a value
**/
public static final int HEADER_STATUS_RELEASE_TO_LOAD = 3;
/** status which is loading **/
public static final int HEADER_STATUS_LOADING = 4;
/**
* set header status to {@link #HEADER_STATUS_CLICK_TO_LOAD}
*/
private void setHeaderStatusClickToLoad() {
if (currentHeaderStatus != HEADER_STATUS_CLICK_TO_LOAD) {
resetHeaderPadding();
headerImage.clearAnimation();
headerImage.setVisibility(View.GONE);
headerProgressBar.setVisibility(View.GONE);
headerText.setText(headerDefaultText);
currentHeaderStatus = HEADER_STATUS_CLICK_TO_LOAD;
}
}
/**
* set header status to {@link #HEADER_STATUS_DROP_DOWN_TO_LOAD}
*/
private void setHeaderStatusDropDownToLoad() {
if (currentHeaderStatus != HEADER_STATUS_DROP_DOWN_TO_LOAD) {
headerImage.setVisibility(View.VISIBLE);
if (currentHeaderStatus != HEADER_STATUS_CLICK_TO_LOAD) {
headerImage.clearAnimation();
headerImage.startAnimation(reverseFlipAnimation);
}
headerProgressBar.setVisibility(View.GONE);
headerText.setText(headerPullText);
if (isVerticalFadingEdgeEnabled()) {
setVerticalScrollBarEnabled(false);
}
currentHeaderStatus = HEADER_STATUS_DROP_DOWN_TO_LOAD;
}
}
/**
* set header status to {@link #HEADER_STATUS_RELEASE_TO_LOAD}
*/
private void setHeaderStatusReleaseToLoad() {
if (currentHeaderStatus != HEADER_STATUS_RELEASE_TO_LOAD) {
headerImage.setVisibility(View.VISIBLE);
headerImage.clearAnimation();
headerImage.startAnimation(flipAnimation);
headerProgressBar.setVisibility(View.GONE);
headerText.setText(headerReleaseText);
currentHeaderStatus = HEADER_STATUS_RELEASE_TO_LOAD;
}
}
/**
* set header status to {@link #HEADER_STATUS_LOADING}
*/
private void setHeaderStatusLoading() {
if (currentHeaderStatus != HEADER_STATUS_LOADING) {
resetHeaderPadding();
headerImage.setVisibility(View.GONE);
headerImage.clearAnimation();
headerProgressBar.setVisibility(View.VISIBLE);
headerText.setText(headerLoadingText);
currentHeaderStatus = HEADER_STATUS_LOADING;
setSelection(0);
}
}
/**
* adjust header padding according to motion event
*
* @param ev
*/
private void adjustHeaderPadding(MotionEvent ev) {
// adjust header padding according to motion event history
int pointerCount = ev.getHistorySize();
if (isVerticalFadingEdgeEnabled()) {
setVerticalScrollBarEnabled(false);
}
for (int i = 0; i < pointerCount; i++) {
if (currentHeaderStatus == HEADER_STATUS_DROP_DOWN_TO_LOAD
|| currentHeaderStatus == HEADER_STATUS_RELEASE_TO_LOAD) {
headerLayout
.setPadding(
headerLayout.getPaddingLeft(),
(int) (((ev.getHistoricalY(i) - actionDownPointY) - headerOriginalHeight) / headerPaddingTopRate),
headerLayout.getPaddingRight(), headerLayout.getPaddingBottom());
}
}
}
/**
* reset header padding
*/
private void resetHeaderPadding() {
headerLayout.setPadding(headerLayout.getPaddingLeft(), headerOriginalTopPadding,
headerLayout.getPaddingRight(), headerLayout.getPaddingBottom());
}
/**
* measure header layout
*
* @param child
*/
private void measureHeaderLayout(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isDropDownStyle) {
return super.onTouchEvent(event);
}
hasReachedTop = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
actionDownPointY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
adjustHeaderPadding(event);
break;
case MotionEvent.ACTION_UP:
if (!isVerticalScrollBarEnabled()) {
setVerticalScrollBarEnabled(true);
}
/**
* set status when finger leave screen if first item visible and
* header status is not HEADER_STATUS_LOADING
* <ul>
* <li>if current header status is
* HEADER_STATUS_RELEASE_TO_LOAD, call onDropDown.</li>
* <li>if current header status is
* HEADER_STATUS_DROP_DOWN_TO_LOAD, then set header status to
* HEADER_STATUS_CLICK_TO_LOAD and hide header layout.</li>
* </ul>
*/
if (getFirstVisiblePosition() == 0 && currentHeaderStatus != HEADER_STATUS_LOADING) {
switch (currentHeaderStatus) {
case HEADER_STATUS_RELEASE_TO_LOAD:
onDropDown();
break;
case HEADER_STATUS_DROP_DOWN_TO_LOAD:
setHeaderStatusClickToLoad();
setSecondPositionVisible();
break;
case HEADER_STATUS_CLICK_TO_LOAD:
default:
break;
}
}
break;
}
return super.onTouchEvent(event);
}
}