android 개발 중ListView의 묘용: 밑에 있는 리셋과 끝까지 더 많은 원본 불러오기

37758 단어 android
오늘 프로젝트도 곧 끝나니까 블로그를 한 편 써서ListView의 밑줄 리셋과 밑에 더 많은 이해를 기록해 주세요.
먼저 ListView는 새로 고침된 인터페이스가 없다는 것을 알았습니다. 기존의 기능을 빌려 더 많은 기능을 확장하려면 ListView가 우리의 요구를 충족시킬 수 있도록 수정해야 합니다.
1. ListView의 표면적인 이해는 List의 형식으로 표시된 View이다. 즉, 한 줄 한 줄 표시를 하려면 ListView에 어댑터를 추가하는 것이 없어서는 안 된다.그래서 우리가 수정한 ListView는 당연히 어댑터 등 흔히 볼 수 있는 ListView에 대한 조작을 추가할 수 있다.
2. 우리가 목록 인터페이스에 대한 조작을 포착하려면 아래로 내려가거나 끝까지 스크롤하려면 사건에 대한 감청기가 있어야 한다. 여기서 특히 중요하다. 우리는 단추에 감청기를 추가하는 것을 알고 있다. 우리는 우리가 어떤 단추에 조작을 한 것을 포착하고 우리의 조작을 수행할 수 있다. 여기서 아래로 내려가거나 끝까지 스크롤할 수 있다.우리도 당연히 이 동작을 포착하고 우리가 실행하고자 하는 동작을 실행해야 한다.
3. 원리는 우리가 알고 있다. 이제 이 동작을 어떻게 포획하는지, 그리고 포획 동작을 포획한 후에 우리가 원하는 조작을 어떻게 추가하는지.동작만 잡으면 네가 무엇을 하고 싶은지 나는 관심이 없다. 왜냐하면 수요가 각양각색이기 때문이다.
4. 동작을 포착한 후에 우리는 사용자에게 반응을 보이기 시작한다...
다음은 이 데모의 코드를 살펴보겠습니다.

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);
		
	}


}


2. 내려와서 리셋

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();
		}
		*/
	}

}


3. 데모 인터페이스

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;
	}
}


좋은 웹페이지 즐겨찾기