> Android在线手册 > 【FastDev4Android框架开发】Android 列表下拉刷新组件PullToRefreshListView使用(三)

(一):写在前面的话
接着上一篇继续更新,上一篇文章已经把FastDev4Android项目新增图片自动无限轮播,包括项目结构已经需要进行完善的功能,那么今天我们继续完善这个项目;今天我们会再项目添加下拉刷新组件以及组件实现讲解和使用基本方法;
(二):基本实现
这边我们采用继承Listview控件来扩展下拉刷新的功能,主要在listview的头部添加一个下拉刷新的view,然后监听OnScrollListener滚动接口和实现onTouchEvent方法来处理。进行下拉listview滑动到指定的高度,然后接口回调加载刷新方法即可。
【FastDev4Android框架开发】Android 列表下拉刷新组件PullToRefreshListView使用(三)
效果如下:
【FastDev4Android框架开发】Android 列表下拉刷新组件PullToRefreshListView使用(三)
(三):详细实现:
这边主要看几个重点地方,详细的代码到项目中查看即可
onTouchEvent处理方法

/**
     * touch事件处理
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int y = (int) event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastMotionY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int offsetY = (int) event.getY();
            int deltY = Math.round(offsetY - mLastMotionY);
            mLastMotionY = offsetY;

            if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
                deltY = deltY / 2;
                mRefreshOriginalTopPadding += deltY;
                if (mRefreshOriginalTopPadding < -mRefreshViewHeight) {
                    mRefreshOriginalTopPadding = -mRefreshViewHeight;
                }
                resetheaderPadding();
            }
            break;
        case MotionEvent.ACTION_UP:
            //当手指抬开得时候 进行判断下拉的距离 ,如果>=临界值,那么进行刷洗,否则回归原位
            if (!isVerticalScrollBarEnabled()) {
                setVerticalScrollBarEnabled(true);
            }
            if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
                if (mRefreshView.getBottom() >= mRefreshViewHeight 
                        && mRefreshState == RELEASE_TO_REFRESH) {
                    //准备开始刷新
                    prepareForRefresh();
                } else {
                    // Abort refresh
                    resetHeader();
                }
            }
            break;
        }
        return super.onTouchEvent(event);
    }

onScrooll和onScrollStateChanged处理listview滑动过程中,实时改变头部view(pull view)上面的数据效果显示

@Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL && mRefreshState != REFRESHING) {
             if (firstVisibleItem == 0) {
                if ((mRefreshView.getBottom() >= mRefreshViewHeight)
                        && mRefreshState != RELEASE_TO_REFRESH) {
                    mRefreshViewText.setText(R.string.pull_to_refresh_release_label_it);
                    mRefreshViewImage.clearAnimation();
                    mRefreshViewImage.startAnimation(mFlipAnimation);
                    mRefreshState = RELEASE_TO_REFRESH;
                } else if (mRefreshView.getBottom() < mRefreshViewHeight
                        && mRefreshState != PULL_TO_REFRESH) {
                    mRefreshViewText.setText(R.string.pull_to_refresh_pull_label_it);
                    mRefreshViewImage.clearAnimation();
                    mRefreshViewImage.startAnimation(mReverseFlipAnimation);
                    mRefreshState = PULL_TO_REFRESH;
                }
            }
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

使用方法如下:

package com.chinaztt.fda.test;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.chinaztt.fda.ui.R;
import com.chinaztt.fda.ui.base.BaseActivity;
import com.chinaztt.fda.utils.UIUtils;
import com.chinaztt.fda.widget.PullToRefreshListView;

import java.util.ArrayList;
import java.util.List;


/**
 * 当前类注释:下拉刷新,上拉加载更多组件实例
 * 项目名:FastDev4Android
 * 包名:com.chinaztt.fda.test
 * 作者:江清清 on 15/10/23 11:25
 * 邮箱:jiangqqlmj@163.com
 * QQ: 781931404
 * 公司:江苏中天科技软件技术有限公司
 */
public class PullListviewActivity extends BaseActivity{
    private PullToRefreshListView lv_pull_item;
    private PullAdapter mPullAdapter;
    private LayoutInflater mInflater;
    private List<String> mTitles;
    private View load_more;
    private TextView load_more_tv; // listview底部加载view 显示数据
    private ProgressBar load_more_progress; // listview底部加载view 显示进度
    private LinearLayout load_next_page_layout;

