很简单的一个自定义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;
}