自定義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>


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