    private Handler newHandler=new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what==1){
                refreshTitles();
                UIUtils.savePullToRefreshLastUpdateAt(lv_pull_item,UIUtils.DEMO_PULL_TIME_KEY);
                //刷新view
                mPullAdapter.notifyDataSetChanged();
            }else if(msg.what==2){
                moreTitles();
                //刷新view
                mPullAdapter.notifyDataSetChanged();
                showToastMsgShort("加载更多数据成功...");
                load_more_tv.setText("数据加载完成");
            }

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pull_listview_layout);
        lv_pull_item=(PullToRefreshListView)this.findViewById(R.id.lv_pull_item);
        mInflater=getLayouInflater();

        //特别注意 里边的view的控件可以根据当前的状态 修改字符串信息
        load_more = mInflater.inflate(R.layout.load_more_footview_layout,
                null);
        load_more_tv = (TextView) load_more
                .findViewById(R.id.load_next_page_text);
        load_more_progress = (ProgressBar) load_more
                .findViewById(R.id.load_next_page_progress);
        load_next_page_layout = (LinearLayout) load_more
                .findViewById(R.id.load_next_page_layout);
        //listview添加尾部 上拉加载更多view
        lv_pull_item.addFooterView(load_more, null, false);
        load_more_tv.setText("上拉加载更多数据...");

        initTitles();
        //初始化 上次下拉刷新的时间
        UIUtils.setPullToRefreshLastUpdated(lv_pull_item,UIUtils.DEMO_PULL_TIME_KEY);
        mPullAdapter=new PullAdapter();
        lv_pull_item.setAdapter(mPullAdapter);
        //listview item点击事件处理
        lv_pull_item.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                int index = position++;
                showToastMsgShort("点击了第:" + index + "个item");
            }
        });

        //listview 开始下拉刷新回调函数
        lv_pull_item.setOnRefreshListener(new PullToRefreshListView.OnRefreshListener() {
            @Override
            public void onRefresh() {
                //进行加载数据
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000);
                            newHandler.sendEmptyMessage(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                }).start();
            }
        });
        //listview 滑动  进行上拉加载更多
        lv_pull_item.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
                    if (lv_pull_item.getLastVisiblePosition() == (lv_pull_item
                            .getCount() - 1)) {
                        load_more_tv.setText("正在加载最新数据....");
                          //进行获取最新数据
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    Thread.sleep(5000);
                                    newHandler.sendEmptyMessage(2);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }

                            }
                        }).start();
                    }
                }
                if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
                    //正在滑动中,当前listview正在滑动 可以暂停图片加载器或者其他一些耗时操作
                } else {

                }
            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

            }
        });
    }
    private void initTitles(){
        mTitles=new ArrayList<String>();
        for(int i=0;i<20;i++){
            int index=i+1;
            mTitles.add("当前是:"+index+"");
        }
    }

    private void refreshTitles(){
        List<String> newTitles=new ArrayList<String>();
        for(int i=0;i<5;i++){
            int index=i+1;
            newTitles.add("新数据是:"+index+"");
        }
        newTitles.addAll(mTitles);
        mTitles.removeAll(mTitles);
        mTitles.addAll(newTitles);
    }
    private void moreTitles(){
        List<String> newTitles=new ArrayList<String>();
        for(int i=0;i<8;i++){
            int index=i+1;
            newTitles.add("更多数据是:"+index+"");
        }
        mTitles.addAll(newTitles);
    }

    class PullAdapter extends BaseAdapter{

        @Override
        public int getCount() {
            return mTitles.size();
        }

        @Override
        public Object getItem(int position) {
            return mTitles.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Hondler _Hondler=null;
            if(convertView==null){
                _Hondler=new Hondler();
                convertView=mInflater.inflate(R.layout.lv_main_item,null);
                _Hondler.tv_item=(TextView)convertView.findViewById(R.id.tv_item);
                convertView.setTag(_Hondler);
            }else
            {
                _Hondler=(Hondler)convertView.getTag();
            }
            _Hondler.tv_item.setText(mTitles.get(position));
            return convertView;
        }
    }

    static class Hondler{
        TextView tv_item;
    }
}

以上也是PullToRefreshListView的实现重点代码和基本使用效果,整个控件的功能实现代码如下:

package com.chinaztt.fda.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.chinaztt.fda.ui.R;
import com.chinaztt.fda.utils.Log;

/**
 * 当前类注释:下拉刷新,上拉加载更多组件
 * 项目名:FastDev4Android
 * 包名:com.chinaztt.fda.widget
 * 作者:江清清 on 15/10/23 13:32
 * 邮箱:jiangqqlmj@163.com
 * QQ: 781931404
 * 公司:江苏中天科技软件技术有限公司
 */
