Android開発におけるListViewの利点:ドロップダウン・リフレッシュと最終ロードのソース・コードの追加

37745 ワード

今日もプロジェクトはもうすぐ终わって、ブログを书いて、私のListViewのプルダウンの更新と底のロードに対してもっと多くの理解を记录します
まず、ListViewにはドロップダウン・リフレッシュのインタフェースがないことを知っています.既存の機能を利用して、より多くの機能を拡張するには、ListViewを少し修正して、私たちのニーズを満たす必要があります.
一、ListViewの表面理解は、ListViewの形式で表示されるView、すなわち、1行1行の表示であり、ListViewにアダプタを追加することは少なくない.したがって、変更後のListViewはもちろん、アダプタなどの一般的なListViewに対する操作を追加することもできます.
二、私たちのリストインタフェースに対する操作をキャプチャするには、ドロップダウンまたはスクロール下部にイベントに対するリスナーが必要です.ここでは特に重要です.私たちはボタンにリスナーを追加することを知っています.私たちはあるボタンに操作を加えたことをキャプチャすることができます.それから、私たちの操作を実行することができます.ここのドロップダウンまたはスクロール下部では、もちろんこの動作をキャプチャし、私たちが実行したい動作を実行する必要があります.
三、原理は私たちが知っていて、今どのようにこの動作を捕獲するかを残して、それから動作を捕獲した後に私たちはどのように私たちが望んでいる操作を追加します.アクションをキャプチャすれば、あなたが何をしたいのかは気にしません.需要がいろいろあるからです.
四、アクションをキャプチャした後、私たちはユーザーに反応を始めました...
次に、このDemoのコード1、ListViewの継承を見てみましょう.

package com.example.pulldownview;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;


/**
 * <p>      ListView                  </p>
 *           ,   ListView  Flying   ,     </br>
 *       ,      scroll        
 * @author solo ho</br> Email:[email protected]
 */

public class ScrollOverListView extends ListView {

	private static final String TAG = "ScrollOverListView";
	private static final boolean DEBUG = false;
	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 onInterceptTouchEvent(MotionEvent ev) {
		if (ev.getAction() == MotionEvent.ACTION_DOWN) {
			if (DEBUG) Log.d(TAG, "onInterceptTouchEvent Action down");
			mLastY = (int) ev.getRawY();
		}
		return super.onInterceptTouchEvent(ev);
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		final int action = ev.getAction();
		final int y = (int) ev.getRawY();
		
		boolean isHandled = false;
		switch(action){
			case MotionEvent.ACTION_DOWN:{
				if (DEBUG) Log.d(TAG, "action down");
				mLastY = y;
				isHandled = mOnScrollOverListener.onMotionDown(ev);
				if (isHandled) {
					break;
				}
				break;
			}
			
			case MotionEvent.ACTION_MOVE:{
				if (DEBUG) Log.d(TAG, "action move");
				final int childCount = getChildCount();
				if(childCount == 0) {
					break;
				}
				
				final int itemCount = getAdapter().getCount() - mBottomPosition;
				
				final int deltaY = y - mLastY;
				if (DEBUG) Log.d(TAG, "lastY=" + mLastY +" y=" + 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();
				
				isHandled = mOnScrollOverListener.onMotionMove(ev, deltaY);
				
				if(isHandled){
					break;
				}
				
				//DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY);
				if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) {
					if (DEBUG) Log.d(TAG, "action move pull down");
		            isHandled = mOnScrollOverListener.onListViewTopAndPullDown(ev, deltaY);
		            if(isHandled){
		            	break;
		            }
		        }
				
				// DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY);
		        if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) {
		        	if (DEBUG) Log.d(TAG, "action move pull up");
		        	isHandled = mOnScrollOverListener.onListViewBottomAndPullUp(ev, deltaY);
		        	if(isHandled){
		        		break;
		        	}
		        }
				break;
			}
			
			case MotionEvent.ACTION_CANCEL:
			case MotionEvent.ACTION_UP:{
				if (DEBUG) Log.d(TAG, "action move pull up");
				isHandled = mOnScrollOverListener.onMotionUp(ev);
				if (isHandled) {
					break;
				}
				break;
			}
		}
		
