zoukankan      html  css  js  c++  java
  • Android—自定义控件实现ListView下拉刷新

    这篇博客为大家介绍一个android常见的功能——ListView下拉刷新(参考自他人博客,网址忘记了,阅读他的代码自己理解注释的,希望能帮助到大家):

    首先下拉未松手时候手机显示这样的界面:

    下面的代码是自定的扎样的控件:

    package com.dhsr.smartID.view;
    
    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;
    
    import com.example.sirelinkscanapp.R;
    
    /**
     * 自定义控件,完成下拉时候ListView显示“图标及提示正在刷新……”的布局
     * 
     * @author cyf
     * 
     */
    public class XListViewHeader extends LinearLayout {
    	private LinearLayout mContainer;
    	// 图片
    	private ImageView mArrowImageView;
    	// 圆形进度条
    	private ProgressBar mProgressBar;
    	private TextView mHintTextView;
    	// 状态
    	private int mState = STATE_NORMAL;
    	// 动画
    	private Animation mRotateUpAnim;
    	private Animation mRotateDownAnim;
    	private final int ROTATE_ANIM_DURATION = 180;
    	// 正常
    	public final static int STATE_NORMAL = 0;
    	// 准备刷新
    	public final static int STATE_READY = 1;
    	// 刷新中
    	public final static int STATE_REFRESHING = 2;
    
    	public XListViewHeader(Context context) {
    		super(context);
    		initView(context);
    	}
    
    	/**
    	 * @param context
    	 * @param attrs
    	 */
    	public XListViewHeader(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		initView(context);
    	}
    
    	/**
    	 * “ 松开即可刷新……正在刷新……”的布局
    	 * 
    	 * @param context
    	 */
    	private void initView(Context context) {
    		// 初始情况,设置下拉刷新view高度为0
    		LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
    				LayoutParams.MATCH_PARENT, 0);
    		mContainer = (LinearLayout) LayoutInflater.from(context).inflate(
    				R.layout.xlistview_header, null);
    		// 加载视图
    		addView(mContainer, lp);
    		// 居中方式
    		setGravity(Gravity.BOTTOM);
    		// 初始化控件
    		mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);
    		mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);
    		mProgressBar = (ProgressBar) findViewById(R.id.xlistview_header_progressbar);
    		// 设置动画
    		mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,
    				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
    				0.5f);
    		mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
    		mRotateUpAnim.setFillAfter(true);
    		mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,
    				Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
    				0.5f);
    		mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
    		mRotateDownAnim.setFillAfter(true);
    	}
    
    	/**
    	 * 根据下拉状态执行相应的功能,开始以及停止相应的动画
    	 * 
    	 * @param state
    	 */
    	public void setState(int state) {
    		if (state == mState)
    			return;
    		if (state == STATE_REFRESHING) { // 显示进度
    			mArrowImageView.clearAnimation();
    			mArrowImageView.setVisibility(View.INVISIBLE);
    			mProgressBar.setVisibility(View.VISIBLE);
    		} else { // 显示箭头图片
    			mArrowImageView.setVisibility(View.VISIBLE);
    			mProgressBar.setVisibility(View.INVISIBLE);
    		}
    		switch (state) {
    		
    		case STATE_NORMAL:
    			if (mState == STATE_READY) {
    				// 开始动画
    				mArrowImageView.startAnimation(mRotateDownAnim);
    			}
    			//刷新中
    			if (mState == STATE_REFRESHING) {
    				// 清除动画
    				mArrowImageView.clearAnimation();
    			}
    			mHintTextView.setText(R.string.xlistview_header_hint_normal);
    			break;
    		case STATE_READY:
    			if (mState != STATE_READY) {
    				mArrowImageView.clearAnimation();
    				mArrowImageView.startAnimation(mRotateUpAnim);
    				mHintTextView.setText(R.string.xlistview_header_hint_ready);
    			}
    			break;
    		case STATE_REFRESHING:
    			mHintTextView.setText(R.string.xlistview_header_hint_loading);
    			break;
    		default:
    		}
    
    		mState = state;
    	}
        
    	public void setVisiableHeight(int height) {
    		if (height < 0)
    			height = 0;
    		LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mContainer
    				.getLayoutParams();
    		lp.height = height;
    		mContainer.setLayoutParams(lp);
    	}
    
    	public int getVisiableHeight() {
    		return mContainer.getHeight();
    	}
    
    }
    

     接下来需要自定义自己的ListView继承与android本身的ListView,方便自己添加新的方法。

    package com.dhsr.smartID.view;
    
    import android.content.Context;
    import android.util.AttributeSet;
    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.AbsListView.OnScrollListener;
    import android.widget.ListAdapter;
    import android.widget.ListView;
    import android.widget.RelativeLayout;
    import android.widget.Scroller;
    import android.widget.TextView;
    
    import com.example.sirelinkscanapp.R;
    
    /**
     * 自定义ListView
     * 
     * @author cyf
     * 
     */
    public class XListView extends ListView implements OnScrollListener {
    
    	private final String TAG = "XListView";
    	private float mLastY = -1;
    	private Scroller mScroller;
    	// 滑动
    	private OnScrollListener mScrollListener;
    
    	// 为外界创建监听
    	private IXListViewListener mListViewListener;
    
    	// 刚才定义的自定义控件
    	private XListViewHeader mHeaderView;
    
    	private RelativeLayout mHeaderViewContent;
    	private TextView mHeaderTimeView;
    	private int mHeaderViewHeight; // header view's height
    	private boolean mEnablePullRefresh = true;
    	private boolean mPullRefreshing = false; // is refreashing.
    
    	private boolean mEnablePullLoad;
    	private boolean mPullLoading;
    	private boolean mIsFooterReady = false;
    
    	// total list items, used to detect is at the bottom of listview.
    	private int mTotalItemCount;
    
    	// for mScroller, scroll back from header or footer.
    	private int mScrollBack;
    	private final static int SCROLLBACK_HEADER = 0;
    	private final static int SCROLLBACK_FOOTER = 1;
    
    	private final static int SCROLL_DURATION = 400; // scroll back duration
    	private final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px
    														// at bottom, trigger
    														// load more.
    	private final static float OFFSET_RADIO = 1.8f; // support iOS like pull
    													// feature.
    
    	/**
    	 * @param context
    	 */
    	public XListView(Context context) {
    		super(context);
    		initWithContext(context);
    	}
    
    	public XListView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		initWithContext(context);
    	}
    
    	public XListView(Context context, AttributeSet attrs, int defStyle) {
    		super(context, attrs, defStyle);
    		initWithContext(context);
    	}
    
    	/**
    	 * 初始化控件
    	 * 
    	 * @param context
    	 */
    	private void initWithContext(Context context) {
    		mScroller = new Scroller(context, new DecelerateInterpolator());
    
    		super.setOnScrollListener(this);
    
    		mHeaderView = new XListViewHeader(context);
    		mHeaderViewContent = (RelativeLayout) mHeaderView
    				.findViewById(R.id.xlistview_header_content);
    		mHeaderTimeView = (TextView) mHeaderView
    				.findViewById(R.id.xlistview_header_time);
    
    		addHeaderView(mHeaderView, null, false);
    
    		mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
    				new OnGlobalLayoutListener() {
    					@Override
    					public void onGlobalLayout() {
    						mHeaderViewHeight = mHeaderViewContent.getHeight();
    						getViewTreeObserver()
    								.removeGlobalOnLayoutListener(this);
    					}
    				});
    	}
    
    	@Override
    	public void setAdapter(ListAdapter adapter) {
    		// make sure XListViewFooter is the last footer view, and only add once.
    		if (mIsFooterReady == false) {
    			if (mEnablePullLoad) {
    				mIsFooterReady = true;
    				// addFooterView(mFooterView);
    
    			}
    		}
    		super.setAdapter(adapter);
    	}
    
    	public void setPullRefreshEnable(boolean enable) {
    		mEnablePullRefresh = enable;
    		if (!mEnablePullRefresh) { // disable, hide the content
    			mHeaderViewContent.setVisibility(View.INVISIBLE);
    		} else {
    			mHeaderViewContent.setVisibility(View.VISIBLE);
    		}
    	}
    
    	/**
    	 * 停止刷新
    	 */
    	public void stopRefresh() {
    		if (mPullRefreshing == true) {
    			mPullRefreshing = false;
    			resetHeaderHeight();
    		}
    	}
    
    	// 可使进入Activity时便执行下拉刷新
    	public void startRefresh() {
    		if (mPullRefreshing == false) {
    			mPullRefreshing = true;
    			mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
    			mHeaderView.setVisiableHeight(90);
    			if (mListViewListener != null) {
    				mListViewListener.onRefresh();
    			}
    		}
    	}
    
    	/**
    	 * stop load more, reset footer view.
    	 */
    	public void stopLoadMore() {
    		if (mPullLoading == true) {
    			mPullLoading = false;
    
    		}
    	}
    
    	/**
    	 * set last refresh time
    	 * 
    	 * @param time
    	 */
    	public void setRefreshTime(String time) {
    		mHeaderTimeView.setText(time);
    	}
    
    	private void invokeOnScrolling() {
    		if (mScrollListener instanceof OnXScrollListener) {
    			OnXScrollListener l = (OnXScrollListener) mScrollListener;
    			l.onXScrolling(this);
    		}
    	}
    
    	private void updateHeaderHeight(float delta) {
    		mHeaderView.setVisiableHeight((int) delta
    				+ mHeaderView.getVisiableHeight());
    		if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头
    			if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
    				mHeaderView.setState(XListViewHeader.STATE_READY);
    			} else {
    				mHeaderView.setState(XListViewHeader.STATE_NORMAL);
    			}
    			if (mPullLoading) { // disable, hide the content
    				mHeaderViewContent.setVisibility(View.INVISIBLE);
    			} else {
    				mHeaderViewContent.setVisibility(View.VISIBLE);
    			}
    		}
    		setSelection(0); // scroll to top each time
    	}
    
    	/**
    	 * reset header view's height.
    	 */
    	private void resetHeaderHeight() {
    		int height = mHeaderView.getVisiableHeight();
    		if (height == 0) // not visible.
    			return;
    		// refreshing and header isn't shown fully. do nothing.
    		if (mPullRefreshing && height <= mHeaderViewHeight) {
    			return;
    		}
    		int finalHeight = 0; // default: scroll back to dismiss header.
    		// is refreshing, just scroll back to show all the header.
    		if (mPullRefreshing && height > mHeaderViewHeight) {
    			finalHeight = mHeaderViewHeight;
    		}
    		mScrollBack = SCROLLBACK_HEADER;
    		mScroller.startScroll(0, height, 0, finalHeight - height,
    				SCROLL_DURATION);
    		// trigger computeScroll
    		invalidate();
    	}
    
    	/**
    	 * 触屏监听
    	 */
    	@Override
    	public boolean onTouchEvent(MotionEvent ev) {
    		if (mLastY == -1) {
    			mLastY = ev.getRawY();
    		}
    
    		switch (ev.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			mLastY = ev.getRawY();
    			break;
    		case MotionEvent.ACTION_MOVE:
    			final float deltaY = ev.getRawY() - mLastY;
    			// DLog.i(TAG, "deltaY is " + deltaY);
    			mLastY = ev.getRawY();
    
    			if (getFirstVisiblePosition() == 0
    					&& (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
    				// the first item is showing, header has shown or pull down.
    				updateHeaderHeight(deltaY / OFFSET_RADIO);
    				invokeOnScrolling();
    			} else if (getLastVisiblePosition() == mTotalItemCount - 1) {
    				// last item, already pulled up or want to pull up.
    
    			}
    			break;
    		default:
    			mLastY = -1; // reset
    			if (getFirstVisiblePosition() == 0) {
    				// invoke refresh
    				if (!mPullRefreshing && mEnablePullRefresh && !mPullLoading
    						&& mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
    					mPullRefreshing = true;
    					mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
    					// DLog.i(TAG, "invoke refresh");
    					if (mListViewListener != null) {
    						// 此时执行刷刷新的方法
    						mListViewListener.onRefresh();
    					}
    				}
    				resetHeaderHeight();
    			} else if (getLastVisiblePosition() == mTotalItemCount - 1) {
    				// invoke load more.
    				if (!mPullLoading && mEnablePullLoad && !mPullRefreshing) {
    					// DLog.i(TAG, "invoke load more");
    
    				}
    
    			}
    			break;
    		}
    		return super.onTouchEvent(ev);
    	}
    
    	@Override
    	public void computeScroll() {
    		if (mScroller.computeScrollOffset()) {
    			if (mScrollBack == SCROLLBACK_HEADER) {
    				mHeaderView.setVisiableHeight(mScroller.getCurrY());
    			} else {
    
    			}
    			postInvalidate();
    			invokeOnScrolling();
    		}
    		super.computeScroll();
    	}
    
    	@Override
    	public void setOnScrollListener(OnScrollListener l) {
    		mScrollListener = l;
    	}
    
    	/**
    	 * 在滚动时回调,回调2-3次,手指没抛开则回调2次,scrollState = 2的这次不回调 第1次:scrollState =
    	 * SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动 第2次:scrollState =
    	 * SCROLL_STATE_FLING(2)手指做了抛的动作(手指离开屏幕前,用力滑了一下) 第3次:scrollState =
    	 * SCROLL_STATE_IDLE(0) 停止滚动
    	 */
    	@Override
    	public void onScrollStateChanged(AbsListView view, int scrollState) {
    		if (mScrollListener != null) {
    			mScrollListener.onScrollStateChanged(view, scrollState);
    		}
    		// 滑到底部时,自动加载更多。 也可以禁用此逻辑
    		if (getLastVisiblePosition() == mTotalItemCount - 1
    				&& scrollState == SCROLL_STATE_IDLE) {
    			if (!mPullLoading && mEnablePullLoad && !mPullRefreshing) {
    				// DLog.i(TAG, "invoke load more");
    
    			}
    		}
    	}
    
    	/**
    	 * 正在滚动的时候回调,停止滚动时才停止回调,单击时回调一次
    	 */
    	@Override
    	public void onScroll(AbsListView view, int firstVisibleItem,
    			int visibleItemCount, int totalItemCount) {
    		// send to user's listener
    		mTotalItemCount = totalItemCount;
    		if (mScrollListener != null) {
    			mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
    					totalItemCount);
    		}
    	}
    
    	public void setXListViewListener(IXListViewListener l) {
    		mListViewListener = l;
    	}
    
    	/**
    	 * 监听ListView的滑动事件
    	 * 
    	 * @author cyf
    	 * 
    	 */
    	public interface OnXScrollListener extends OnScrollListener {
    		public void onXScrolling(View view);
    	}
    
    	/**
    	 * 自定义接口
    	 */
    	public interface IXListViewListener {
    		// 下拉刷新时候执行的方法
    		public void onRefresh();
    
    		// 上拉加载时候执行的方法
    		public void onLoadMore();
    	}
    }
    

     MainActivity的xml文件中的ListView就不要用android的ListView了,用上面自定义的ListView(加包名)

    package com.test.andy;
    
    import java.util.ArrayList;
    
    import com.test.andy.view.XListView;
    import com.test.andy.view.XListView.IXListViewListener;
    
    import com.test.andy.R;
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.widget.ArrayAdapter;
    /**
     * XListViewActivity
     * 实现IXListViewListener接口是为了实现其中的下拉刷新等方法
     * @author cyf
     *
     */
    public class XListViewActivity extends Activity implements IXListViewListener {
    	//自定义的ListView
    	private XListView mListView;
    	private ArrayAdapter<String> mAdapter;
    	private ArrayList<String> items = new ArrayList<String>();
    	private Handler mHandler;
    	private int start = 0;
    	private int refreshCnt = 0;
    	/** Called when the activity is first created. */
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    		//初始化的时候加载20条数据
    		geneItems();
    		mListView = (XListView) findViewById(R.id.xListView);
    		mListView.setPullLoadEnable(true);
    		mAdapter = new ArrayAdapter<String>(this, R.layout.list_item, items);
    		mListView.setAdapter(mAdapter);
    //		mListView.setPullRefreshEnable(false);
    		mListView.setXListViewListener(this);
    		mHandler = new Handler();
    		mListView.startRefresh();
    	}
    
    	private void geneItems() {
    		for (int i = 0; i != 20; ++i) {
    			items.add("refresh cnt " + (++start));
    		}
    	}
    
    	private void onLoad() {
    		mListView.stopRefresh();
    		mListView.stopLoadMore();
    		mListView.setRefreshTime("刚刚");
    	}
    	
    	@Override
    	public void onRefresh() {
    		mHandler.postDelayed(new Runnable() {
    			@Override
    			public void run() {
    				start = ++refreshCnt;
    				items.clear();
    				geneItems();
    				// mAdapter.notifyDataSetChanged();
    				mAdapter = new ArrayAdapter<String>(XListViewActivity.this, R.layout.list_item, items);
    				mListView.setAdapter(mAdapter);
    				onLoad();
    			}
    		}, 2000);
    	}
    
    	@Override
    	public void onLoadMore() {
    		mHandler.postDelayed(new Runnable() {
    			@Override
    			public void run() {
    				geneItems();
    				mAdapter.notifyDataSetChanged();
    				onLoad();
    			}
    		}, 2000);
    	}
    
    }
    

     重要的代码都总结在这了,希望给大家带来帮助,谢谢。

  • 相关阅读:
    我是如何去了解jquery的(六),案例之幻灯片轮换
    近两年学习博客案例目录整理
    我是如何去了解jquery的(三),事件之点击事件,光棍节特献
    滚动鼠标放大缩小图片效果,兼容火狐
    自己动手制作jquery插件之自动添加删除行(下)
    JY案例一:瀑布流布局页面
    我是如何去了解jquery的(一),从查询开始
    自己动手制作jquery插件之自动添加删除行(上)
    我是如何去了解jquery的(二),复杂选择符
    我是如何去了解jquery的(七),案例之不间断滚动
  • 原文地址:https://www.cnblogs.com/yunfang/p/5389170.html
Copyright © 2011-2022 走看看