自定义ListView实现下拉刷新

很简单的一个自定义ListView,参考于开源中国APP,不过实用性很差,没有使用阻尼系数、下拉得太快,现在在找着一个更好用的下拉刷新和下拉加载更多的例子,就暂时将这个例子保存到博客。


自定义ListView代码:

public class PullToRefreshListView extends ListView implements OnScrollListener{

	/**
	 * 下拉可以刷新
	 */
	private static final int PULL_TO_REFRESH = 0;
	/**
	 * 松开后刷新
	 */
	private static final int RELEASE_TO_REFRESH = 1;
	/**
	 * 正在刷新
	 */
	private static final int REFRESHING = 2;
	/**
	 * 刷新完成
	 */
	private static final int DONE = 3;
	
	private LayoutInflater mInflater;
	private LinearLayout mHeadView;
	private TextView mTipsTextView;
	private TextView mLastUpdateTextView;
	private ImageView mArrowImageView;
	private ProgressBar mProgressBar;
	
	//箭头动画效果
	private RotateAnimation animation;
	private RotateAnimation reverseAnimation;
	
	/**
	 * 下拉刷新头部实际高度
	 */
	private int headContentHeight;
	/**
	 * 下拉刷新头部原PaddingTop
	 */
	private int headContentOriginalPaddingTop;
	/**
	 * 用户按下时的纵坐标值
	 */
	private int startY;
	/**
	 * 显示在屏幕中的第一项
	 */
	private int firstItemIndex;
	/**
	 * 当前的滑动状态
	 */
	private int currentScrollState;
	/**
	 * 当前刷新状态
	 */
	private int state;
	/**
	 * 用来保证startY的值在一个完整的touch事件中只被记录一次
	 */
	private boolean isRecored;
	
	
	public OnRefreshListener refreshListener;
	
	private boolean isBack;
	
