com.qianjiang.framework.widget.pulltorefresh.PullToRefreshListView.java Source code

Java tutorial

Introduction

Here is the source code for com.qianjiang.framework.widget.pulltorefresh.PullToRefreshListView.java

Source

package com.qianjiang.framework.widget.pulltorefresh;

/*******************************************************************************
 * 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.
 *******************************************************************************/

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.ListView;

import com.qianjiang.framework.R;

/**
 * ListView
 * 
 * 
 * @author wang.xy
 * @since 2012-8-14
 * @version 2012-8-14??<br>
 *          2012-09-29  ??<br>
 *          2012-10/31 ?? ??<br>
 *          2012-11-09  ?setHeaderVisible?<br>
 *          2014-02-06  ?setHeaderVisible<br>
 * 
 */
public class PullToRefreshListView extends PullToRefreshAdapterViewBase<ListView> {

    // listview headerview?
    private boolean mListViewHasShow;
    private HeaderLoadingLayout mHeaderLoadingView;
    private FooterLoadingLayout mFooterLoadingView;

    private FrameLayout mLvFooterLoadingFrame;
    // ?
    private boolean mIsOnclick;

    /**
     * 
     * 
     * @param context
     *            ?
     */
    public PullToRefreshListView(Context context) {
        super(context);
        setDisableScrollingWhileRefreshing(false);
    }

