自定义listview实现上拉加载下拉刷新

Android内自带的listview没有上拉加载下拉刷新的功能,可是在正常的开发过程中这一点是必不可少的。

首先是自定义listview,就是定义一个继承listview的类

package com.example.handerview;

import java.text.SimpleDateFormat;

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


public class RefreshListView extends ListView implements OnScrollListener{

	
	  private static final String TAG = "RefreshListView";
	  private int firstVisibleItemPosition; // 屏幕显示在第一个的item的索引
	  private int downY; // 按下时y轴的偏移量
	  private int headerViewHeight; // 头布局的高度
	  private View headerView; // 头布局的对象

	  private final int DOWN_PULL_REFRESH = 0; // 下拉刷新状态
	  private final int RELEASE_REFRESH = 1; // 松开刷新
	  private final int REFRESHING = 2; // 正在刷新中
	  private int currentState = DOWN_PULL_REFRESH; // 头布局的状态: 默认为下拉刷新状态

	  private Animation upAnimation; // 向上旋转的动画
	  private Animation downAnimation; // 向下旋转的动画

	  private ImageView ivArrow; // 头布局的箭头
	  private ProgressBar mProgressBar; // 头布局的进度条
	  private TextView tvState; // 头布局的状态
	  private TextView tvLastUpdateTime; // 头布局的最后更新时间

	  private OnRefreshListener mOnRefershListener;
	  private boolean isScrollToBottom; // 是否滑动到底部
	  private View footerView; // 脚布局的对象
	  private int footerViewHeight; // 脚布局的高度
	  private boolean isLoadingMore = false; // 是否正在加载更多中

	  public RefreshListView(Context context, AttributeSet attrs) {
	    super(context, attrs);
	    //初始化头部
	    initHeaderView();
	    
	    //初始化底部
	    initFooterView();
	    this.setOnScrollListener(this);
	  }

	  /**
	   * 初始化脚布局
	   */
	  private void initFooterView() {
		//获取布局文件
	    footerView = View.inflate(getContext(), R.layout.listview_footer, null);
	    
	 // 系统会帮我们测量出headerView的高度
	    footerView.measure(0, 0);
	    footerViewHeight = footerView.getMeasuredHeight();
	    footerView.setPadding(0, -footerViewHeight, 0, 0);
	    this.addFooterView(footerView);
	  }

	  /**
	   * 初始化头布局
	   */
	  private void initHeaderView() {
		//获取布局文件
	    headerView = View.inflate(getContext(), R.layout.listview_header, null);
	    
	    //获取布局文件内的箭头
	    ivArrow = (ImageView) headerView.findViewById(R.id.iv_listview_header_arrow);
	    
	    //获取布局文件内的进度条
	    mProgressBar = (ProgressBar) headerView.findViewById(R.id.pb_listview_header);
	    tvState = (TextView) headerView.findViewById(R.id.tv_listview_header_state);
	    tvLastUpdateTime = (TextView) headerView.findViewById(R.id.tv_listview_header_last_update_time);

	    // 设置最后刷新时间
	    tvLastUpdateTime.setText("最后刷新时间: " + getLastUpdateTime());

	    headerView.measure(0, 0); // 系统会帮我们测量出headerView的高度
	    headerViewHeight = headerView.getMeasuredHeight();
	    headerView.setPadding(0, -headerViewHeight, 0, 0);
	    this.addHeaderView(headerView); // 向ListView的顶部添加一个view对象
	    initAnimation();
	  }

	  /**
	   * 获得系统的最新时间
	   * 
	   * @return
	   */
	  private String getLastUpdateTime() {
	    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	    return sdf.format(System.currentTimeMillis());
	  }

	  /**
	   * 初始化动画
	   */
	  private void initAnimation() {
	    upAnimation = new RotateAnimation(0f, -180f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);
	    upAnimation.setDuration(500);
	    upAnimation.setFillAfter(true); // 动画结束后, 停留在结束的位置上

	    downAnimation = new RotateAnimation(-180f, -360f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);
	    downAnimation.setDuration(500);
	    downAnimation.setFillAfter(true); // 动画结束后, 停留在结束的位置上
	  }

	  
	  /**
	   * listview的触摸事件
	   */
	  @Override
	  public boolean onTouchEvent(MotionEvent ev) {
	    switch (ev.getAction()) {
	      case MotionEvent.ACTION_DOWN :
	        downY = (int) ev.getY();
	        break;
	      case MotionEvent.ACTION_MOVE :
	    	  //移动中的Y
	        int moveY = (int) ev.getY();
	        
	        // 移动中的y - 按下的y = 间距.
	        int diff = (moveY - downY) / 2;
	        
	        // -头布局的高度 + 间距 = paddingTop
	        int paddingTop = -headerViewHeight + diff;
	        
	        // 如果: -头布局的高度 > paddingTop的值 执行super.onTouchEvent(ev);
	        if (firstVisibleItemPosition == 0&& -headerViewHeight < paddingTop) {
	        	
	          if (paddingTop > 0 && currentState == DOWN_PULL_REFRESH) {
	        	  // 完全显示了.
	            Log.i(TAG, "松开刷新");
	            currentState = RELEASE_REFRESH;
	            refreshHeaderView();
	          } else if (paddingTop < 0
	              && currentState == RELEASE_REFRESH) { // 没有显示完全
	            Log.i(TAG, "下拉刷新");
	            currentState = DOWN_PULL_REFRESH;
	            refreshHeaderView();
	          }
	          // 下拉头布局
	          headerView.setPadding(0, paddingTop, 0, 0);
	          return true;
	        }
	        break;
	      case MotionEvent.ACTION_UP :
	        // 判断当前的状态是松开刷新还是下拉刷新
	        if (currentState == RELEASE_REFRESH) {
	          Log.i(TAG, "刷新数据.");
	          // 把头布局设置为完全显示状态
	          headerView.setPadding(0, 0, 0, 0);
	          // 进入到正在刷新中状态
	          currentState = REFRESHING;
	          refreshHeaderView();

	          if (mOnRefershListener != null) {
	            mOnRefershListener.onDownPullRefresh(); // 调用使用者的监听方法
	          }
	        } else if (currentState == DOWN_PULL_REFRESH) {
	          // 隐藏头布局
	          headerView.setPadding(0, -headerViewHeight, 0, 0);
	        }
	        break;
	      default :
	        break;
	    }
	    return super.onTouchEvent(ev);
	  }