	public PullToRefreshListView(Context context) {
		super(context);
		init(context);
	}
	
	
	public PullToRefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init(context);
	}

	
	private void init(Context context) {
		//设置箭头动画效果
		animation = new RotateAnimation(0, -180, 
				RotateAnimation.RELATIVE_TO_SELF, 0.5f, 
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		animation.setDuration(150);
		animation.setFillAfter(true);
		//设置箭头反向动画效果
		reverseAnimation = new RotateAnimation(-180, 0, 
				RotateAnimation.RELATIVE_TO_SELF, 0.5f, 
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		reverseAnimation.setDuration(150);
		reverseAnimation.setFillAfter(true);
		
		mInflater = LayoutInflater.from(context);
		mHeadView = (LinearLayout)mInflater.inflate(R.layout.pull_to_refresh_head, null);
		mArrowImageView = (ImageView)mHeadView.findViewById(R.id.pull_to_refresh_arrow);
		mArrowImageView.setMinimumHeight(50);
		mArrowImageView.setMinimumWidth(50);
		mProgressBar = (ProgressBar)mHeadView.findViewById(R.id.pull_to_refresh_progressBar);
		mTipsTextView = (TextView)mHeadView.findViewById(R.id.pull_to_refresh_tips);
		mLastUpdateTextView = (TextView)mHeadView.findViewById(R.id.pull_to_refresh_lastupdate);
		
		measureView(mHeadView);
		headContentHeight = mHeadView.getMeasuredHeight();
		headContentOriginalPaddingTop = mHeadView.getPaddingTop();
		mHeadView.setPadding(mHeadView.getPaddingLeft(), -1 * headContentHeight, 
				mHeadView.getPaddingRight(), mHeadView.getPaddingBottom());
		
		addHeaderView(mHeadView);
		setOnScrollListener(this);
	}
	
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			if(firstItemIndex == 0 && !isRecored) {
				startY = (int)event.getY();
				isRecored = true;
			}
			break;
		
		//当失去焦点&取消动作
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			if(state != REFRESHING) {
				if(state == DONE) {}
				//用户下拉高度不满足刷新
				else if(state == PULL_TO_REFRESH) {
					state = DONE;
					changeHeaderViewByState();
				}
				//用户下拉高度满足刷新
				else if(state == RELEASE_TO_REFRESH) {
					state = REFRESHING;
					changeHeaderViewByState();
					onRefresh();
				}
			}
			isRecored = false;
			isBack = false;
			
			break;
		
		case MotionEvent.ACTION_MOVE:
			int tempY = (int)event.getY();
			if(!isRecored && firstItemIndex == 0) {
				startY = tempY;
				isRecored = true;
			}
			if(state != REFRESHING && isRecored) {
				//当前状态为"松开后刷新"
				if(state == RELEASE_TO_REFRESH) {
					//用户向上滑动,下拉高度小于"应该显示松开后刷新"的高度
					if((tempY - startY < headContentHeight + 20) && (tempY - startY) > 0) {
						state = PULL_TO_REFRESH;
						changeHeaderViewByState();
					}
					//一下次向上滑动置顶
					else if(tempY - startY <= 0) {
						state = DONE;
						changeHeaderViewByState();
					//往下拉,或者还没有上推到屏幕顶部掩盖head
					} else {
						//不用进行任何操作,只更新paddingTop的值
					}
				}
				//当前状态为"下拉可以刷新"
				else if(state == PULL_TO_REFRESH) {
					//用户下拉到可以进入"松开后刷新"状态
					if((tempY - startY >= headContentHeight + 20) && currentScrollState == SCROLL_STATE_TOUCH_SCROLL) {
						state = RELEASE_TO_REFRESH;
						isBack = true;
						changeHeaderViewByState();
					}
					//一下子向上滑动置顶
					else if(tempY - startY <= 0) {
						state = DONE;
						changeHeaderViewByState();
					}
				}
				//当前状态为"刷新完成"
				else if(state == DONE) {
					if(tempY - startY > 0) {
						state = PULL_TO_REFRESH;
						changeHeaderViewByState();
					}
				}
				
				//当前状态"下拉可以刷新":头部PaddingTop为原PaddingTop加上滑动距离
				if(state == PULL_TO_REFRESH) {
					int topPadding = (int)(-1 * headContentHeight + (tempY - startY));
					mHeadView.setPadding(mHeadView.getPaddingLeft(), topPadding, 
							mHeadView.getPaddingRight(), mHeadView.getPaddingBottom());
					mHeadView.invalidate();
				}
				//当前状态"松开后刷新":头部PaddingTop为原PaddingTop加上滑动距离
				if(state == RELEASE_TO_REFRESH) {
					int topPadding = (int)(tempY - startY - headContentHeight);
					mHeadView.setPadding(mHeadView.getPaddingLeft(), topPadding, 
							mHeadView.getPaddingRight(), mHeadView.getPaddingBottom());
					mHeadView.invalidate();
				}
			}
			break;
		}
		return super.onTouchEvent(event);
	}
	
	
	/**
	 * 根据当前状态更新界面
	 */
	private void changeHeaderViewByState() {
		switch (state) {
		case PULL_TO_REFRESH:
			mProgressBar.setVisibility(View.GONE);
			mTipsTextView.setVisibility(View.VISIBLE);
			mTipsTextView.setText(R.string.pull_to_refresh_pull);
			mLastUpdateTextView.setVisibility(View.VISIBLE);
			mArrowImageView.setVisibility(View.VISIBLE);
			mArrowImageView.clearAnimation();
			if(isBack) {
				isBack = false;
				mArrowImageView.clearAnimation();
				mArrowImageView.startAnimation(reverseAnimation);
			}
			
			break;

		case RELEASE_TO_REFRESH:
			mProgressBar.setVisibility(View.GONE);
			mTipsTextView.setVisibility(View.VISIBLE);
			mTipsTextView.setText(R.string.pull_to_refresh_release);
			mLastUpdateTextView.setVisibility(View.VISIBLE);
			mArrowImageView.setVisibility(View.VISIBLE);
			mArrowImageView.clearAnimation();
			mArrowImageView.startAnimation(animation);
			
			break;
			
		case REFRESHING:
			mHeadView.setPadding(mHeadView.getPaddingLeft(), headContentOriginalPaddingTop, 
					mHeadView.getPaddingRight(), mHeadView.getPaddingBottom());
			mHeadView.invalidate();
			
			mProgressBar.setVisibility(View.VISIBLE);
			mTipsTextView.setText(R.string.pull_to_refresh_refreshing);
			mLastUpdateTextView.setVisibility(View.GONE);
			mArrowImageView.clearAnimation();
			mArrowImageView.setVisibility(View.GONE);
			
			break;
			
		case DONE:
			mHeadView.setPadding(mHeadView.getPaddingLeft(), -1 * headContentHeight, 
					mHeadView.getPaddingRight(), mHeadView.getPaddingBottom());
			mHeadView.invalidate();
			
			mProgressBar.setVisibility(View.GONE);
			mArrowImageView.clearAnimation();
			mTipsTextView.setText(R.string.pull_to_refresh_pull);
			mLastUpdateTextView.setVisibility(View.VISIBLE);
			
			break;
		}
	}
	
	
	/**
	 * 量取头部实际宽高
	 * @param view
	 */
	private void measureView(View view) {
		ViewGroup.LayoutParams params = view.getLayoutParams();
		if(params == null) {
			params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
		}
		int viewWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, params.width);
		int lpHeight = params.height;
		int viewHeightSpec;
		if(lpHeight > 0) {
			viewHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
		} else {
			viewHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}
		view.measure(viewWidthSpec, viewHeightSpec);
	}


	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		currentScrollState = scrollState;
	}


	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		firstItemIndex = firstVisibleItem;
	}

	
	/**
	 * 点击刷新
	 */
	public void clickRefresh() {
		setSelection(0);
		state = REFRESHING;
		changeHeaderViewByState();
		onRefresh();
	}
	
	
	/**
	 * 设置刷新回调函数
	 * @param refreshListener
	 */
	public void setOnRefreshListener(OnRefreshListener refreshListener) {
		this.refreshListener = refreshListener;
	}
	
	
	/**
	 * 刷新回调接口
	 * @author AA
	 *
	 */
	public interface OnRefreshListener {
		public void onRefresh();
	}
	
	
	/**
	 * 刷新
	 */
	private void onRefresh() {
		if(refreshListener != null) {
			refreshListener.onRefresh();
		}
	}
	
	
	/**
	 * 刷新完成
	 */
	public void onRefreshComplete() {
		state = DONE;
		changeHeaderViewByState();
	}
	
	
	/**
	 * 刷新完成后设置最后刷新时间
	 * @param update
	 */
	public void onRefreshComplete(String update) {
		mLastUpdateTextView.setText(update);
		onRefreshComplete();
	}
}