		mLastY = y;
		if (isHandled) {
			return true;
		}
		return super.onTouchEvent(ev);
	}
	
	
	/**  */
	private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){

		@Override
		public boolean onListViewTopAndPullDown(MotionEvent event, int delta) {
			return false;
		}

		@Override
		public boolean onListViewBottomAndPullUp(MotionEvent event, 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(index < 0)
			throw new IllegalArgumentException("Top position must > 0");
		
		mTopPosition = index;
	}
	
	/**
	 *               ,             ,       
	 * 
	 * @param index      ,          
	 */
	public void setBottomPosition(int index){
		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)
	 * 
	 * @author solo ho</br> Email:[email protected]
	 */
	public interface OnScrollOverListener {
		
		/**
		 *        
		 * 
		 * @param delta             
		 * @return 
		 */
		boolean onListViewTopAndPullDown(MotionEvent event, int delta);

		/**
		 *        
		 * 
		 * @param delta             
		 * @return 
		 */
		boolean onListViewBottomAndPullUp(MotionEvent event, 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);
		
	}


}


二、更新する

package com.example.pulldownview;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
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.TextView;

import com.example.pulldownview.ScrollOverListView.OnScrollOverListener;

/**
 *       </br>
 *               ,
 * ScrollOverListView          
 * @author Solo Email:[email protected]
 */
public class PullDownView extends LinearLayout implements OnScrollOverListener, OnScrollListener {
	private static final String TAG = "PullDownView";
	private static final boolean DEBUG = false;
	
	private static final int AUTO_INCREMENTAL = 10;		//    ,    
	
	private static final int WHAT_SET_HEADER_HEIGHT = 1;// Handler what     
	
	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 Context mContext;
	private Field overScrollModeField;
	
	private int mMoveDeviation;				//     
	private int mHeaderIncremental;			//          
	private int mDefaultHeaderViewHeight;	//          
	private int mStartIndex;				//   List      
	//private int mEndIndex;					//   List       
	
	private float mMotionDownLastY;	//      Y   
	
	private boolean mEnablePullDown;		//       
	private boolean mIsPullUpDone;			//       
	private boolean mEnableLoadMore;			//       
	private boolean mEnableAutoFetchMore;	//           
	private boolean mIsNoMoreData;			//         
	private boolean mIsDidLoad;				//        
	
	//        
	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;
	
	private static final int STATE_NONE = 0;
	private static final int STATE_REFRESHING = 1;		//    
	private static final int STATE_LOADING_MORE = 2;	//      
	private static final int STATE_DRAGING = 4;			//    
	private static final int STATE_MOTION_DOWN = 8;		//   
	private int state = STATE_NONE;

