zoukankan      html  css  js  c++  java
  • ListView下拉刷新及上拉更多两种状态

    一、前言:

            很多应用都会用到ListView,当然如果是iOS就会用UITableViewController,这两个控件在不同的OS上,功能是一样的,只是有些细微的不同(iOS的UITableViewController支持静态与动态两种),不过,大多数应用都用的是动态属性,那么,这里就涉及到一个问题:刷新及加载更多内容。

            目前网上流行的有两种方式:

            1. 通用的方法,即将ListView, GridView和ScrollView当成ChildView,在这顶部及底部各加一个Layout,但是,一但出现了,就一直显示在顶部或底部,并不会随着ChildView的滚动而滚动,功能实用,就是有点破坏美感;

            2. 各自实现,即如果需要实现ListView的下拉刷新和上拉更多,那么就得去继承ListView,并对它的HeaderView和FooterView做一些扩展,同理,GridView和ScrollView;

            本篇将使用第二种方法来实现,如果通过继承ListView的方式,来实现下拉刷新,以及上拉更多,或者是点击底部加载更多的。

    二、实现:

    2.1 HeaderView的布局,以及代码实现

    <?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:background="#ffffff"
        android:gravity="bottom">
        
        <RelativeLayout 
            android:id="@+id/header_content"
            android:layout_width="match_parent"
            android:layout_height="60dip">
            
            <LinearLayout 
                android:id="@+id/layoutTitle"
                android:layout_width="wrap_content"
            	   android:layout_height="wrap_content"
            	   android:layout_centerInParent="true"
            	   android:gravity="center"
            	   android:orientation="vertical">
            	
                <TextView 
    	            android:id="@+id/refresh_tips"
    	            android:layout_width="wrap_content"
    	            android:layout_height="wrap_content"
    	            android:textSize="15sp"
    	            android:text="@string/pull_down_for_refresh"/>
                
                <LinearLayout 
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:orientation="horizontal"
                    android:layout_marginTop="4dip">
                    
                    <TextView 
    		            android:layout_width="wrap_content"
    		            android:layout_height="wrap_content"
    		            android:textSize="12sp"
    		            android:text="@string/label_update"/>
                    <TextView 
    		            android:id="@+id/refresh_last_time"
    		            android:layout_width="wrap_content"
    		            android:layout_height="wrap_content"
    		            android:textSize="12sp"
    		            android:text="@string/label_last_time"/>
                    
                </LinearLayout>
                
            </LinearLayout>
            
            <ImageView 
    	        android:id="@+id/ivArrow"
    	        android:layout_height="wrap_content"
    	        android:layout_width="wrap_content"
    	        android:layout_toLeftOf="@id/layoutTitle"
    	        android:layout_centerInParent="true"
    	        android:layout_marginRight="30dip"
    	        android:contentDescription="@string/image_desc"
    	        android:src="@drawable/refresh_arrow_down"/>
            
            <ProgressBar 
    	        android:id="@+id/pbWaiting"
    	        android:visibility="gone"
    	        android:layout_height="wrap_content"
    	        android:layout_width="wrap_content"
    	        android:layout_toLeftOf="@id/layoutTitle"
    	        android:layout_centerInParent="true"
    	        android:layout_marginRight="30dip"
    	        style="?android:attr/progressBarStyleSmall"/>
    
        </RelativeLayout>
    
    </LinearLayout>

    布局很简单,一些TextView,一个ImageView和一个ProgressBar。再来看看代码实现

    package com.chris.list.refresh;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.animation.Animation;
    import android.view.animation.RotateAnimation;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    public class HeaderView extends LinearLayout {
    
    	public final static int STATE_NORMAL = 0;
    	public final static int STATE_WILL_RELEASE = 1;
    	public final static int STATE_REFRESHING = 2;
    	private int mState = STATE_NORMAL;
    	
    	private View mHeader = null;
    	private ImageView mArrow = null;
    	private ProgressBar mProgressBar = null;
    	private TextView mRefreshTips = null;
    	//private TextView mRefreshLastTime = null;
    	private RotateAnimation mRotateUp = null;
    	private RotateAnimation mRotateDown = null;
    	private final static int ROTATE_DURATION = 250;
    	
    	public HeaderView(Context context) {
    		this(context, null);
    	}
    	
    	public HeaderView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		initHeaderView(context);
    	}
    
    	private void initHeaderView(Context context){
    		LinearLayout.LayoutParams lp = new LayoutParams(
    				LayoutParams.MATCH_PARENT, 0);
    		mHeader = LayoutInflater.from(context).inflate(R.layout.refresh_header, null);
    		addView(mHeader, lp);
    		setGravity(Gravity.BOTTOM);
    		
    		mArrow = (ImageView) mHeader.findViewById(R.id.ivArrow);
    		mProgressBar = (ProgressBar) mHeader.findViewById(R.id.pbWaiting);
    		mRefreshTips = (TextView) mHeader.findViewById(R.id.refresh_tips);
    		//mRefreshLastTime = (TextView) mHeader.findViewById(R.id.refresh_last_time);
    		
    		mRotateUp = new RotateAnimation(0.0f, -180.0f,  
    				Animation.RELATIVE_TO_SELF, 0.5f, 
    				Animation.RELATIVE_TO_SELF, 0.5f);
    		mRotateUp.setDuration(ROTATE_DURATION);
    		mRotateUp.setFillAfter(true);
    		
    		mRotateDown = new RotateAnimation(-180.0f, 0.0f,
    				Animation.RELATIVE_TO_SELF, 0.5f, 
    				Animation.RELATIVE_TO_SELF, 0.5f);
    		mRotateDown.setDuration(ROTATE_DURATION);
    		mRotateDown.setFillAfter(true);
    	}
    	
    	public void setHeaderState(int state){
    		if(mState == state){
    			return;
    		}
    		
    		mArrow.clearAnimation();
    		if(state == STATE_REFRESHING){
    			mProgressBar.setVisibility(View.VISIBLE);
    			mArrow.setVisibility(View.GONE);
    		}else{
    			mProgressBar.setVisibility(View.GONE);
    			mArrow.setVisibility(View.VISIBLE);
    		}
    		
    		switch(state){
    		case STATE_NORMAL:
    			mArrow.startAnimation(mRotateDown);
    			mRefreshTips.setText(R.string.pull_down_for_refresh);
    			break;
    			
    		case STATE_WILL_RELEASE:
    			mArrow.startAnimation(mRotateUp);
    			mRefreshTips.setText(R.string.release_for_refresh);
    			break;
    			
    		case STATE_REFRESHING:
    			mRefreshTips.setText(R.string.refreshing);
    			break;
    		
    		default:
    			break;
    		}
    		
    		mState = state;
    	}
    	
    	public int getCurrentState(){
    		return mState;
    	}
    	
    	public void setHeaderHeight(int height){
    		if(height <= 0){
    			height = 0;
    		}
    		LayoutParams lp = (LayoutParams) mHeader.getLayoutParams();
    		lp.height = height;
    		mHeader.setLayoutParams(lp);
    	}
    	public int getHeaderHeight(){
    		return mHeader.getHeight();
    	}
    }
    

    这个代码中,主要就两个函数:setHeaderState 和 setHeaderHeight。 前者是根据TouchEvent,以及当前移动的距离,来设置状态,同时,移动的距离去设置HeaderView的高度,达到一点一点的显示出来。

    2.2 FooterView的布局,以及代码实现

    看了HeaderView的布局与实现后,FooterView的布局与实现也差不多,咱们一起来看看吧

    <?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:gravity="top" >
    
        <RelativeLayout
            android:id="@+id/footer_content"
            android:layout_width="match_parent"
            android:layout_height="60dip" >
    
            <TextView
                android:id="@+id/loader_tips"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="@string/pull_up_for_more"
                android:textSize="15sp" />
    
            <ImageView
                android:id="@+id/ivLoaderArrow"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:layout_marginRight="30dip"
                android:layout_toLeftOf="@id/loader_tips"
                android:contentDescription="@string/image_desc"
                android:src="@drawable/refresh_arrow_up" />
    
            <ProgressBar
                android:id="@+id/pbLoaderWaiting"
                style="?android:attr/progressBarStyleSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:layout_marginRight="30dip"
                android:layout_toLeftOf="@id/loader_tips"
                android:visibility="gone" />
        </RelativeLayout>
    
    </LinearLayout>

    哇,这个布局比HeaderView布局还要简单!?这个布局涵盖了两部分,不过,在布局中无法体现出来,但在代码实现中体现出来了:

    1. 上拉更多,这个布局全部显示;

    2. 如果是滑到底部点击加载,就不会有ImageView;

    还是来看看代码实现吧

    package com.chris.list.refresh;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.animation.Animation;
    import android.view.animation.RotateAnimation;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    public class FooterView extends LinearLayout {
    
    	public final static int FOOTER_OPTIONS_PULL = 0;
    	public final static int FOOTER_OPTIONS_CLICK = 1;
    	private static int sFooterOps = FOOTER_OPTIONS_PULL;
    	
    	public final static int STATE_NORMAL = 0;
    	public final static int STATE_WILL_RELEASE = 1;
    	public final static int STATE_LOADING = 2;
    	private int mState = STATE_NORMAL;
    	
    	private View mFooter = null;
    	private ImageView mArrow = null;
    	private ProgressBar mProgressBar = null;
    	private TextView mLoaderTips = null;
    	
    	private RotateAnimation mRotateUp = null;
    	private RotateAnimation mRotateDown = null;
    	private final static int ROTATE_DURATION = 250;
    	
    	public FooterView(Context context) {
    		this(context, null);
    	}
    	
    	public FooterView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		initFooterView(context);
    	}
    	
    	private void initFooterView(Context context){
    		LinearLayout.LayoutParams lp = new LayoutParams(
    				LayoutParams.MATCH_PARENT, 0);
    		mFooter = LayoutInflater.from(context).inflate(R.layout.loader_footer, null);
    		addView(mFooter, lp);
    		
    		mArrow = (ImageView) mFooter.findViewById(R.id.ivLoaderArrow);
    		mProgressBar = (ProgressBar) mFooter.findViewById(R.id.pbLoaderWaiting);
    		mLoaderTips = (TextView) mFooter.findViewById(R.id.loader_tips);
    		
    		mRotateDown = new RotateAnimation(0.0f, 180.0f,  
    				Animation.RELATIVE_TO_SELF, 0.5f, 
    				Animation.RELATIVE_TO_SELF, 0.5f);
    		mRotateDown.setDuration(ROTATE_DURATION);
    		mRotateDown.setFillAfter(true);
    		
    		mRotateUp = new RotateAnimation(180.0f, 0.0f,
    				Animation.RELATIVE_TO_SELF, 0.5f, 
    				Animation.RELATIVE_TO_SELF, 0.5f);
    		mRotateUp.setDuration(ROTATE_DURATION);
    		mRotateUp.setFillAfter(true);
    		
    		setFooterViewOptions(FOOTER_OPTIONS_CLICK);
    	}
    	
    	public void setFooterViewOptions(int options){
    		sFooterOps = options;
    		
    		switch(sFooterOps){
    		case FOOTER_OPTIONS_PULL:
    			hide();
    			break;
    			
    		case FOOTER_OPTIONS_CLICK:
    			show();
    			break;
    			
    		default:
    			break;
    		}
    	}
    	
    	public int getFooterViewOptions(){
    		return sFooterOps;
    	}
    	
    	public void setFooterState(int state){
    		if(mState == state){
    			return;
    		}
    		
    		mArrow.clearAnimation();
    		if(state == STATE_LOADING){
    			mProgressBar.setVisibility(View.VISIBLE);
    			mArrow.setVisibility(View.GONE);
    		}else{
    			mProgressBar.setVisibility(View.GONE);
    			mArrow.setVisibility(View.VISIBLE);
    		}
    		
    		switch(state){
    		case STATE_NORMAL:
    			mArrow.startAnimation(mRotateUp);
    			mLoaderTips.setText(R.string.pull_up_for_more);
    			break;
    			
    		case STATE_WILL_RELEASE:
    			mArrow.startAnimation(mRotateDown);
    			mLoaderTips.setText(R.string.release_for_more);
    			break;
    			
    		case STATE_LOADING:
    			mLoaderTips.setText(R.string.loading);
    			break;
    		
    		default:
    			break;
    		}
    		mState = state;
    	}
    	
    	public int getCurrentState(){
    		return mState;
    	}
    	
    	public void setFooterHeight(int height){
    		if(height <= 0){
    			height = 0;
    		}
    		
    		LayoutParams lp = (LayoutParams) mFooter.getLayoutParams();
    		lp.height = height;
    		mFooter.setLayoutParams(lp);
    	}
    	
    	public int getFooterHeight(){
    		return mFooter.getHeight();
    	}
    	
    	public void hide(){
    		mArrow.clearAnimation();
    		mArrow.setVisibility(View.VISIBLE);
    		mLoaderTips.setText(R.string.pull_up_for_more);
    		setFooterHeight(0);
    	}
    	
    	public void show(){
    		mArrow.clearAnimation();
    		mArrow.setVisibility(View.GONE);
    		mLoaderTips.setText(R.string.click_for_more);
    		
    		LayoutParams lp = (LayoutParams) mFooter.getLayoutParams();
    		lp.height = LayoutParams.WRAP_CONTENT;
    		mFooter.setLayoutParams(lp);
    	}
    }
    

    代码中,有个Options函数,用来提供设置:上拉或点击。同样,也有设置状态,和设计高度。

    2.3 扩展ListView的实现

    package com.chris.list.refresh;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewTreeObserver.OnGlobalLayoutListener;
    import android.view.animation.DecelerateInterpolator;
    import android.widget.AbsListView;
    import android.widget.ListAdapter;
    import android.widget.ListView;
    import android.widget.RelativeLayout;
    import android.widget.Scroller;
    import android.widget.AbsListView.OnScrollListener;
    
    public class ListViewExt extends ListView implements OnScrollListener {
    
    	private final static String TAG = "ChrisLV";
    	
    	private HeaderView mHeaderView = null;
    	private RelativeLayout mHeaderContent = null;
    	private int iHeaderHeight = 0;
    	
    	private FooterView mFooterView = null;
    	private RelativeLayout mFooterContent = null;
    	private int iFooterHeight = 0;
    	
    	private final static int SCROLL_HEADER = 0;
    	private final static int SCROLL_FOOTER = 1;
    	private int iScrollWhich = SCROLL_HEADER;
    	
    	private Scroller mScroller = null;
    	private final static float OFFSET_Y = 0.7f;
    	private float iLastY = 0;
    	private int mTotalNumber = 0;
    	
    	public ListViewExt(Context context) {
    		this(context, null, 0);
    	}
    	public ListViewExt(Context context, AttributeSet attrs) {
    		this(context, attrs, 0);
    	}
    	public ListViewExt(Context context, AttributeSet attrs, int defStyle) {
    		super(context, attrs, defStyle);
    		initView(context);
    	}
    
    	private void initView(Context context){
    		/*
    		 * mScroller用来回弹下拉刷新/上拉更多
    		 * 配合computerScroll来使用
    		 */
    		mScroller = new Scroller(context, new DecelerateInterpolator());
    		super.setOnScrollListener(this);
    		
    		initHeaderView(context);
    		initFooterView(context);
    	}
    	
    	@Override
    	public void setAdapter(ListAdapter adapter) {
    		if(getFooterViewsCount() == 0){
    			addFooterView(mFooterView);
    		}
    		super.setAdapter(adapter);
    	}
    	
    	@Override
    	public boolean onTouchEvent(MotionEvent ev) {
    		
    		switch(ev.getAction()){
    		case MotionEvent.ACTION_DOWN:
    			iLastY = ev.getY();
    			break;
    			
    		case MotionEvent.ACTION_MOVE:
    			float deltaY = ev.getY() - iLastY;
    			iLastY = ev.getY();
    			if(canHeaderPull() && getFirstVisiblePosition() == 0 &&
    			   (deltaY > 0 || mHeaderView.getHeaderHeight() > 0)){
    				updateHeaderState(deltaY * OFFSET_Y);
    			}else if(canFooterPull() && getLastVisiblePosition() == mTotalNumber - 1
    					&& (deltaY < 0 || mFooterView.getFooterHeight() > 0)){
    				updateFooterState(-deltaY * OFFSET_Y);
    			}
    			break;
    			
    		case MotionEvent.ACTION_UP:
    			if(getFirstVisiblePosition() == 0){
    				if(mHeaderView.getHeaderHeight() > iHeaderHeight){
    				   mHeaderView.setHeaderState(HeaderView.STATE_REFRESHING);
    				   if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
    						mFooterView.hide();
    					}
    				}
    				resetHeader();
    			}else if(getLastVisiblePosition() == mTotalNumber - 1){
    				if(mFooterView.getFooterHeight() > iFooterHeight){
    					mFooterView.setFooterState(FooterView.STATE_LOADING);
    				}
    				resetFooter();
    			}
    			break;
    		
    		default:
    			break;
    		}
    		return super.onTouchEvent(ev);
    	}
    
    	@Override
    	public void computeScroll() {
    		if(mScroller.computeScrollOffset()){
    			if(iScrollWhich == SCROLL_HEADER){
    				mHeaderView.setHeaderHeight(mScroller.getCurrY());
    			}else if(iScrollWhich == SCROLL_FOOTER){
    				mFooterView.setFooterHeight(mScroller.getCurrY());
    			}
    		}
    		super.computeScroll();
    	}
    	
    	/*
    	 * 获取ListView有多少个item:
    	 * 1. 在init中,需要设置super.setOnScrollListener;
    	 * 2. 重载以下两个函数;
    	 * 3. 在onScroll中取得totalItemCount即可;
    	 */
    	@Override
    	public void onScroll(AbsListView view, int firstVisibleItem,
    			int visibleItemCount, int totalItemCount) {
    		mTotalNumber = totalItemCount;
    	}
    	@Override
    	public void onScrollStateChanged(AbsListView view, int scrollState) {
    	}
    	/////////////////////////////////////////////////////////////////////////////
    	private boolean canHeaderPull(){
    		if(mFooterView.getCurrentState() == FooterView.STATE_NORMAL){
    			return true;
    		}
    		return false;
    	}
    	
    	private boolean canFooterPull(){
    		if(mHeaderView.getCurrentState() == HeaderView.STATE_NORMAL){
    			return true;
    		}
    		return false;
    	}
    	///////////////////////////////////// Header ////////////////////////////////
    	public void stopRefresh(){
    		if(mHeaderView.getCurrentState() == HeaderView.STATE_REFRESHING){
    			mHeaderView.setHeaderState(HeaderView.STATE_NORMAL);
    			resetHeader();
    			if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
    				mFooterView.show();
    			}
    		}
    	}
    	
    	private void initHeaderView(Context context){
    		mHeaderView = new HeaderView(context);
    		mHeaderContent = (RelativeLayout) mHeaderView.findViewById(R.id.header_content);
    		addHeaderView(mHeaderView);
    		mHeaderView.getViewTreeObserver()
    				   .addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
    			@Override
    			public void onGlobalLayout() {
    				iHeaderHeight = mHeaderContent.getHeight();
    				Log.d(TAG, "iHeaderHeight = " + iHeaderHeight);
    				getViewTreeObserver().removeGlobalOnLayoutListener(this);
    			}
    		});
    	}
    	
    	private void updateHeaderState(float delta){
    		mHeaderView.setHeaderHeight((int)(delta + mHeaderView.getHeaderHeight()));
    		if(mHeaderView.getCurrentState() != HeaderView.STATE_REFRESHING){
    			if(mHeaderView.getHeaderHeight() > iHeaderHeight){
    				mHeaderView.setHeaderState(HeaderView.STATE_WILL_RELEASE);
    			}else{
    				mHeaderView.setHeaderState(HeaderView.STATE_NORMAL);
    			}
    		}
    		setSelection(0);
    	}
    	
    	private void resetHeader(){
    		int height = mHeaderView.getHeaderHeight();
    		if(height == 0){
    			return;
    		}
    
    		int finalHeight = 0;
    		if(height > iHeaderHeight){
    			/*
    			 * 如果超过HeaderView高度,则回滚到HeaderView高度即可
    			 */
    			finalHeight = iHeaderHeight;
    		}else if(mHeaderView.getCurrentState() == HeaderView.STATE_REFRESHING){
    			/*
    			 * 如果HeaderView未完全显示
    			 * 1. 处于正在刷新中,则不管;
    			 * 2. 回滚HeaderView当前可视高度
    			 */
    			return;
    		}
    		
    		iScrollWhich = SCROLL_HEADER;
    		mScroller.startScroll(0, height, 0, finalHeight - height, 250);
    		invalidate();
    	}
    	/////////////////////////////////////////////////////////////////////////////
    	///////////////////////////////////// Footer ////////////////////////////////
    	public void setFooterMode(int options){
    		mFooterView.setFooterViewOptions(options);
    	}
    	
    	public void stopLoad(){
    		if(mFooterView.getCurrentState() == FooterView.STATE_LOADING){
    			mFooterView.setFooterState(FooterView.STATE_NORMAL);
    			resetFooter();
    		}
    	}
    	
    	private void initFooterView(Context context){
    		mFooterView = new FooterView(context);
    		mFooterContent = (RelativeLayout) mFooterView.findViewById(R.id.footer_content);
    		mFooterContent.setOnClickListener(new OnClickListener(){
    			@Override
    			public void onClick(View v) {
    				if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK
    						&& mFooterView.getCurrentState() == FooterView.STATE_NORMAL){
    					mFooterView.setFooterState(FooterView.STATE_LOADING);
    				}
    			}
    		});
    		mFooterView.getViewTreeObserver()
    				   .addOnGlobalLayoutListener(new OnGlobalLayoutListener(){
    			@Override
    			public void onGlobalLayout() {
    				iFooterHeight = mFooterContent.getHeight();
    				Log.d(TAG, "iFooterHeight = " + iFooterHeight);
    				getViewTreeObserver().removeGlobalOnLayoutListener(this);
    			}
    		});
    	}
    	
    	private void updateFooterState(float delta){
    		if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
    			return;
    		}
    		
    		mFooterView.setFooterHeight((int)(delta + mFooterView.getFooterHeight()));
    		if(mFooterView.getCurrentState() != FooterView.STATE_LOADING){
    			if(mFooterView.getFooterHeight() > iFooterHeight){
    				mFooterView.setFooterState(FooterView.STATE_WILL_RELEASE);
    			}else{
    				mFooterView.setFooterState(FooterView.STATE_NORMAL);
    			}
    		}
    	}
    	
    	private void resetFooter(){
    		int height = mFooterView.getFooterHeight();
    		if(height == 0){
    			return;
    		}
    		
    		if(mFooterView.getFooterViewOptions() == FooterView.FOOTER_OPTIONS_CLICK){
    			return;
    		}
    		
    		int finalHeight = 0;
    		if(height > iFooterHeight){
    			finalHeight = iFooterHeight;
    		}else if(mFooterView.getCurrentState() == FooterView.STATE_LOADING){
    			return;
    		}
    
    		iScrollWhich = SCROLL_FOOTER;
    		mScroller.startScroll(0, height, 0, finalHeight - height, 250);
    		invalidate();
    	}
    	/////////////////////////////////////////////////////////////////////////////
    }
    

    代码结构比较清楚,相关的都集中在一起,大致流程是:

    1. down时,记住坐标;

    2. move时,判断当前可见是否是第一个或是最后一个,如果是,则将移动的距离去设置HeaderView或FooterView的高度,达到一点一点的显示出来;

    3. up时,判断HeaderView或FooterView是否滚动的距离超过它们的高度,如果是,则表示是刷新或加载,且回弹到移动的距离-高度;

    4. 代码还提供了冲突设置,即如果当前正在刷新中,则不允许滚动到底部上拉更多,或者显示“点击加载更多”,同样,如果是底部正在加载,则不允许滚动到顶多,下拉刷新;

    2.4 使用举例

    首页布局

    <RelativeLayout 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"
        tools:context=".MainActivity" >
    
        <com.chris.list.refresh.ListViewExt 
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="#000000"
            android:dividerHeight="0.5dip"/>
    
    </RelativeLayout>

    首页Activity代码实现,和一般的使用ListView方法一样

    package com.chris.list.refresh;
    
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.ArrayAdapter;
    import android.app.Activity;
    
    public class MainActivity extends Activity {
    
    	private final static String TAG = "ChrisLV";
    	private ListViewExt mListView = null;
    	private String[] mList = {
    		"abcd1", "abcd2", "abcd3", "abcd4", "abcd5", "abcd6",
    		"abcd7", "abcd8", "abcd9", "abcd10", "abcd11", "abcd12",
    		"abcd13", "abcd14", "abcd15", "abcd16", "abcd17", "abcd18",
    		"abcd19", "abcd20", "abcd21", "abcd22", "abcd23", "abcd24"
    	};
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		
    		mListView = (ListViewExt) findViewById(R.id.listview);
    		mListView.setAdapter(new ArrayAdapter<String>(this, 
    				android.R.layout.simple_list_item_1, 
    				mList));
    		
    		mListView.setOnItemClickListener(new OnItemClickListener(){
    			@Override
    			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
    					long arg3) {
    				Log.d(TAG, "arg2 = " + arg2);
    				if(arg2 > 0){
    					mListView.stopRefresh();
    					mListView.stopLoad();
    				}
    				
    				mListView.setFooterMode(arg2 % 2);
    			}
    		});
    	}
    
    }
    

    在onItemClick中,只是做了简单的将HeaderView或FooterView停止,并设置FooterView的加载模式:是上拉更多,还是点击加载更多。

    三、小结

    本篇文章,大致就这么多,虽然,为了UI体验友好,花了很多精力,但是一通百通,其它的也不外乎是这些,所以大家学习后,希望能举一反三,同时,咱们也交流交流。

    源码下载地址: http://download.csdn.net/detail/qingye_love/5597623

  • 相关阅读:
    EasyUI datagrid动态加载json数据
    利用EasyUI combobox实现模糊搜索
    SQL动态拼接字符串生成分页存储过程
    JavaScript函数表达式
    JavaScript继承
    JavaScript创建对象的常用模式
    JavaScript引用类型
    JavaScript执行环境
    JavaScript变量及数据类型
    jackson序列化和反序列化Json
  • 原文地址:https://www.cnblogs.com/dyllove98/p/3141295.html
Copyright © 2011-2022 走看看