android下拉刷新ListView的介绍和实现代码

 更新时间:2013年04月21日 21:26:09   转载 作者:  
在当下,列表组件不带下拉刷新的都不好意思叫列表。第一次完成列表的下拉刷新功能的时候,直接在Activity中实现,虽然功能上是实现了,总体上感觉很乱。所以第二次用到的时候,就想着封装成一个组件,实现和Activity的解耦。

    大致上,我们发现,下拉刷新的列表和一般列表的区别是,当滚动条在顶端的时候,再往下拉动就会把整个列表拉下来,显示出松开刷新的提示。由此可以看出,在构建这个下拉刷新的组件的时候,只用继承ListView,然后重写onTouchEvent就能实现。还有就是要能在xml布局文件中引用,还需要一个参数为Context,AttributeSet的构造函数。

  表面上的功能大概就这些了。另一方面,刷新的行为似乎还没有定义,在刷新前做什么,刷新时要做什么,刷新完成后要做什么,这些行为写入一个接口中,然后让组件去实现。

  在整个组件的实现中,主体部分自然是onTouchEvent的部分。这里需要做一些说明,在ListView中,数据的滚动和ListView.scrollTo的行为是不一样的。数据的滚动是大概适配器的事。所以在不满足下拉整个列表的条件下,onTouchEvent 应该返回super.onTouchEvent(ev),让ListView组件原本的OnTouchEvent去处理。

  考虑到组件的id和表头的布局需要事先定义,同时我想把这个组件应用于多个项目里,所以就把这个组件作为一个Library去实现。

     下面就是具体的实现代码。

  首先来看一下表头的布局文件chenzong_push_refresh_header.xml:

复制代码 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="40dip"
       >
    <ImageView
        android:layout_width="30dip"
        android:layout_height="40dip"
        android:background="@drawable/arrow_down"
           android:layout_alignParentLeft="true"
        android:id="@+id/push_refresh_header_img"
        android:layout_marginLeft="10dip"
        />
    <ProgressBar
        android:layout_width="40dip"
        android:layout_height="40dip"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="10dip"
        android:id="@+id/push_refresh_header_pb"
        style="@android:style/Widget.ProgressBar.Inverse"
        android:visibility="gone"/>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:orientation="vertical"
        >
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="最近一次更新在:"
            android:textColor="#000000"
            />
        <TextView
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:id="@+id/push_refresh_header_date"
            android:textColor="#000000"
            android:text="2013-03-04 08:03:38"/>
    </LinearLayout>
</RelativeLayout>

       箭头、processBar和最近的一次刷新时间,表头文件就这三个元素。

       刷新的行为接口RefreshOperation的代码:

复制代码 代码如下:

public interface RefreshOperation {
    public void OnRefreshStart();
    public void OnRefreshing();
    public void OnRefreshEnd();
}

     列表拉下来时,箭头翻转的动画arrow_rotate.xml:

复制代码 代码如下:

<?xml version="1.0" encoding="utf-8"?>
<rotate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/linear_interpolator"
    android:fromDegrees="0"
    android:toDegrees="180"
    android:duration="300"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fillAfter="true"
    android:repeatCount="0">

</rotate>

      这些文件和一些资源文件备齐了之后,接下来就是下拉刷新列表PushRefreshList的具体实现:

复制代码 代码如下:

package com.chenzong;

import java.util.Calendar;

import com.doall.pushrefreshlist.R;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ListView;
import android.widget.TextView;


public class PushRefreshList extends ListView implements RefreshOperation{

    private int header_layout=R.layout.chenzong_push_refresh_header;
    //表头文件
    private int arrow_down=R.drawable.arrow_down;
    //箭头往下的资源
    private int arrow_up=R.drawable.arrow_up;
    //箭头往上的资源
    private int img=R.id.push_refresh_header_img;
    //显示箭头的控件id
    private int pb=R.id.push_refresh_header_pb;
    //刷新时的进度条
    private int startPoint=0;
    //触摸的起始点
    private RefreshOperation refresh;
    //刷新行为的对象
    private Animation animation=null;
    private Context context;
    private View headerView;
    private int minPushHeight;

   
    private final String TAG="pushRefresh";