	public PullDownView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initHeaderViewAndFooterViewAndListView(context);
	}

	public PullDownView(Context context) {
		super(context);
		initHeaderViewAndFooterViewAndListView(context);
	}
	
	/*
	 * ==================================
	 * Public method
	 *     ,            
	 * 
	 * ==================================
	 */
	
	/**
	 *       
	 * @author Solo Email:[email protected]
	 */
	public interface OnPullDownListener {
		void onRefresh();
		void onLoadMore();
	}
	
	/**
	 *         ,   Adapter.notifyDataSetChanged  
	 *           ,    notifyDidLoad()
	 *       ,       
	 */
	public void notifyDidDataLoad(boolean isNoMoreData) {
		mIsDidLoad = true;
		mIsNoMoreData = isNoMoreData;
		mFooterView.setVisibility(View.VISIBLE);
		updateFooter();
		mListView.setFooterDividersEnabled(true);
		
		mHeaderViewParams.height = 0;
		mHeaderView.setLayoutParams(mHeaderViewParams);
		updateHeader();
		
		doListViewIdleActionOnDataDidLoad();
	}
	
	/**
	 *         ,   Adapter.notifyDataSetChanged  
	 *            ,    notifyDidRefresh()
	 *             
	 */
	public void notifyDidRefresh(boolean isNoMoreData) {
		mIsNoMoreData = isNoMoreData;
		updateFooter();
		
		state &= ~STATE_REFRESHING;
		mHeaderViewState = HEADER_VIEW_STATE_IDLE;
		setHeaderHeight(0);
		updateHeader();
		
		doListViewIdleActionOnDataDidLoad();
	}
	
	/**
	 *           ,   Adapter.notifyDataSetChanged  
	 *            ,    notyfyDidMore()
	 *           
	 */
	public void notifyDidLoadMore(boolean isNoMoreData) {
		mIsNoMoreData = isNoMoreData;
		state &= ~STATE_LOADING_MORE;
		updateFooter();
	}

	/**
	 *      
	 * @param listener
	 */
	public void setOnPullDownListener(OnPullDownListener listener){
		mOnPullDownListener = listener;
	}

	/**
	 *      listview
	 * @return ScrollOverListView
	 */
	public ScrollOverListView getListView(){
		return mListView;
	}

	/**
	 *           </br>
	 *       ,               
	 * @param index        
	 */
	public void enableAutoFetchMore(boolean enable, int index){
		if(!mEnableLoadMore) return;
		mEnableAutoFetchMore = enable;
		if(enable){
			mListView.setBottomPosition(index);
		}else{
			updateFooter();
		}
	}
	
	/**
	 *         </br>
	 *            
	 */
	public void enableLoadMore(boolean enable) {
		mEnableLoadMore = enable;
		if (!enable) {
			// TODO            
			//             
			mUIHandler.post(new Runnable() {
				
				@Override
				public void run() {
					removeFooter();
				}
			});
		}
	}
	
	/**
	 *         
	 */
	public void enablePullDown(boolean enable) {
		mEnablePullDown = enable;
	}
	
	/*
	 * ==================================
	 * Private method
	 *            
	 * 
	 * ==================================
	 */
	
	/**
	 *      
	 */
	private void initHeaderViewAndFooterViewAndListView(Context context){
		setOrientation(LinearLayout.VERTICAL);
		
		mContext = context;
		
		/*
		 *        
		 *                    
		 *      ,           
		 */
		mEnablePullDown = true;
		mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null);
		mHeaderViewParams = new LayoutParams(android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT);
		mHeaderIncremental = mDefaultHeaderViewHeight;
		addView(mHeaderView, 0, mHeaderViewParams);
		
		mDefaultHeaderViewHeight = getResources().getDimensionPixelSize(R.dimen.pulldown_headerview_height);
		mMoveDeviation = getResources().getDimensionPixelSize(R.dimen.pulldown_move_deviation);
		mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text);
		mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow);
		mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date);
		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);
		
		
		/*
		 *        
		 */
		mEnableLoadMore = true;
		mFooterView = LayoutInflater.from(mContext).inflate(R.layout.pulldown_footer, null);
		mFooterView.setVisibility(View.GONE);
		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 (mIsNoMoreData || !mIsDidLoad || (state & STATE_LOADING_MORE) == STATE_LOADING_MORE) return;
				ScrollOverListView listView = mListView;
				if (listView.getCount() - listView.getHeaderViewsCount() - listView.getFooterViewsCount() > 0) {
					state |= STATE_LOADING_MORE;
					updateFooter();
					mOnPullDownListener.onLoadMore();
				}
			}
		});
		
		
		/*
		 * ScrollOverListView             ,      
		 *     ,        
		 */
		mListView = new ScrollOverListView(context);
		mListView.setFooterDividersEnabled(false);
		mListView.setId(android.R.id.list);
		mListView.addFooterView(mFooterView);
		mListView.setOnScrollOverListener(this);
		mListView.setOnScrollListener(this);
		
		//   2.3     ListView       pull      
		//            
		try {
			Method method = AbsListView.class.getDeclaredMethod("setOverScrollMode", int.class);
			method.setAccessible(true);
			method.invoke(mListView, 2);//View.OVER_SCROLL_NEVER
		} catch (Exception e) {
			e.printStackTrace();
		}
		addView(mListView, android.view.ViewGroup.LayoutParams.FILL_PARENT, android.view.ViewGroup.LayoutParams.FILL_PARENT);

		// set ListView animation
		/*
		AnimationSet set = new AnimationSet(true);

        Animation animation = new AlphaAnimation(0.0f, 1.0f);
        animation.setDuration(50);
        set.addAnimation(animation);

        animation = new TranslateAnimation(
            Animation.RELATIVE_TO_SELF, 0.0f,Animation.RELATIVE_TO_SELF, 0.0f,
            Animation.RELATIVE_TO_SELF, -1.0f,Animation.RELATIVE_TO_SELF, 0.0f
        );
        animation.setDuration(100);
        set.addAnimation(animation);

        LayoutAnimationController controller = new LayoutAnimationController(set, 0.5f);
        mListView.setLayoutAnimation(controller);
		*/
		
		//   listener
		mOnPullDownListener = new OnPullDownListener() {
			@Override
			public void onRefresh() {}
			@Override
			public void onLoadMore() {}
		};
	}
	
	/**
	 *       
	 */
	private void removeFooter(){
		if(mListView.getFooterViewsCount() > 0 && mListView != null && mFooterView != null){
			mListView.removeFooterView(mFooterView);
		}
	}
	
	/**
	 *       </br>
	 *        Loading,
	 *      ,      ,
	 *         。
	 */
	private void updateHeader() {
		if ((state & STATE_REFRESHING) == STATE_REFRESHING) {
			mHeaderArrowView.clearAnimation();
			mHeaderArrowView.setVisibility(View.GONE);
			mHeaderLoadingView.setVisibility(View.VISIBLE);
			mHeaderTextView.setText("     ...");
		} else if ((state & STATE_DRAGING) == STATE_DRAGING) {
			if(mHeaderViewParams.height >= mDefaultHeaderViewHeight){
				if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return;
				mHeaderArrowView.setVisibility(View.VISIBLE);
				mHeaderLoadingView.setVisibility(View.GONE);
				mHeaderViewDateView.setVisibility(View.VISIBLE);
				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;
				mHeaderArrowView.setVisibility(View.VISIBLE);
				mHeaderLoadingView.setVisibility(View.GONE);
				mHeaderViewDateView.setVisibility(View.VISIBLE);
				mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT;
				mHeaderTextView.setText("      ");
				mHeaderArrowView.startAnimation(mRotate180To0Animation);
			}
		} else {
			mHeaderLoadingView.setVisibility(View.GONE);
			mHeaderViewDateView.setVisibility(View.VISIBLE);
			mHeaderArrowView.setVisibility(View.VISIBLE);
			mHeaderTextView.setText("      ");
			mHeaderViewDateView.setText("   :" + dateFormat.format(new Date(System.currentTimeMillis())));
		}
	}
	
	/** 
	 *       </br>
	 *     "  ",
	 *             "      ",
	 *         "   ..."。
	 */
	private void updateFooter() {
		if (!mEnableLoadMore) return;
		
		if (mIsNoMoreData) {
			mFooterTextView.setText("      ");
			mFooterLoadingView.setVisibility(View.GONE);
		} else if ((state & STATE_LOADING_MORE) == STATE_LOADING_MORE) {
			mFooterTextView.setText("     ...");
			mFooterLoadingView.setVisibility(View.VISIBLE);
		} else {
			mFooterTextView.setText("  ");
			mFooterLoadingView.setVisibility(View.GONE);
		}
	}
	
	private void setHeaderHeight(final int height){
		mHeaderIncremental = height;
		mHeaderViewParams.height = height;
		mHeaderView.setLayoutParams(mHeaderViewParams);
	}

	/**
	 *       
	 */
	class HideHeaderViewTask extends TimerTask{
		@Override
		public void run() {
			if((state & STATE_MOTION_DOWN) == STATE_MOTION_DOWN) {
				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((state & STATE_MOTION_DOWN) == STATE_MOTION_DOWN) {
				cancel();
				return;
			}
			mHeaderIncremental -= AUTO_INCREMENTAL;
			if(mHeaderIncremental > mDefaultHeaderViewHeight){
				mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
			}else{
				mHeaderIncremental = mDefaultHeaderViewHeight;
				mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT);
				if (mIsDidLoad && (state & STATE_REFRESHING) != STATE_REFRESHING) {
					state |= STATE_REFRESHING;
					mUIHandler.post(new Runnable() {
						
						@Override
						public void run() {
							//       ,      
							updateHeader();
							mHeaderArrowView.clearAnimation();
							mHeaderArrowView.setVisibility(View.INVISIBLE);
							mHeaderLoadingView.setVisibility(View.VISIBLE);
							mOnPullDownListener.onRefresh();
						}
					});
				}
				cancel();
			}
		}
	}


	private Handler mUIHandler = new Handler(){

		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
				case WHAT_SET_HEADER_HEIGHT :{
					setHeaderHeight(mHeaderIncremental);
					return;
				}
			}
		}
		
	};
	
	/**
	 *        ,    onIdle  
	 */
	private void doListViewIdleActionOnDataDidLoad(){
		new Handler().postDelayed(new Runnable() {
			
			@Override
			public void run() {
				if(mOnListViewIdleListener != null){
					//      FooterView
					final int firstVisiblePosition = mListView.getFirstVisiblePosition();
					final int childCount = mListView.getChildCount(); 
					//Log.d(TAG, "[doListViewIdleActionOnDataDidLoad] firstVisiblePosition:" + firstVisiblePosition + " childCount:" + childCount);
					mOnListViewIdleListener.onIdle(firstVisiblePosition, childCount);
				}
			}
		}, 0);
	}
	
	/**
	 *           
	 */
	private boolean isFillScreenItem(){
		final int firstVisiblePosition = mListView.getFirstVisiblePosition();
		final int lastVisiblePosition = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount();
		final int visibleItemCount = lastVisiblePosition - firstVisiblePosition + 1;
		final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount();
		
		if(visibleItemCount < totalItemCount) return true;
		return false;
	}
	
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		if (ev.getAction() == MotionEvent.ACTION_DOWN) {
			state |= STATE_MOTION_DOWN;
			mIsPullUpDone = false;
			mMotionDownLastY = ev.getRawY();
			Log.d(TAG, "pulldownview.onIntercept:" + mMotionDownLastY);
		}
		return super.onInterceptTouchEvent(ev);
	}
	
	/*
	 * ==================================
	 *    OnScrollOverListener  
	 * 
	 * 
	 * ==================================
	 */

	@Override
	public boolean onListViewTopAndPullDown(MotionEvent event, int delta) {
		//     ListView             
		//         true,  ListView    ,       
		if ((state & STATE_REFRESHING) == STATE_REFRESHING || !mEnablePullDown || !mIsDidLoad) {
			return true;
		}
		
		if (mListView.getCount() - mListView.getFooterViewsCount() == 0) {
			return true;
		}
		
		//                  ,    
		if(mHeaderViewParams.height <= 0){
			final int absMotionY = (int) Math.abs(event.getRawY() - mMotionDownLastY);
			if(absMotionY < mMoveDeviation) {
				return true;
			}
		}
		
		int absDelta = Math.abs(delta);
		final int i = (int) Math.ceil((double)absDelta / 2);
		mHeaderIncremental += i;
		if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight
			setHeaderHeight(mHeaderIncremental);
			updateHeader();
		}
		return true;
	}

	@Override
	public boolean onListViewBottomAndPullUp(MotionEvent event, int delta) {
		if((state & STATE_LOADING_MORE) == STATE_LOADING_MORE
				|| !mIsDidLoad || !mEnableAutoFetchMore || mIsNoMoreData) return false;
		ScrollOverListView listView = mListView;
		if (listView.getCount() - listView.getHeaderViewsCount() - listView.getFooterViewsCount() > 0) {
			state |= STATE_LOADING_MORE;
			updateFooter();
			mOnPullDownListener.onLoadMore();
		}
		return true;
	}

	@Override
	public boolean onMotionDown(MotionEvent ev) {
		return false;
	}

	@Override
	public boolean onMotionMove(MotionEvent ev, int delta) {
		state |= STATE_DRAGING;
		//            ,     
		if(mIsPullUpDone) return true;
		
		// onTopDown   ,      onTopUp  
		if(mHeaderViewParams.height > 0 && delta < 0){
			final int absDelta = Math.abs(delta);
			final int i = (int) Math.ceil((double)absDelta / 2);
			
			mHeaderIncremental -= i;
			if(mHeaderIncremental > 0){
				setHeaderHeight(mHeaderIncremental);
				updateHeader();
			}else{
				mHeaderViewState = HEADER_VIEW_STATE_IDLE;
				mHeaderIncremental = 0;
				setHeaderHeight(mHeaderIncremental);
				mIsPullUpDone = true;
			}
			return true;
		}
		return false;
	}

	@Override
	public boolean onMotionUp(MotionEvent ev) {
		state &= ~STATE_DRAGING;
		state &= ~STATE_MOTION_DOWN;
		//          
		if(mHeaderViewParams.height > 0 || mIsPullUpDone){
			
			//                 ,     ,       
			int x = mHeaderIncremental - mDefaultHeaderViewHeight;
			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;//TODO
	}
	
	/*
	 * =====================================
	 *   ListView    
	 * =====================================
	 */
	
	private OnListViewIdleListener mOnListViewIdleListener;
	
	public interface OnListViewIdleListener {
		void onIdle(int startIndex, int count);
	}
	
	public void setOnListViewIdleListener(OnListViewIdleListener listener){
		if(mListView != null){
			mOnListViewIdleListener = listener;
		}
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if(scrollState == SCROLL_STATE_IDLE){
			if (DEBUG) Log.d(TAG, "IDLE");
			if(mOnListViewIdleListener != null){
				int count = mListView.getChildCount();
				final int childEndIndex = mStartIndex + mListView.getChildCount() -1;
				final int listEndIndex = mListView.getCount() -1;
				if(childEndIndex == listEndIndex){
					count -= mListView.getFooterViewsCount();
				}
				mOnListViewIdleListener.onIdle(mStartIndex, count);
			}
		}
		switch (scrollState) {
		case SCROLL_STATE_FLING:
			if (DEBUG) Log.d(TAG, "FLING");
			break;
		case SCROLL_STATE_TOUCH_SCROLL:
			if (DEBUG) Log.d(TAG, "SCROLL");
		default:
			break;
		}
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		if (DEBUG) Log.d(TAG, "onScroll");
		mStartIndex = firstVisibleItem;
		
		//     I91002.3      over_scroll_never       ,         
		//   12-9-9 2:08:  ,    2.3.3               。
		/*
		final ScrollOverListView localListView = this.mListView;		
		final boolean hasItem = localListView.getCount() > 0;

		try {
			if (overScrollModeField == null) return;
			final Integer mode = (Integer) overScrollModeField.get(localListView);
			
			if (firstVisibleItem <= 0 && hasItem) {
				if (mode != View.OVER_SCROLL_NEVER) {
					if (DEBUG) Log.w(TAG, "set over scroll never");
					overScrollModeField.set(localListView, View.OVER_SCROLL_NEVER);
				}
			} else if (firstVisibleItem + visibleItemCount >= totalItemCount && hasItem) {
				if (mode != View.OVER_SCROLL_ALWAYS) {
					if (DEBUG) Log.w(TAG, "set over scroll always");
					overScrollModeField.set(localListView, View.OVER_SCROLL_ALWAYS);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		*/
	}

}


