很简单的一个自定义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 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>
/** * 初始化列表 */ 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; }