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

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

  • 相关阅读:
    HDU 1495 非常可乐
    ja
    Codeforces Good Bye 2016 E. New Year and Old Subsequence
    The 2019 Asia Nanchang First Round Online Programming Contest
    Educational Codeforces Round 72 (Rated for Div. 2)
    Codeforces Round #583 (Div. 1 + Div. 2, based on Olympiad of Metropolises)
    AtCoder Regular Contest 102
    AtCoder Regular Contest 103
    POJ1741 Tree(点分治)
    洛谷P2634 [国家集训队]聪聪可可(点分治)
  • 原文地址:https://www.cnblogs.com/yunfang/p/5389170.html
Copyright © 2011-2022 走看看