三、Demoインタフェース

package com.example.pulldownview;

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

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.example.pulldownview.PullDownView.OnPullDownListener;

public class MainActivity extends Activity {

    private PullDownView pullDownView;
	private ScrollOverListView listView;
	private MyAdapter adapter;
	private List<String> arrays;
	private LayoutInflater inflater;

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        inflater = getLayoutInflater();
        
        pullDownView = (PullDownView) findViewById(R.id.pulldownview);
        pullDownView.enableAutoFetchMore(true, 0);
        listView = pullDownView.getListView();
        adapter = new MyAdapter();
        listView.setAdapter(adapter);
        
        initArrays(new Handler(){
        	@Override
        	public void handleMessage(Message msg) {
        		arrays = (List<String>) msg.obj;
        		adapter.notifyDataSetChanged();
        		pullDownView.notifyDidDataLoad(false);
        	}
        });
        
        pullDownView.setOnPullDownListener(new OnPullDownListener() {
			
			@Override
			public void onRefresh() {
				getNewString(new Handler(){
					@Override
					public void handleMessage(Message msg) {
						arrays.add(0, (String) msg.obj);
						adapter.notifyDataSetChanged();
						pullDownView.notifyDidRefresh(arrays.isEmpty());
					}
				});
			}
			
			@Override
			public void onLoadMore() {
				getNewString(new Handler(){
					@Override
					public void handleMessage(Message msg) {
						arrays.add((String) msg.obj);
						adapter.notifyDataSetChanged();
						pullDownView.notifyDidLoadMore(arrays.isEmpty());
					}
				});
			}
		});
    }
	
	private void initArrays(final Handler handler) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(3000);
				} catch (Exception e) {
					Thread.interrupted();
					e.printStackTrace();
				}
				
				List<String> as = new ArrayList<String>();
				as.add("first");
				as.add("second");
				as.add("third");
				as.add("four");
				as.add("five");
				as.add("first");
				as.add("second");
				as.add("third");
				as.add("four");
				as.add("five");
				as.add("first");
				as.add("second");
				as.add("third");
				as.add("four");
				as.add("five");
				as.add("first");
				as.add("second");
				as.add("third");
				as.add("four");
				as.add("five");
				as.add("first");
				as.add("second");
				as.add("third");
				as.add("four");
				as.add("five");
				
				handler.obtainMessage(0, as).sendToTarget();
			}
		}).start();
	}
	
	private void getNewString(final Handler handler) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(2000);
				} catch (Exception e) {
					Thread.interrupted();
					e.printStackTrace();
				}
				handler.obtainMessage(0, "New Text " + System.currentTimeMillis()).sendToTarget();
			}
		}).start();
	}

	
	private class MyAdapter extends BaseAdapter {

		@Override
		public int getCount() {
			return arrays == null ? 0 : arrays.size();
		}

		@Override
		public Object getItem(int position) {
			return null;
		}

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

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			ViewHolder holder;
			if (convertView == null) {
				holder = new ViewHolder();
				convertView = inflater.inflate(R.layout.item_list, null);
				holder.textView = (TextView) convertView.findViewById(R.id.text);
				convertView.setTag(holder);
			} else {
				holder = (ViewHolder) convertView.getTag();
			}
			holder.textView.setText(arrays.get(position));
			
			return convertView;
		}
	}
	
	private static class ViewHolder {
		TextView textView;
	}
}