zoukankan      html  css  js  c++  java
  • listview下拉刷新 上拉(滑动分页)加载更多

    最 近做的类似于微博的项目中,有个Android功能要使用到listview的向下拉刷新来刷新最新消息,向上拉刷新(滑动分页)来加载更多。
    新浪微博就是使用这种方式的典型。
    当用户从网络上读取微博的时候,如果一下子全部加载用户未读的微博这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容。这时候,我们就需要用到另一个功能,那就是listview的分页了,其实这个分页可以做成客户端的分页,也可以做成服务器端的分页(点击加载时,从服务器对应的加载第N页就好了!!!)。通过分页分次加载数据,用户看多少就去加载多少。
    通常这也分为两种方式,一种是设置一个按钮,用户点击即加载。另一种是当用户滑动到底部时自动加载。今天我就和大家分享一下滑动到底端时自动加载这个功能的实现。
    效果图如下所示:

    下拉刷新最主要的流程是:
    (1). 下拉,显示提示头部界面(HeaderView),这个过程提示用户”下拉刷新”
    (2). 下拉到一定程度,超出了刷新最基本的下拉界限,我们认为达到了刷新的条件,提示用户可以”松手刷新”了,效果上允许用户继续下拉
    (3). 用户松手,可能用户下拉远远不止提示头部界面,所以这一步,先反弹回仅显示提示头部界面,然后提示用户”正在加载”。
    (4). 加载完成后,隐藏提示头部界面。

    那么让我们看看怎么才能实现呢???
    第一步:既然是要显示listview ,那么就应该有个listview 的容器pulldown.xml

    <?xml version="1.0" encoding="utf-8"?>
    <com.solo.pulldown.PullDownView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/pull_down_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@android:color/white">
     
    </com.solo.pulldown.PullDownView>

    第二步:自定义一个listview中显示的item对象pulldown_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@android:id/text1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:gravity="center_vertical"
        android:paddingLeft="6dip"
        android:minHeight="?android:attr/listPreferredItemHeight"
        android:textColor="@android:color/black"
    />

    第三步:定义一个header的xml布局文件pulldown_header.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >
     
        <ImageView
            android:id="@+id/pulldown_header_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_marginLeft="20dp"
            android:scaleType="centerCrop"
            android:src="@drawable/z_arrow_down"
            android:visibility="invisible" />
     
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@+id/pulldown_header_arrow"
            android:layout_alignTop="@+id/pulldown_header_arrow"
            android:layout_centerHorizontal="true"
            android:gravity="center_vertical"
            android:orientation="vertical" >
     
            <TextView
                android:id="@+id/pulldown_header_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="加载中..." />
     
            <TextView
                android:id="@+id/pulldown_header_date"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="更新于:"
                android:visibility="gone" />
        </LinearLayout>
     
        <ProgressBar
            android:id="@+id/pulldown_header_loading"
            style="@android:style/Widget.ProgressBar.Small.Inverse"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp" />
     
    </RelativeLayout>

    第四步:如果需要向上拉更新更多的话,那就定义一个底部的footer的布局文件,在此为方便起见,只定义一个progressbar跟textview,更加复杂的显示,就交给你们了~~~~~pulldown_footer.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="10dp"
        android:paddingTop="10dp" >
     
        <TextView
            android:id="@+id/pulldown_footer_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="更多"
            android:textSize="15dp" />
     
        <ProgressBar
            android:id="@+id/pulldown_footer_loading"
            style="@android:style/Widget.ProgressBar.Small.Inverse"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp"
            android:visibility="gone" />
     
    </RelativeLayout>

    第五步:那么主要的文件这才登场:::::::重写listview这个文件主要任务是提供触摸的事件的处理方法。

    /**
     * <p>一个可以监听ListView是否滚动到最顶部或最底部的自定义控件</p>
     * 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br>
     * 如果加以改进,可以实现监听scroll滚动的具体位置等
     */
     
    public class ScrollOverListView extends ListView {
     
        private int mLastY;
        private int mTopPosition;
        private int mBottomPosition;
     
        public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
        }
     
        public ScrollOverListView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
     
        public ScrollOverListView(Context context) {
            super(context);
            init();
        }
     
        private void init(){
            mTopPosition = 0;
            mBottomPosition = 0;
        }
     
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            final int action = ev.getAction();
            final int y = (int) ev.getRawY();
     
            switch(action){
                case MotionEvent.ACTION_DOWN:{
                    mLastY = y;
                    final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);
                    if (isHandled) {
                        mLastY = y;
                        return isHandled;
                    }
                    break;
                }
     
                case MotionEvent.ACTION_MOVE:{
                    final int childCount = getChildCount();
                    if(childCount == 0) return super.onTouchEvent(ev);
     
                    final int itemCount = getAdapter().getCount() - mBottomPosition;
     
                    final int deltaY = y - mLastY;
                    //DLog.d("lastY=%d y=%d", mLastY, y);
     
                    final int firstTop = getChildAt(0).getTop();
                    final int listPadding = getListPaddingTop();
     
                    final int lastBottom = getChildAt(childCount - 1).getBottom();
                    final int end = getHeight() - getPaddingBottom();
     
                    final int firstVisiblePosition = getFirstVisiblePosition();
     
                    final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY);
     
                    if(isHandleMotionMove){
                        mLastY = y;
                        return true;
                    }
     
                    //DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);
                    if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {
                        final boolean isHandleOnListViewTopAndPullDown;
                        isHandleOnListViewTopAndPullDown = mOnScrollOverListener.onListViewTopAndPullDown(deltaY);
                        if(isHandleOnListViewTopAndPullDown){
                            mLastY = y;
                            return true;
                        }
                    }
     
                    // DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);
                    if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {
                        final boolean isHandleOnListViewBottomAndPullDown;
                        isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY);
                        if(isHandleOnListViewBottomAndPullDown){
                            mLastY = y;
                            return true;
                        }
                    }
                    break;
                }
     
                case MotionEvent.ACTION_UP:{
                    final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev);
                    if (isHandlerMotionUp) {
                        mLastY = y;
                        return true;
                    }
                    break;
                }
            }
     
            mLastY = y;
            return super.onTouchEvent(ev);
        }
     
        /**空的*/
        private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){
     
            @Override
            public boolean onListViewTopAndPullDown(int delta) {
                return false;
            }
     
            @Override
            public boolean onListViewBottomAndPullUp(int delta) {
                return false;
            }
     
            @Override
            public boolean onMotionDown(MotionEvent ev) {
                return false;
            }
     
            @Override
            public boolean onMotionMove(MotionEvent ev, int delta) {
                return false;
            }
     
            @Override
            public boolean onMotionUp(MotionEvent ev) {
                return false;
            }
     
        };
     
        // =============================== public method ===============================
     
        /**
         * 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个
         *
         * @param index 正数第几个,必须在条目数范围之内
         */
        public void setTopPosition(int index){
            if(getAdapter() == null)
                throw new NullPointerException("You must set adapter before setTopPosition!");
            if(index < 0)
                throw new IllegalArgumentException("Top position must > 0");
     
            mTopPosition = index;
        }
     
        /**
         * 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个
         *
         * @param index 倒数第几个,必须在条目数范围之内
         */
        public void setBottomPosition(int index){
            if(getAdapter() == null)
                throw new NullPointerException("You must set adapter before setBottonPosition!");
            if(index < 0)
                throw new IllegalArgumentException("Bottom position must > 0");
     
            mBottomPosition = index;
        }
     
        /**
         * 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br>
         *
         * @see OnScrollOverListener
         */
        public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener){
            mOnScrollOverListener = onScrollOverListener;
        }
     
        /**
         * 滚动监听接口</br>
         * @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)
         *
         */
        public interface OnScrollOverListener {
     
            /**
             * 到达最顶部触发
             *
             * @param delta 手指点击移动产生的偏移量
             * @return
             */
            boolean onListViewTopAndPullDown(int delta);
     
            /**
             * 到达最底部触发
             *
             * @param delta 手指点击移动产生的偏移量
             * @return
             */
            boolean onListViewBottomAndPullUp(int delta);
     
            /**
             * 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN}
             *
             * @return 返回true表示自己处理
             * @see View#onTouchEvent(MotionEvent)
             */
            boolean onMotionDown(MotionEvent ev);
     
            /**
             * 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE}
             *
             * @return 返回true表示自己处理
             * @see View#onTouchEvent(MotionEvent)
             */
            boolean onMotionMove(MotionEvent ev, int delta);
     
            /**
             * 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}
             *
             * @return 返回true表示自己处理
             * @see View#onTouchEvent(MotionEvent)
             */
            boolean onMotionUp(MotionEvent ev);
     
        }
     
    }

    第六步:下拉刷新控件,真正实现下拉刷新的是这个控件,而上面的那个ScrollOverListView只是提供触摸的事件等

    /**
     * 下拉刷新控件</br>
     * 真正实现下拉刷新的是这个控件,
     * ScrollOverListView只是提供触摸的事件等
     */
    public class PullDownView extends LinearLayout implements OnScrollOverListener{
        private static final String TAG = "PullDownView";
     
        private static final int START_PULL_DEVIATION = 50; // 移动误差
        private static final int AUTO_INCREMENTAL = 10;     // 自增量,用于回弹
     
        private static final int WHAT_DID_LOAD_DATA = 1;    // Handler what 数据加载完毕
        private static final int WHAT_ON_REFRESH = 2;       // Handler what 刷新中
        private static final int WHAT_DID_REFRESH = 3;      // Handler what 已经刷新完
        private static final int WHAT_SET_HEADER_HEIGHT = 4;// Handler what 设置高度
        private static final int WHAT_DID_MORE = 5;         // Handler what 已经获取完更多
     
        private static final int DEFAULT_HEADER_VIEW_HEIGHT = 105;  // 头部文件原本的高度
     
        private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm");
     
        private View mHeaderView;
        private LayoutParams mHeaderViewParams;
        private TextView mHeaderViewDateView;
        private TextView mHeaderTextView;
        private ImageView mHeaderArrowView;
        private View mHeaderLoadingView;
        private View mFooterView;
        private TextView mFooterTextView;
        private View mFooterLoadingView;
        private ScrollOverListView mListView;
     
        private OnPullDownListener mOnPullDownListener;
        private RotateAnimation mRotateOTo180Animation;
        private RotateAnimation mRotate180To0Animation;
     
        private int mHeaderIncremental; // 增量
        private float mMotionDownLastY; // 按下时候的Y轴坐标
     
        private boolean mIsDown;            // 是否按下
        private boolean mIsRefreshing;      // 是否下拉刷新中
        private boolean mIsFetchMoreing;    // 是否获取更多中
        private boolean mIsPullUpDone;      // 是否回推完成
        private boolean mEnableAutoFetchMore;   // 是否允许自动获取更多
     
        // 头部文件的状态
        private static final int HEADER_VIEW_STATE_IDLE = 0;            // 空闲
        private static final int HEADER_VIEW_STATE_NOT_OVER_HEIGHT = 1; // 没有超过默认高度
        private static final int HEADER_VIEW_STATE_OVER_HEIGHT = 2;     // 超过默认高度
        private int mHeaderViewState = HEADER_VIEW_STATE_IDLE;
     
        public PullDownView(Context context, AttributeSet attrs) {
            super(context, attrs);
            initHeaderViewAndFooterViewAndListView(context);
        }
     
        public PullDownView(Context context) {
            super(context);
            initHeaderViewAndFooterViewAndListView(context);
        }
     
        /*
         * ==================================
         * Public method
         * 外部使用,具体就是用这几个就可以了
         *
         * ==================================
         */
     
        /**
         * 刷新事件接口
         */
        public interface OnPullDownListener {
            void onRefresh();
            void onMore();
        }
     
        /**
         * 通知加载完了数据,要放在Adapter.notifyDataSetChanged后面
         * 当你加载完数据的时候,调用这个notifyDidLoad()
         * 才会隐藏头部,并初始化数据等
         */
        public void notifyDidLoad() {
            mUIHandler.sendEmptyMessage(WHAT_DID_LOAD_DATA);
        }
     
        /**
         * 通知已经刷新完了,要放在Adapter.notifyDataSetChanged后面
         * 当你执行完刷新任务之后,调用这个notifyDidRefresh()
         * 才会隐藏掉头部文件等操作
         */
        public void notifyDidRefresh() {
            mUIHandler.sendEmptyMessage(WHAT_DID_REFRESH);
        }
     
        /**
         * 通知已经获取完更多了,要放在Adapter.notifyDataSetChanged后面
         * 当你执行完更多任务之后,调用这个notyfyDidMore()
         * 才会隐藏加载圈等操作
         */
        public void notifyDidMore() {
            mUIHandler.sendEmptyMessage(WHAT_DID_MORE);
        }
     
        /**
         * 设置监听器
         * @param listener
         */
        public void setOnPullDownListener(OnPullDownListener listener){
            mOnPullDownListener = listener;
        }
     
        /**
         * 获取内嵌的listview
         * @return ScrollOverListView
         */
        public ListView getListView(){
            return mListView;
        }
     
        /**
         * 是否开启自动获取更多
         * 自动获取更多,将会隐藏footer,并在到达底部的时候自动刷新
         * @param index 倒数第几个触发
         */
        public void enableAutoFetchMore(boolean enable, int index){
            if(enable){
                mListView.setBottomPosition(index);
                mFooterLoadingView.setVisibility(View.VISIBLE);
            }else{
                mFooterTextView.setText("更多");
                mFooterLoadingView.setVisibility(View.GONE);
            }
            mEnableAutoFetchMore = enable;
        }
     
        /*
         * ==================================
         * Private method
         * 具体实现下拉刷新等操作
         *
         * ==================================
         */
     
        /**
         * 初始化界面
         */
        private void initHeaderViewAndFooterViewAndListView(Context context){
            setOrientation(LinearLayout.VERTICAL);
            //setDrawingCacheEnabled(false);
            /*
             * 自定义头部文件
             * 放在这里是因为考虑到很多界面都需要使用
             * 如果要修改,和它相关的设置都要更改
             */
            mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);
            mHeaderViewParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
            addView(mHeaderView, 0, mHeaderViewParams);
     
            mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);
            mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);
            mHeaderLoadingView = mHeaderView.findViewById(R.id.pulldown_header_loading);
     
            // 注意,图片旋转之后,再执行旋转,坐标会重新开始计算
            mRotateOTo180Animation = new RotateAnimation(0, 180,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            mRotateOTo180Animation.setDuration(250);
            mRotateOTo180Animation.setFillAfter(true);
     
            mRotate180To0Animation = new RotateAnimation(180, 0,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            mRotate180To0Animation.setDuration(250);
            mRotate180To0Animation.setFillAfter(true);
     
            /**
             * 自定义脚部文件
             */
            mFooterView = LayoutInflater.from(context).inflate(R.layout.pulldown_footer, null);
            mFooterTextView = (TextView) mFooterView.findViewById(R.id.pulldown_footer_text);
            mFooterLoadingView = mFooterView.findViewById(R.id.pulldown_footer_loading);
            mFooterView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(!mIsFetchMoreing){
                        mIsFetchMoreing = true;
                        mFooterLoadingView.setVisibility(View.VISIBLE);
                        mOnPullDownListener.onMore();
                    }
                }
            });
     
            /*
             * ScrollOverListView 同样是考虑到都是使用,所以放在这里
             * 同时因为,需要它的监听事件
             */
            mListView = new ScrollOverListView(context);
            mListView.setOnScrollOverListener(this);
            mListView.setCacheColorHint(0);
            addView(mListView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
     
            // 空的listener
            mOnPullDownListener = new OnPullDownListener() {
                @Override
                public void onRefresh() {}
                @Override
                public void onMore() {}
            };
        }
     
        /**
         * 在下拉和回推的时候检查头部文件的状态</br>
         * 如果超过了默认高度,就显示松开可以刷新,
         * 否则显示下拉可以刷新
         */
        private void checkHeaderViewState(){
            if(mHeaderViewParams.height >= DEFAULT_HEADER_VIEW_HEIGHT){
                if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;
                mHeaderViewState = HEADER_VIEW_STATE_OVER_HEIGHT;
                mHeaderTextView.setText("松开可以刷新");
                mHeaderArrowView.startAnimation(mRotateOTo180Animation);
            }else{
                if(mHeaderViewState == HEADER_VIEW_STATE_NOT_OVER_HEIGHT
                        || mHeaderViewState == HEADER_VIEW_STATE_IDLE) return;
                mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;
                mHeaderTextView.setText("下拉可以刷新");
                mHeaderArrowView.startAnimation(mRotate180To0Animation);
            }
        }
     
        private void setHeaderHeight(final int height){
            mHeaderIncremental = height;
            mHeaderViewParams.height = height;
            mHeaderView.setLayoutParams(mHeaderViewParams);
        }
     
        /**
         * 自动隐藏动画
         */
        class HideHeaderViewTask extends TimerTask{
            @Override
            public void run() {
                if(mIsDown) {
                    cancel();
                    return;
                }
                mHeaderIncremental -= AUTO_INCREMENTAL;
                if(mHeaderIncremental > 0){
                    mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
                }else{
                    mHeaderIncremental = 0;
                    mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
                    cancel();
                }
            }
        }
     
        /**
         * 自动显示动画
         */
        class ShowHeaderViewTask extends TimerTask{
     
            @Override
            public void run() {
                if(mIsDown) {
                    cancel();
                    return;
                }
                mHeaderIncremental -= AUTO_INCREMENTAL;
                if(mHeaderIncremental > DEFAULT_HEADER_VIEW_HEIGHT){
                    mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
                }else{
                    mHeaderIncremental = DEFAULT_HEADER_VIEW_HEIGHT;
                    mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
                    if(!mIsRefreshing){
                        mIsRefreshing = true;
                        mUIHandler.sendEmptyMessage(WHAT_ON_REFRESH);
                    }
                    cancel();
                }
            }
        }
     
        private Handler mUIHandler = new Handler(){
     
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case WHAT_DID_LOAD_DATA:{
                        mHeaderViewParams.height = 0;
                        mHeaderLoadingView.setVisibility(View.GONE);
                        mHeaderTextView.setText("下拉可以刷新");
                        mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);
                        mHeaderViewDateView.setVisibility(View.VISIBLE);
                        mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
                        mHeaderArrowView.setVisibility(View.VISIBLE);
                        showFooterView();
                        return;
                    }
     
                    case WHAT_ON_REFRESH:{
                        // 要清除掉动画,否则无法隐藏
                        mHeaderArrowView.clearAnimation();
                        mHeaderArrowView.setVisibility(View.INVISIBLE);
                        mHeaderLoadingView.setVisibility(View.VISIBLE);
                        mOnPullDownListener.onRefresh();
                        return;
                    }
     
                    case WHAT_DID_REFRESH :{
                        mIsRefreshing = false;
                        mHeaderViewState = HEADER_VIEW_STATE_IDLE;
                        mHeaderArrowView.setVisibility(View.VISIBLE);
                        mHeaderLoadingView.setVisibility(View.GONE);
                        mHeaderViewDateView.setText("更新于:" + dateFormat.format(new Date(System.currentTimeMillis())));
                        setHeaderHeight(0);
                        showFooterView();
                        return;
                    }
     
                    case WHAT_SET_HEADER_HEIGHT :{
                        setHeaderHeight(mHeaderIncremental);
                        return;
                    }
     
                    case WHAT_DID_MORE :{
                        mIsFetchMoreing = false;
                        mFooterTextView.setText("更多");
                        mFooterLoadingView.setVisibility(View.GONE);
                    }
                }
            }
     
        };
     
        /**
         * 显示脚步脚部文件
         */
        private void showFooterView(){
            if(mListView.getFooterViewsCount() == 0 && isFillScreenItem()){
                mListView.addFooterView(mFooterView);
                mListView.setAdapter(mListView.getAdapter());
            }
        }
     
        /**
         * 条目是否填满整个屏幕
         */
        private boolean isFillScreenItem(){
            final int firstVisiblePosition = mListView.getFirstVisiblePosition();
            final int lastVisiblePostion = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();
            final int visibleItemCount = lastVisiblePostion - firstVisiblePosition + 1;
            final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();
     
            if(visibleItemCount < totalItemCount) return true;
            return false;
        }
     
        /*
         * ==================================
         * 实现 OnScrollOverListener接口
         *
         *
         * ==================================
         */
     
        @Override
        public boolean onListViewTopAndPullDown(int delta) {
            if(mIsRefreshing || mListView.getCount() - mListView.getFooterViewsCount() == 0) return false;
     
            int absDelta = Math.abs(delta);
            final int i = (int) Math.ceil((double)absDelta / 2);
     
            mHeaderIncremental += i;
            if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight
                setHeaderHeight(mHeaderIncremental);
                checkHeaderViewState();
            }
            return true;
        }
     
        @Override
        public boolean onListViewBottomAndPullUp(int delta) {
            if(!mEnableAutoFetchMore || mIsFetchMoreing) return false;
            // 数量充满屏幕才触发
            if(isFillScreenItem()){
                mIsFetchMoreing = true;
                mFooterTextView.setText("加载更多中...");
                mFooterLoadingView.setVisibility(View.VISIBLE);
                mOnPullDownListener.onMore();
                return true;
            }
            return false;
        }
     
        @Override
        public boolean onMotionDown(MotionEvent ev) {
            mIsDown = true;
            mIsPullUpDone = false;
            mMotionDownLastY = ev.getRawY();
            return false;
        }
     
        @Override
        public boolean onMotionMove(MotionEvent ev, int delta) {
            //当头部文件回推消失的时候,不允许滚动
            if(mIsPullUpDone) return true;
     
            // 如果开始按下到滑动距离不超过误差值,则不滑动
            final int absMotionY = (int) Math.abs(ev.getRawY() - mMotionDownLastY);
            if(absMotionY < START_PULL_DEVIATION) return true;
     
            final int absDelta = Math.abs(delta);
            final int i = (int) Math.ceil((double)absDelta / 2);
     
            // onTopDown在顶部,并上回推和onTopUp相对
            if(mHeaderViewParams.height > 0 && delta < 0){
                mHeaderIncremental -= i;
                if(mHeaderIncremental > 0){
                    setHeaderHeight(mHeaderIncremental);
                    checkHeaderViewState();
                }else{
                    mHeaderViewState = HEADER_VIEW_STATE_IDLE;
                    mHeaderIncremental = 0;
                    setHeaderHeight(mHeaderIncremental);
                    mIsPullUpDone = true;
                }
                return true;
            }
            return false;
        }
     
        @Override
        public boolean onMotionUp(MotionEvent ev) {
            mIsDown = false;
            // 避免和点击事件冲突
            if(mHeaderViewParams.height > 0){
                // 判断头文件拉动的距离与设定的高度,小了就隐藏,多了就固定高度
                int x = mHeaderIncremental - DEFAULT_HEADER_VIEW_HEIGHT;
                Timer timer = new Timer(true);
                if(x < 0){
                    timer.scheduleAtFixedRate(new HideHeaderViewTask(), 0, 10);
                }else{
                    timer.scheduleAtFixedRate(new ShowHeaderViewTask(), 0, 10);
                }
                return true;
            }
            return false;
        }
     
    }

    第七步:这个java文件就是封装数据,然后调用上两个文件就好了。废话不多说,上代码:::::

    public class PullDownActivity extends Activity implements OnPullDownListener, OnItemClickListener{
     
        private static final int WHAT_DID_LOAD_DATA = 0;
        private static final int WHAT_DID_REFRESH = 1;
        private static final int WHAT_DID_MORE = 2;
     
        private ListView mListView;
        private ArrayAdapter<String> mAdapter;
     
        private PullDownView mPullDownView;
        private List<String> mStrings = new ArrayList<String>();
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.pulldown);
     
            /*
             * 1.使用PullDownView
             * 2.设置OnPullDownListener
             * 3.从mPullDownView里面获取ListView
             */
            mPullDownView = (PullDownView) findViewById(R.id.pull_down_view);
            mPullDownView.setOnPullDownListener(this);
            mListView = mPullDownView.getListView();
     
            mListView.setOnItemClickListener(this);
            mAdapter = new ArrayAdapter<String>(this, R.layout.pulldown_item, mStrings);
            mListView.setAdapter(mAdapter);
     
            mPullDownView.enableAutoFetchMore(true, 1);
     
            loadData();
        }
     
        private void loadData(){
            new Thread(new Runnable() {
     
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    List<String> strings = new ArrayList<String>();
                    for (String body : mStringArray) {
                        strings.add(body);
                    }
                    Message msg = mUIHandler.obtainMessage(WHAT_DID_LOAD_DATA);
                    msg.obj = strings;
                    msg.sendToTarget();
                }
            }).start();
        }
     
        @Override
        public void onRefresh() {
            new Thread(new Runnable() {
     
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Message msg = mUIHandler.obtainMessage(WHAT_DID_REFRESH);
                    msg.obj = "After refresh " + System.currentTimeMillis();
                    msg.sendToTarget();
                }
            }).start();
        }
     
        @Override
        public void onMore() {
            new Thread(new Runnable() {
     
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Message msg = mUIHandler.obtainMessage(WHAT_DID_MORE);
                    msg.obj = "After more " + System.currentTimeMillis();
                    msg.sendToTarget();
                }
            }).start();
        }
     
        private Handler mUIHandler = new Handler(){
     
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case WHAT_DID_LOAD_DATA:{
                        if(msg.obj != null){
                            List<String> strings = (List<String>) msg.obj;
                            if(!strings.isEmpty()){
                                mStrings.addAll(strings);
                                mAdapter.notifyDataSetChanged();
                            }
                        }
                        // 诉它数据加载完毕;
                        mPullDownView.notifyDidLoad();
                        break;
                    }
                    case WHAT_DID_REFRESH :{
                        String body = (String) msg.obj;
                        mStrings.add(0, body);
                        mAdapter.notifyDataSetChanged();
                        // 告诉它更新完毕
                        mPullDownView.notifyDidRefresh();
                        break;
                    }
     
                    case WHAT_DID_MORE:{
                        String body = (String) msg.obj;
                        mStrings.add(body);
                        mAdapter.notifyDataSetChanged();
                        // 告诉它获取更多完毕
                        mPullDownView.notifyDidMore();
                        break;
                    }
                }
     
            }
     
        };
     
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Toast.makeText(this, "啊,你点中我了 " + position, Toast.LENGTH_SHORT).show();
        }
     
        // 模拟数据
        private String[] mStringArray = {
                "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
                "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", "Airag", "Airedale",
                "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert", "American Cheese"
        };
     
    }

    整个程序就这么完成了,总之,可以使用前两个封装好的文件,然后,调用就好了~~~~没什么太难的地方。

    源代码下载地址:我是传送门   我也是个传送门

    http://k-beta.com/android-listview-more-refresh.html

  • 相关阅读:
    Dungeon Master (BFS与DFS的应用)
    Broken Keyboard(模拟数组或者双重链表的运用)
    自己设置的纸牌游戏(简单数组栈和队列的设计)
    贪吃蛇(双重优先队列的综合运用)
    N!的阶乘附带简单大整数类的输入输出(暂时没有深入的了解)
    大整数乘法(Comba 乘法 (Comba  Multiplication)原理)
    建筑抢修(堆优先队列和贪心的结合)
    lower_bound()函数与quicksort()函数的简单掌握
    Long Jumps(二分查找lower_bound()函数的运用)
    团队队列(列和map结合的经典运用)
  • 原文地址:https://www.cnblogs.com/gzggyy/p/3154934.html
Copyright © 2011-2022 走看看