    /**
     * 
     * 
     * @param context
     *            ?
     * @param attrs
     *            
     */
    public PullToRefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setDisableScrollingWhileRefreshing(false);
    }

    /**
     * 
     * 
     * @param context
     *            ?
     * @param mode
     *            ?
     */
    public PullToRefreshListView(Context context, Mode mode) {
        super(context, mode);
        setDisableScrollingWhileRefreshing(false);
    }

    @Override
    public ContextMenuInfo getContextMenuInfo() {
        return ((InternalListView) getRefreshableView()).getContextMenuInfo();
    }

    @Override
    public void setHeaderLabel(CharSequence label) {
        super.setHeaderLabel(label);

        if (null != mHeaderLoadingView && getMode().canPullDown()) {
            mHeaderLoadingView.setSubHeaderText(label);
        }

        refreshLoadingViewsHeight();
    };

    public boolean isOnclick() {
        return mIsOnclick;
    }

    /**
     * ?
     * 
     * @param pullLabel
     *            ?
     * @param mode
     *            ?
     */
    @Override
    public void setPullLabel(String pullLabel, Mode mode) {
        super.setPullLabel(pullLabel, mode);

        if (null != mHeaderLoadingView && mode.canPullDown()) {
            mHeaderLoadingView.setPullLabel(pullLabel);
        }
        // if (null != mFooterLoadingView && mode.canPullUp()) {
        // // mFooterLoadingView.setPullLabel(pullLabel);
        // }
    }

    /**
     * ?
     * 
     * @param refreshingLabel
     *            ?
     * @param mode
     *            ?
     */
    @Override
    public void setRefreshingLabel(String refreshingLabel, Mode mode) {
        super.setRefreshingLabel(refreshingLabel, mode);

        if (null != mHeaderLoadingView && mode.canPullDown()) {
            mHeaderLoadingView.setRefreshingLabel(refreshingLabel);
        }
        if (null != mFooterLoadingView && mode.canPullUp()) {
            mFooterLoadingView.setRefreshingLabel(refreshingLabel);
        }
    }

    /**
     * ?
     * 
     * @param releaseLabel
     *            ?
     * @param mode
     *            ?
     */
    @Override
    public void setReleaseLabel(String releaseLabel, Mode mode) {
        super.setReleaseLabel(releaseLabel, mode);

        if (null != mHeaderLoadingView && mode.canPullDown()) {
            mHeaderLoadingView.setReleaseLabel(releaseLabel);
        }
        if (null != mFooterLoadingView && mode.canPullUp()) {
            mFooterLoadingView.setReleaseLabel(releaseLabel);
        }
    }

    @Override
    protected final ListView createRefreshableView(Context context, AttributeSet attrs) {
        ListView lv = new InternalListView(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);
        mHeaderLoadingView = new HeaderLoadingLayout(context, Mode.PULL_DOWN_TO_REFRESH, a);
        frame.addView(mHeaderLoadingView, android.view.ViewGroup.LayoutParams.FILL_PARENT,
                android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
        mHeaderLoadingView.setVisibility(View.GONE);
        lv.addHeaderView(frame, null, false);

        mLvFooterLoadingFrame = new FrameLayout(context);
        mFooterLoadingView = new FooterLoadingLayout(context, Mode.PULL_UP_TO_REFRESH, a);
        mLvFooterLoadingFrame.addView(mFooterLoadingView, android.view.ViewGroup.LayoutParams.FILL_PARENT,
                android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
        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 int getNumberInternalFooterViews() {
        return null != mFooterLoadingView ? 1 : 0;
    }

    @Override
    protected int getNumberInternalHeaderViews() {
        return null != mHeaderLoadingView ? 1 : 0;
    }

    @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 = mRefreshableView.getAdapter();
        if (!mListViewHasShow) {
            // headerView
            if (!getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) {
                super.resetHeader();
                return;
            }
        }

        HeaderLoadingLayout originalLoadingLayout = null;
        HeaderLoadingLayout listViewLoadingLayout = null;
        FooterLoadingLayout originalFooterLoadingLayout = null;
        FooterLoadingLayout listViewFooterLoadingLayout = null;

        int scrollToHeight = getHeaderHeight();
        int selection;
        boolean scroll;

        switch (getCurrentMode()) {
        case PULL_UP_TO_REFRESH:
            originalFooterLoadingLayout = getFooterLayout();
            listViewFooterLoadingLayout = mFooterLoadingView;
            selection = mRefreshableView.getCount() - 1;
            scroll = mRefreshableView.getLastVisiblePosition() == selection;
            break;
        case PULL_DOWN_TO_REFRESH:
        default:
            originalLoadingLayout = getHeaderLayout();
            listViewLoadingLayout = mHeaderLoadingView;
            scrollToHeight *= -1;
            selection = 0;
            scroll = mRefreshableView.getFirstVisiblePosition() == selection;
            break;
        }

        // Set our Original View to Visible
        if (originalLoadingLayout != null) {
            originalLoadingLayout.setVisibility(View.VISIBLE);
        } else {
            originalFooterLoadingLayout.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 && getState() != MANUAL_REFRESHING) {
            mRefreshableView.setSelection(selection);
            setHeaderScroll(scrollToHeight);
        }

        // Hide the ListView Header/Footer
        if (listViewLoadingLayout != null) {
            listViewLoadingLayout.setVisibility(View.GONE);
            setTimeLineTopPadding(0);
        } else {
            listViewFooterLoadingLayout.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 = mRefreshableView.getAdapter();
        if (!getShowViewWhileRefreshing() || null == adapter || adapter.isEmpty()) {
            super.setRefreshingInternal(doScroll);
            return;
        }
        super.setRefreshingInternal(false);
        HeaderLoadingLayout originalLoadingLayout = null;
        HeaderLoadingLayout listViewLoadingLayout = null;
        FooterLoadingLayout originalFooterLoadingLayout = null;
        FooterLoadingLayout listViewFooterLoadingLayout = null;
        final int selection;
        final int scrollToY;
        switch (getCurrentMode()) {
        case PULL_UP_TO_REFRESH:
            originalFooterLoadingLayout = getFooterLayout();
            listViewFooterLoadingLayout = mFooterLoadingView;
            selection = mRefreshableView.getCount() - 1;
            scrollToY = getScrollY() - getFooterHeight();
            break;
        case PULL_DOWN_TO_REFRESH:
        default:
            originalLoadingLayout = getHeaderLayout();
            listViewLoadingLayout = mHeaderLoadingView;
            selection = 0;
            scrollToY = getScrollY() + 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
            setHeaderScroll(scrollToY);
        }
        // ?
        if (originalFooterLoadingLayout != null && originalFooterLoadingLayout.HasMoreData) {
            // Set our Original View to Visible
            if (originalLoadingLayout != null) {
                originalLoadingLayout.setVisibility(View.INVISIBLE);
            } else {
                originalFooterLoadingLayout.setVisibility(View.INVISIBLE);
            }
            // Hide the ListView Header/Footer
            if (listViewLoadingLayout != null) {
                listViewLoadingLayout.setVisibility(View.VISIBLE);
                listViewLoadingLayout.measure(0, 0);
                setTimeLineTopPadding(listViewLoadingLayout.getMeasuredHeight());
                mListViewHasShow = true;
                listViewLoadingLayout.refreshing();
            } else {
                listViewFooterLoadingLayout.setVisibility(View.VISIBLE);
                listViewFooterLoadingLayout.refreshing();
            }
        } else {
            // ? Set our Original View to Visible
            if (originalLoadingLayout != null) {
                originalLoadingLayout.setVisibility(View.INVISIBLE);
            } else {
                // FooterLoadingLayout
                originalFooterLoadingLayout.setVisibility(View.VISIBLE);
            }
            // Hide the ListView Header/Footer
            if (listViewLoadingLayout != null) {
                mListViewHasShow = true;
                listViewLoadingLayout.setVisibility(View.VISIBLE);
                listViewLoadingLayout.measure(0, 0);
                setTimeLineTopPadding(listViewLoadingLayout.getMeasuredHeight());
                listViewLoadingLayout.refreshing();
            } else {
                // ?? listViewFooter FooterLoadingLayout
                listViewFooterLoadingLayout.setVisibility(View.GONE);
                originalFooterLoadingLayout.refreshing();
            }
        }
        if (doScroll) {
            // Make sure the ListView is scrolled to show the loading
            // header/footer
            mRefreshableView.setSelection(selection);
            // Smooth scroll as normal
            smoothScrollTo(0);
        }
    }

    @Override
    protected void setHeaderVisible() {

        if (mIsFisrtHeaderShowing || getItemCount() <= 0) {
            super.setHeaderVisible();
        } else {
            // ???
            setTimeTask();
            // ?
            // ????listview
            HeaderLoadingLayout originalLoadingLayout = null;
            HeaderLoadingLayout listViewLoadingLayout = null;
            originalLoadingLayout = getHeaderLayout();
            listViewLoadingLayout = mHeaderLoadingView;
            // Scroll  listviewheaderview

            setHeaderScroll(0);
            // Set our Original View to Visible
            if (originalLoadingLayout != null) {
                originalLoadingLayout.setVisibility(View.INVISIBLE);
            }
            // lisetview headerview
            if (listViewLoadingLayout != null) {
                mListViewHasShow = true;
                listViewLoadingLayout.setVisibility(View.VISIBLE);
                listViewLoadingLayout.refreshing();
            }
        }
    }

    class InternalListView extends ListView implements EmptyViewMethodAccessor {

        private boolean mAddedLvFooter;
        private int mDefaultXDistance;
        private int mDefaultYDistance;
        /** ???? */
        private float mStartX = 0;
        private float mStartY = 0;
        /** ?Y */
        private int touchSlop = 0;
        private float mRate = 1;

        public InternalListView(Context context, AttributeSet attrs) {
            super(context, attrs);

            final ViewConfiguration configuration = ViewConfiguration.get(getContext());
            touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);
            mDefaultXDistance = ViewConfiguration.getMinimumFlingVelocity();

            // ViewConfiguration viewConfiguration =
            // ViewConfiguration.get(context);
            // mDefaultXDistance = viewConfiguration.getMinimumFlingVelocity();
            // mDefaultYDistance = mDefaultXDistance / 2;
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStartX = (int) ev.getX();
                mStartY = (int) ev.getY();
                // Log.d("xx", "===============down===");
                break;
            case MotionEvent.ACTION_UP:
                if (!isMovingInX(ev.getX())) {
                    mIsOnclick = true;
                    // return false;
                }
                mIsOnclick = false;
                break;
            case MotionEvent.ACTION_MOVE:
                int stopX = (int) ev.getX();
                int stopY = (int) ev.getY();
                // Log.d("xx", "===============isMovingInX==="
                // + isMovingInX(stopX) + "   " + lastMotionX +"   " +
                // mDefaultXDistance / 5 );
                // ???
                if (!isMovingInY(stopX, stopY)) {
                    return false;
                }

            default:
                break;
            }
            return super.onInterceptTouchEvent(ev);
        }

        /**
         * ?
         * 
         * @param x
         *            Position X
         * @param y
         *            Position Y
         */
        private boolean isMovingInY(float x, float y) {
            final int xDiff = (int) Math.abs(x - mStartX);
            final int yDiff = (int) Math.abs(y - mStartY);
            final int touchSlop = this.touchSlop;
            if (xDiff == 0) {
                return true;
            }
            if ((yDiff / xDiff >= 1) && (yDiff > touchSlop)) {
                return true;
            }
            return false;
        }

        /**
         * isMovingInX?
         * 
         * @param x
         * @param y
         * @return boolean
         * @exception
         * @since 1.0.0
         */
        private boolean isMovingInX(float x) {
            final int xDiff = (int) Math.abs(x - mStartX);
            if (xDiff >= mDefaultXDistance / 2) {
                return true;
            }
            return false;
        }

        private boolean isMovingHorizontal(int startX, int startY, int stopX, int stopY) {
            int xDiff = stopX - startX;
            int yDiff = Math.abs(stopY - startY);
            boolean conditionOne = xDiff >= mDefaultXDistance && yDiff < mDefaultYDistance;
            boolean conditionTwo = yDiff >= mDefaultYDistance && xDiff < -mDefaultXDistance
                    && Math.abs(xDiff) / yDiff > mRate;
            if (conditionOne || conditionTwo) {
                return true;
            }
            return false;
        }

        @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 (!mAddedLvFooter && mIsFooterViewEnabled) {
                addFooterView(mLvFooterLoadingFrame, null, false);
                mAddedLvFooter = true;
            }
            if (mIsShowHeaderFresh) {
                setHeaderVisible(false);
            }
            super.setAdapter(adapter);
        }

        @Override
        public void setEmptyView(View emptyView) {
            PullToRefreshListView.this.setEmptyView(emptyView);
        }

        @Override
        public View getEmptyView() {
            return PullToRefreshListView.this.getEmptyView();
        }

        @Override
        public void setEmptyViewInternal(View emptyView) {
            super.setEmptyView(emptyView);
        }
    }

    @Override
    protected boolean isNeedTimeLine() {
        return false;
    }

}