1.效果
这个效果在很多App里都可以用到,基本上就已经泛滥了.这里就记录一下如何实现这一种效果.(截图没注意大小,丢帧也严重,所以看上去有点卡顿)
2.实现步骤
2.1布局文件
首先要明确的是,这一块也是一个布局文件,我们首先写出这个layout,代码比较简单,就不做说明了:
pull_to_refesh.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="wrap_content"
android:orientation="horizontal">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp">
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/common_listview_headview_red_arrow"/>
<ProgressBar
android:id="@+id/pb_loading"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>
<LinearLayout
android:paddingRight="50dp"
android:layout_marginTop="13dp"
android:gravity="center"
android:layout_width="0dp"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="#f00"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2016-07-12 14:00:20"
android:textColor="#a000"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>
2.2创建一个类继承自ListView
public class PullToRefreshListView extends ListView {}
在这个类里面,我们去实现下拉刷新.
2.3 initView()
private void initView() {
mHeaderView = View.inflate(getContext(), R.layout.pull_to_refesh, null);
//作为ListView 的headerView
this.addHeaderView(mHeaderView);
tv_title = (TextView) mHeaderView.findViewById(R.id.tv_title);
tv_time = (TextView) mHeaderView.findViewById(R.id.tv_time);
iv_arrow = (ImageView) mHeaderView.findViewById(R.id.iv_arrow);
pb_loading = (ProgressBar) mHeaderView.findViewById(R.id.pb_loading);
//隐藏掉下拉刷新的头布局
mHeaderView.measure(0, 0);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
//初始化Animation
initAnimation();
//初始化现在时间
setCurrentTime();
}
2.3.1
在这个方法里,我们将pull_to_refesh.xml转换成的view对象mHeaderView,作为headerView设置给当前自定义的ListView
this.addHeaderView(mHeaderView);
2.3.2
再将mHeaderView隐藏起来:
mHeaderView.measure(0, 0);
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
2.3.3
初始化动画的方法,在效果图中,我们可以看到,当下拉刷新时候,向下的箭头会有一个180度的旋转动画:
private void initAnimation() {
mRotateAnimationUp = new RotateAnimation(0, 180,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotateAnimationUp.setDuration(2000);
mRotateAnimationUp.setFillAfter(true);
mRotateAnimationDown = new RotateAnimation(180,0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
mRotateAnimationDown.setDuration(2000);
mRotateAnimationDown.setFillAfter(true);
}
2.3.4
刷新后设置时间的方法:
public void setCurrentTime(){
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = simpleDateFormat.format(new Date());
tv_time.setText(time);
}
2.4重写onTouchEvent()方法
先贴出方法,在分析
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mStartY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
mEndY = (int) ev.getY();
//当用户按住头条新闻的ViewPager下来时,ACTION_DOWN事件会被ViewPager消耗,导致mStartY没有被赋值
//所以在此处需要重新的拿Y值
if (mStartY == -1) {
mStartY = (int) ev.getY();
}
//如果现在的状态是正在刷新,那么直接跳出循环
if (mCurrentState==ConstantValues.STATE_REFRESHING){
break;
}
//在Y方向上的偏移量
int dy = mEndY - mStartY;
//拿到当前显示的第一个item的位置
int firstVisiblePosition = getFirstVisiblePosition();
Log.d("firstVisiblePosition------>",firstVisiblePosition+"");
//下拉&&当前显示的是第一个item (mHeaderView)
if (dy > 0 && firstVisiblePosition == 0) {
int padding = dy - mHeaderViewHeight;
Log.d("------>", "dy--->" + dy + "--->mHeaderViewHeight" + mHeaderViewHeight + "--->padding" + padding);
mHeaderView.setPadding(0, padding, 0, 0);
if (padding > 0 && mCurrentState != ConstantValues.STATE_RELEASE_TO_REFRESH) {
//改为松开刷新
mCurrentState = ConstantValues.STATE_RELEASE_TO_REFRESH;
refreshState();
} else if (padding < 0 && mCurrentState != ConstantValues.STATE_PULL_TO_REFRESH) {
//改为下拉刷新
mCurrentState = ConstantValues.STATE_PULL_TO_REFRESH;
refreshState();
}
return true;
}
break;
case MotionEvent.ACTION_UP:
//为下一次刷新做准备,重新赋值为-1
mStartY=-1;
if (mCurrentState==ConstantValues.STATE_RELEASE_TO_REFRESH){
mCurrentState=ConstantValues.STATE_REFRESHING;
refreshState();
//完整展示头布局
mHeaderView.setPadding(0,0,0,0);
/**
*4.(合适的时机)进行回调
*@author zfy
*@created at 2016/7/12 14:23
*/
if (mListener!=null){
mListener.onRefresh();
}
}else if (mCurrentState==ConstantValues.STATE_PULL_TO_REFRESH){
//隐藏头布局
mHeaderView.setPadding(0,-mHeaderViewHeight,0,0);
}
break;
}
return super.onTouchEvent(ev);
}
-
首先会有mCurrentState这个量,表示的是下拉栏当前的状态
1.STATE_PULL_TO_REFRESH=1;表示目前处于需要被下拉的状态
2.STATE_RELEASE_TO_REFRESH=2;表示目前处于需要松手的状态
3.STATE_REFRESHING=3;表示目前处于正在刷新的状态 ACTION_DOWN时候拿到当前的Y值
mStartY = (int) ev.getY();
默认等于-1ACTION_MOVE时候:我们根据当前下拉的位置padding,重新设置mHeaderView的padding值.并修改当前的下拉状态
ACTION_UP的时候:对mStartY重新赋值,并且进行回调.
- 其中根据状态值重新刷新界面的方法refreshState如下:
private void refreshState() {
switch (mCurrentState) {
case ConstantValues.STATE_PULL_TO_REFRESH:
tv_title.setText("下拉刷新");
iv_arrow.startAnimation(mRotateAnimationDown);
pb_loading.setVisibility(View.INVISIBLE);
iv_arrow.setVisibility(View.VISIBLE);
break;
case ConstantValues.STATE_RELEASE_TO_REFRESH:
tv_title.setText("松开刷新");
iv_arrow.startAnimation(mRotateAnimationUp);
pb_loading.setVisibility(View.INVISIBLE);
iv_arrow.setVisibility(View.VISIBLE);
break;
case ConstantValues.STATE_REFRESHING:
tv_title.setText("正在刷新...");
//清除箭头动画,否则不能隐藏
iv_arrow.clearAnimation();
pb_loading.setVisibility(View.VISIBLE);
iv_arrow.setVisibility(View.INVISIBLE);
break;
}
}
2.5回调
- 定义下拉刷新的接口
- 暴露接口设置监听
- 定义成员变量接收监听对象
- 在合适的时机进行回调
- 在前端逻辑界面设置回调
/**
* 3.定义成员变量接收监听对象
*
* @author zfy
* @created at 2016/7/12 14:09
*/
private OnRefreshListener mListener;
/**
* 2.暴露接口设置监听
*
* @author zfy
* @created at 2016/7/12 14:08
*/
public void setOnRefreshListener(OnRefreshListener listener) {
mListener = listener;
}
/**
* 1.下拉刷新的回调接口
*
* @author zfy
* @created at 2016/7/12 14:06
*/
public interface OnRefreshListener {
public void onRefresh();
}
步骤4在onTouchEvent方法中实现
步骤5在前段从服务器再次请求数据时候调用
/**
*5.前端界面设置回调
*@author zfy
*@created at 2016/7/12 14:14
*/
lv_list.setOnRefreshListener(new PullToRefreshListView.OnRefreshListener() {
@Override
public void onRefresh() {
//从服务器中重新拿数据的方法
getDataFromServer();
}
});
2.6刷新数据之后,隐藏下拉刷新头View的方法
/**
*刷新完成后收起头View的方法
*@author zfy
*@return
*@param success 是否成功刷新
*@created at 2016/7/12 14:56
*/
public void onRefreshComplete(boolean success){
mHeaderView.setPadding(0,-mHeaderViewHeight,0,0);
tv_title.setText("下拉刷新");
mCurrentState=ConstantValues.STATE_PULL_TO_REFRESH;
pb_loading.setVisibility(INVISIBLE);
iv_arrow.setVisibility(VISIBLE);
if (success) {
setCurrentTime();
}
}
这个方法在回调步骤5,重新从服务器拿数据方法getDataFromServer()内部调用
三.补充
- 上述步骤完成之后,下拉刷新的方法基本就已经实现了.
- 下拉刷新的实现过程也差不多就是一个简单的自定义View的过程
- 这个功能也大可不必自己手敲,很多开源类库都已经实现了这个功能.
四.mCurrentState
后来自己看自己敲的代码都晕了…现在梳理一下mCurrentState 值的变化.
我这个字写得真是丑…..