public class PullToRefreshListView extends ListView implements OnScrollListener, OnClickListener {

    /**
     * 下拉状态
     */
    private static final int PULL_TO_REFRESH = 1;   //下拉-默认为初始状态  准备下拉刷新
    private static final int RELEASE_TO_REFRESH = 2;   //释放刷新
    private static final int REFRESHING = 3;       //正在刷新

    private static final String TAG = "PullRefreshListView";

    private OnRefreshListener mOnRefreshListener;

    /**
     * 组件滑动监听器 scroll  当view在进行下拉滑动的时候,判断滑动的距离,
     * 如果达到可以进行刷新的临界点时候,回调当前接口中的方法
     * Listener that will receive notifications every time the list scrolls.
     */
    private OnScrollListener mOnScrollListener;

    //下拉刷新的的头部view
    private LinearLayout mRefreshView;
    private ImageView mRefreshViewImage;
    private ProgressBar mRefreshViewProgress;
    private TextView mRefreshViewText;
    private TextView mRefreshViewLastUpdated;


    private int mRefreshState;
    private int mCurrentScrollState;

    private RotateAnimation mFlipAnimation;
    private RotateAnimation mReverseFlipAnimation;

    private int mRefreshViewHeight;
    private int mRefreshOriginalTopPadding;
    private int mLastMotionY;

    public PullToRefreshListView(Context context) {
        super(context);
        init(context);
    }

    public PullToRefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        mFlipAnimation = new RotateAnimation(0, -180,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mFlipAnimation.setInterpolator(new LinearInterpolator());
        mFlipAnimation.setDuration(250);
        mFlipAnimation.setFillAfter(true);
        mReverseFlipAnimation = new RotateAnimation(-180, 0,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);
        mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
        mReverseFlipAnimation.setDuration(250);
        mReverseFlipAnimation.setFillAfter(true);

        mRefreshView = (LinearLayout) View.inflate(context, R.layout.pull_to_refresh_header, null);
        mRefreshViewText = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_text);
        mRefreshViewImage = (ImageView) mRefreshView.findViewById(R.id.pull_to_refresh_image);
        mRefreshViewProgress = (ProgressBar) mRefreshView.findViewById(R.id.pull_to_refresh_progress);
        mRefreshViewLastUpdated = (TextView) mRefreshView.findViewById(R.id.pull_to_refresh_updated_at);

        mRefreshState = PULL_TO_REFRESH;
        mRefreshViewImage.setMinimumHeight(50); //设置下拉最小的高度为50

        setFadingEdgeLength(0);
        setHeaderDividersEnabled(false);

        //把refreshview加入到listview的头部
        addHeaderView(mRefreshView);
        super.setOnScrollListener(this);
        mRefreshView.setOnClickListener(this);

        mRefreshView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
        mRefreshViewHeight = mRefreshView.getMeasuredHeight();
        mRefreshOriginalTopPadding = -mRefreshViewHeight;

