com.zy.booking.wedget.swiptlistview.SwipeListView.java Source code

Java tutorial

Introduction

Here is the source code for com.zy.booking.wedget.swiptlistview.SwipeListView.java

Source

/*
 * 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.zy.booking.wedget.swiptlistview;

import java.util.List;

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.View.MeasureSpec;
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.zy.booking.R;

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