头部XML代码:

<?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="match_parent"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_gravity="center" >

        <ImageView
            android:id="@+id/pull_to_refresh_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@+id/pull_to_refresh_layout"
            android:layout_toStartOf="@+id/pull_to_refresh_layout"
            android:contentDescription="@string/iv_cont_des_string"
            android:src="@drawable/pull_to_refresh_arrow" />

        <ProgressBar
            android:id="@+id/pull_to_refresh_progressBar"
            android:layout_width="28dp"
            android:layout_height="28dp"
            android:layout_centerVertical="true"
            android:layout_toLeftOf="@+id/pull_to_refresh_layout"
            android:layout_toStartOf="@+id/pull_to_refresh_layout"
            android:visibility="gone" />

        <LinearLayout
            android:id="@+id/pull_to_refresh_layout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:layout_marginLeft="@dimen/space_10"
            android:layout_marginStart="@dimen/space_10"
            android:gravity="center_horizontal"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/pull_to_refresh_tips"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/pull_to_refresh_pull"
                android:textColor="@color/black" />

            <TextView
                android:id="@+id/pull_to_refresh_lastupdate"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="最后刷新:2014-10-21 15:17"
                android:textColor="@color/black"
                android:textSize="@dimen/text_size_10" />
        </LinearLayout>
    </RelativeLayout>

</LinearLayout>

Acitivity代码:

/**
	 * 初始化列表
	 */
	private void initListView() {
		mFAQListView = (PullToRefreshListView)mFaqView.findViewById(R.id.lv_faq);
		TextView emptyView = (TextView)mFaqView.findViewById(R.id.tv_faq_listview_empty);
		mFaqList = getData();
		mAdapter = new ListViewFaqAdapter(getActivity(), mFaqList);
		mFAQListView.setAdapter(mAdapter);
		mFAQListView.setEmptyView(emptyView);
		mFAQListView.setOnItemClickListener(this);
		mFAQListView.setOnRefreshListener(new OnRefreshListener() {
			
			@Override
			public void onRefresh() {
				new AsyncTask<Integer, Integer, Integer>() {

					@Override
					protected Integer doInBackground(Integer... params) {
						try {
							Thread.sleep(2000);
						} catch (Exception e) {
							e.printStackTrace();
						}
						return null;
					}
					
					
					protected void onPostExecute(Integer result) {
						mFAQListView.onRefreshComplete();
					};
					
					
				}.execute(0);
				Toast.makeText(getActivity(), "正在刷新...", Toast.LENGTH_LONG).show();
				
			}
		});
	}
	
	
	/**
	 * 获得列表要显示的数据
	 * @return
	 */
	private List<HashMap<String, Object>> getData() {
		List<HashMap<String, Object>> list = new ArrayList<HashMap<String,Object>>();
		for(int i=0; i<titles.length; i++) {
			HashMap<String, Object> map = new HashMap<String, Object>();
			map.put(Consts.LISTVIEW_FAQ_TITLE, titles[i]);
			map.put(Consts.LISTVIEW_FAQ_AUTHOR, "大嘴" + i + "号");
			map.put(Consts.LISTVIEW_FAQ_DATE, (i+1)*2 + "小时前");
			map.put(Consts.LISTVIEW_FAQ_COUNT, i+6 + "回|" + (i+1)*6 + "阅");
			list.add(map);
		}
		return list;
	}




你可能感兴趣的:(ListView,下拉刷新)