	  /**
	   * 根据currentState刷新头布局的状态
	   */
	  private void refreshHeaderView() {
	    switch (currentState) {
	      case DOWN_PULL_REFRESH : // 下拉刷新状态
	        tvState.setText("下拉刷新");
	        ivArrow.startAnimation(downAnimation); // 执行向下旋转
	        break;
	      case RELEASE_REFRESH : // 松开刷新状态
	        tvState.setText("松开刷新");
	        ivArrow.startAnimation(upAnimation); // 执行向上旋转
	        break;
	      case REFRESHING : // 正在刷新中状态
	        ivArrow.clearAnimation();
	        ivArrow.setVisibility(View.GONE);
	        mProgressBar.setVisibility(View.VISIBLE);
	        tvState.setText("正在刷新中...");
	        break;
	      default :
	        break;
	    }
	  }

	  /**
	   * 当滚动状态改变时回调
	   */
	  @Override
	  public void onScrollStateChanged(AbsListView view, int scrollState) {

	    if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_FLING) {
	      // 判断当前是否已经到了底部
	      if (isScrollToBottom && !isLoadingMore) {
	        isLoadingMore = true;
	        // 当前到底部
	        Log.i(TAG, "加载更多数据");
	        footerView.setPadding(0, 0, 0, 0);
	        this.setSelection(this.getCount());

	        if (mOnRefershListener != null) {
	          mOnRefershListener.onLoadingMore();
	        }
	      }
	    }
	  }


	  /**
	   * 设置刷新监听事件
	   * 
	   * @param listener
	   */
	  public void setOnRefreshListener(OnRefreshListener listener) {
	    mOnRefershListener = listener;
	  }

	  /**
	   * 隐藏头布局
	   */
	  public void hideHeaderView() {
	    headerView.setPadding(0, -headerViewHeight, 0, 0);
	    ivArrow.setVisibility(View.VISIBLE);
	    mProgressBar.setVisibility(View.GONE);
	    tvState.setText("下拉刷新");
	    tvLastUpdateTime.setText("最后刷新时间: " + getLastUpdateTime());
	    currentState = DOWN_PULL_REFRESH;
	  }

	  /**
	   * 隐藏脚布局
	   */
	  public void hideFooterView() {
	    footerView.setPadding(0, -footerViewHeight, 0, 0);
	    isLoadingMore = false;
	  }
	  public interface OnRefreshListener {

		  /**
		   * 下拉刷新
		   */
		  void onDownPullRefresh();

		  /**
		   * 上拉加载更多
		   */
		  void onLoadingMore();

		void onRefresh();
		}
	/**
	 * 当滚动时调用
	 * 
	 * @param firstVisibleItem
	 *            当前屏幕显示在顶部的item的position
	 * @param visibleItemCount
	 *            当前屏幕显示了多少个条目的总数
	 * @param totalItemCount
	 *            ListView的总条目的总数
	 */
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
			firstVisibleItemPosition = firstVisibleItem;
		    if (getLastVisiblePosition() == (totalItemCount - 1)) {
		      isScrollToBottom = true;
		    } else {
		      isScrollToBottom = false;
		    }
		
	}
	  
	  
}
这就是上边代码中有用到的布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include 
        layout="@layout/listview_header"
        android:visibility="gone"/>
    
    <com.example.handerview.RefreshListView
        android:id="@+id/refreshlistview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
    </com.example.handerview.RefreshListView>
    
    <include 
        layout="@layout/listview_footer"
        android:visibility="gone"/>
    
    
</LinearLayout>


上拉加载和下拉刷新都需要有一个布局来占位和显示内容,包括刷新或加载过程中的动态图片

导入的头布局

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >
    
    <FrameLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dip" >

    <ImageView
      android:id="@+id/iv_listview_header_arrow"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:minWidth="30dip"
      android:src="@drawable/ic_launcher" />

    <ProgressBar
      android:id="@+id/pb_listview_header"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:indeterminateDrawable="@anim/common_progressbar"
      android:visibility="gone" />
  </FrameLayout>

  <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:gravity="center_horizontal"
    android:orientation="vertical" >

    <TextView
      android:id="@+id/tv_listview_header_state"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="下拉刷新"
      android:textColor="#FF0000"
      android:textSize="18sp" />

    <TextView
      android:id="@+id/tv_listview_header_last_update_time"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="5dip"
      android:text="最后刷新时间: 2014-10-10 12:56:12"
      android:textColor="@android:color/white"
      android:textSize="14sp" />
  </LinearLayout>

</LinearLayout>
</span>
导入的脚布局

<span style="font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    
    <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_margin="10dip"
    android:gravity="center_vertical"
    android:orientation="horizontal" >

    <ProgressBar
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      android:indeterminateDrawable="@anim/common_progressbar"
       />

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginLeft="10dip"
      android:text="加载更多..."
      android:textColor="#FF0000"
      android:textSize="18sp" />

</LinearLayout>
</LinearLayout>
</span>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章