        resetHeaderPadding();
    }

    /**
     * Set the listener that will receive notifications every time the list scrolls.
     * 
     * @param l  The scroll listener.
     */
    @Override
    public void setOnScrollListener(OnScrollListener l) {
        mOnScrollListener = l;
    }

    /**
     * 注册listview下拉刷新回到接口
     * Register a callback to be invoked when this list should be refreshed.
     * 
     * @param onRefreshListener  The callback to run.
     */
    public void setOnRefreshListener(OnRefreshListener onRefreshListener) {
        mOnRefreshListener = onRefreshListener;
    }

     /**
      * 进行设置设置上一次更新的时候
      *
     * Set a text to represent when the list was last updated. 
     * @param lastUpdated Last updated at.
     */
    public void setLastUpdated(CharSequence lastUpdated) {
        if (lastUpdated != null) {
            mRefreshViewLastUpdated.setVisibility(View.VISIBLE);
            mRefreshViewLastUpdated.setText(lastUpdated);
        } else {
            mRefreshViewLastUpdated.setVisibility(View.GONE);
        }
    }

    /**
     * touch事件处理
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int y = (int) event.getY();
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mLastMotionY = y;
            break;
        case MotionEvent.ACTION_MOVE:
            int offsetY = (int) event.getY();
            int deltY = Math.round(offsetY - mLastMotionY);
            mLastMotionY = offsetY;

            if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
                deltY = deltY / 2;
                mRefreshOriginalTopPadding += deltY;
                if (mRefreshOriginalTopPadding < -mRefreshViewHeight) {
                    mRefreshOriginalTopPadding = -mRefreshViewHeight;
                }
                resetHeaderPadding();
            }
            break;
        case MotionEvent.ACTION_UP:
            //当手指抬开得时候 进行判断下拉的距离 ,如果>=临界值,那么进行刷洗,否则回归原位
            if (!isVerticalScrollBarEnabled()) {
                setVerticalScrollBarEnabled(true);
            }
            if (getFirstVisiblePosition() == 0 && mRefreshState != REFRESHING) {
                if (mRefreshView.getBottom() >= mRefreshViewHeight 
                        && mRefreshState == RELEASE_TO_REFRESH) {
                    //准备开始刷新
                    prepareForRefresh();
                } else {
                    // Abort refresh
                    resetHeader();
                }
            }
            break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {
        if (mCurrentScrollState == SCROLL_STATE_TOUCH_SCROLL && mRefreshState != REFRESHING) {
             if (firstVisibleItem == 0) {
                if ((mRefreshView.getBottom() >= mRefreshViewHeight)
                        && mRefreshState != RELEASE_TO_REFRESH) {
                    mRefreshViewText.setText(R.string.pull_to_refresh_release_label_it);
                    mRefreshViewImage.clearAnimation();
                    mRefreshViewImage.startAnimation(mFlipAnimation);
                    mRefreshState = RELEASE_TO_REFRESH;
                } else if (mRefreshView.getBottom() < mRefreshViewHeight
                        && mRefreshState != PULL_TO_REFRESH) {
                    mRefreshViewText.setText(R.string.pull_to_refresh_pull_label_it);
                    mRefreshViewImage.clearAnimation();
                    mRefreshViewImage.startAnimation(mReverseFlipAnimation);
                    mRefreshState = PULL_TO_REFRESH;
                }
            }
        }

        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        mCurrentScrollState = scrollState;

        if (mOnScrollListener != null) {
            mOnScrollListener.onScrollStateChanged(view, scrollState);
        }
    }

    /**
     * Sets the header padding back to original size.
     */
    private void resetHeaderPadding() {
        mRefreshView.setPadding(
                mRefreshView.getPaddingLeft(),
                mRefreshOriginalTopPadding,
                mRefreshView.getPaddingRight(),
                mRefreshView.getPaddingBottom());
    }

    public void prepareForRefresh() {
        if (mRefreshState != REFRESHING) {
            mRefreshState = REFRESHING;

            mRefreshOriginalTopPadding = 0;
            resetHeaderPadding();

            mRefreshViewImage.clearAnimation();
            mRefreshViewImage.setVisibility(View.GONE);
            mRefreshViewProgress.setVisibility(View.VISIBLE);
            mRefreshViewText.setText(R.string.pull_to_refresh_refreshing_label_it);

            onRefresh();
        }
    }

    private void resetHeader() {
        mRefreshState = PULL_TO_REFRESH;

        mRefreshOriginalTopPadding = -mRefreshViewHeight;
        resetHeaderPadding();

        mRefreshViewImage.clearAnimation();
        mRefreshViewImage.setVisibility(View.VISIBLE);
        mRefreshViewProgress.setVisibility(View.GONE);
        mRefreshViewText.setText(R.string.pull_to_refresh_pull_label_it);
    }

    /**
     * 开始回调刷新
     */
    public void onRefresh() {
        Log.d(TAG, "onRefresh");
        if (mOnRefreshListener != null) {
            mOnRefreshListener.onRefresh();
        }
    }

    /**
     * Resets the list to a normal state after a refresh.
     */
    public void onRefreshComplete() {        
        Log.d(TAG, "onRefreshComplete");

        resetHeader();
    }

    @Override
    public void onClick(View v) {
        Log.d(TAG, "onClick");
    }

    /**
     * Interface definition for a callback to be invoked when list should be
     * refreshed.
     */
    public interface OnRefreshListener {
        /**
         * Called when the list should be refreshed.
         * <p>
         * A call to {@link PullToRefreshListView #onRefreshComplete()} is
         * expected to indicate that the refresh has completed.
         */
        public void onRefresh();
    }
}

核心实现类的代码就这些,其实整个文件读下来,实现起来也不难的吧~大家可以根据上面的代码可以修改定制成自己的控件都没问题的~希望可以帮助到大家,如果需要整个demo源代码可以去Github中下载整个项目:Https://github.com/jiangqqlmj/FastDev4Android 同时欢迎大家star和fork整个开源快速开发框架项目~如果有什么意见和反馈,欢迎留言,必定第一时间回复。也欢迎有同样兴趣的童鞋加入到该项目中来,一起维护该项目。