    public PushRefreshList(Context cotext, AttributeSet attrs) {
        super(context, attrs);
        View empty=new View(context);
        //判断是否到列表的顶端,通常要用到this.getFirstVisiblePosition(),这里创建一个高度的为零View,加到headerView和数据之间
        this.addHeaderView(empty);
        LayoutInflater inflater=LayoutInflater.from(context);
        headerView=inflater.inflate(header_layout, null);
        this.addHeaderView(headerView);
        this.setRefreshOperation(this);
        this.context=context;

    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        this.minPushHeight=headerView.getMeasuredHeight();
        //获取下拉刷新的触发高度
        super.onLayout(changed, l, t, r, b);
    }

    private boolean canHandleEvent(int dy)
    {
        return (dy<0&&this.getFirstVisiblePosition()==0&&!isPbVisible());
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {

        int action=ev.getAction();
        switch(action)
        {
        case MotionEvent.ACTION_DOWN:
            startPoint=(int)ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            int dy=startPoint-(int)ev.getY();
            if(canHandleEvent(dy))
            {
                if(animation==null)
                {
                    if(Math.abs(this.getScrollY())>=this.minPushHeight)
                    {   
                        animation=AnimationUtils.loadAnimation(context, R.anim.arrow_rotate);
                        View mView=headerView.findViewById(img);
                        mView.startAnimation(animation);
                        this.setScrollbarFadingEnabled(true);
                    }
                }
                this.scrollTo(0,dy/2);   
                return true;
            }
            break;
        case MotionEvent.ACTION_UP:

                this.setScrollbarFadingEnabled(false);
                if(animation!=null)
                {
                    setImgBackgroundUp();
                    switchCompent(View.INVISIBLE,View.VISIBLE);
                    this.scrollTo(0,-minPushHeight);
                    PushRefreshList.this.refresh.OnRefreshStart();
                    new Thread(mRunnable).start();
                    animation=null;
                }
                else
                    this.scrollTo(0,0);
            break;
        }
        return super.onTouchEvent(ev);
    }

    private Runnable mRunnable=new Runnable()
    {

        @Override
        public void run() {
            PushRefreshList.this.refresh.OnRefreshing();
            mHandler.obtainMessage().sendToTarget();
        }
    };

    private Handler mHandler=new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            PushRefreshList.this.refresh.OnRefreshEnd();
            PushRefreshList.this.scrollTo(0, 0);
            PushRefreshList.this.setImgBackgroundDown();
            PushRefreshList.this.switchCompent(View.VISIBLE, View.GONE);
            TextView tv=(TextView)headerView.findViewById(R.id.push_refresh_header_date);
            tv.setText(this.getDateStr());
        }

        private String getDateStr()
        {
            Calendar ca=Calendar.getInstance();
            int year=ca.get(Calendar.YEAR);
            int month=ca.get(Calendar.MONTH);
            int date=ca.get(Calendar.DATE);
            int hour=ca.get(Calendar.HOUR);
            int mintes=ca.get(Calendar.MINUTE);
            int second=ca.get(Calendar.SECOND);
            return year+"-"+(month+1)+"-"+date+" "+hour+":"+mintes+":"+second;
        }
    };

    private void switchCompent(int imgStatus,int pbStatus)
    {
        View img=headerView.findViewById(R.id.push_refresh_header_img);
        img.clearAnimation();
        //执行了动画的控件如果不调用clearAnimation,setVisibility(View.GONE)会失效
        img.setVisibility(imgStatus);
        headerView.findViewById(R.id.push_refresh_header_pb).setVisibility(pbStatus);
    }

    private boolean isPbVisible()
    {
        return View.VISIBLE==headerView.findViewById(R.id.push_refresh_header_pb).getVisibility();
    }

    private void setImgBackgroundUp()
    {
        View mView=headerView.findViewById(this.img);
        mView.setBackgroundResource(arrow_up);
    }

    private void setImgBackgroundDown()
    {
        View mView=headerView.findViewById(this.img);
        mView.setBackgroundResource(arrow_down);
    }

    public void setRefreshOperation(RefreshOperation refresh)
    {
        this.refresh=refresh;
    }

    @Override
    public void OnRefreshStart() {

    }

    @Override
    public void OnRefreshing() {

    }

    @Override
    public void OnRefreshEnd() {
    }

相关文章

最新评论