Android---可以实现下拉刷新的ListView

时间:2022-12-24 19:42:31

1.效果

Android---可以实现下拉刷新的ListView
这个效果在很多App里都可以用到,基本上就已经泛滥了.这里就记录一下如何实现这一种效果.(截图没注意大小,丢帧也严重,所以看上去有点卡顿)

2.实现步骤

2.1布局文件
Android---可以实现下拉刷新的ListView
首先要明确的是,这一块也是一个布局文件,我们首先写出这个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();默认等于-1

  • ACTION_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回调

  1. 定义下拉刷新的接口
  2. 暴露接口设置监听
  3. 定义成员变量接收监听对象
  4. 在合适的时机进行回调
  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 值的变化.
Android---可以实现下拉刷新的ListView
我这个字写得真是丑…..