实现思路就是自己写一个刷新效果的布局,然后把这个布局定义为ListView的头,监听手势的滑动效果,分别更新不同的UI。
效果图如下:
直接上代码,代码是完整的,除了那个箭头的图片需要你自己提供,其它的都可以用我的代码,然后你看懂之后,自己在改一下就好了,我写了很多注释。
view_header.xml(自定义刷新的显示内容)
此处一定要在顶层布局加上
android:gravity="bottom"
这样滑动时的效果才会和我的效果图一致。
<?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:gravity="bottom"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="150dp"
android:gravity="center">
<FrameLayout
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_margin="5dp">
<ImageView
android:id="@+id/iv_head_view_arrow"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:src="@drawable/down" />
<ProgressBar
android:id="@+id/pb_head_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tv_view_header_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="下拉刷新"
android:textColor="#F00"
android:textSize="25sp" />
<TextView
android:id="@+id/tv_view_header_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:singleLine="true"
android:text="最后刷新时间: 2015-10-11 09:20:35"
android:textColor="#666"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
MyHeaderView.class
public class MyHeaderView extends LinearLayout {
private ImageView header_arrow;
private ProgressBar header_progressBar;
private TextView header_hint_txt;
private TextView header_time;
private RotateAnimation animation_up;
private RotateAnimation animation_down;
/**
* 初始状态---正常状态
*/
public static final int STATE_NORMAL = 1;
/**
* 准备刷新状态---松开刷新状态
*/
public static final int STATE_READY = 2;
/**
* 正在刷新
*/
public static final int STATE_REFRESHING = 3;
/**
* 状态值
*/
private int mState = STATE_NORMAL;
private View headerView;
public MyHeaderView(Context context) {
super(context);
init();
}
public MyHeaderView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MyHeaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
// 初始化控件
private void init() {
headerView = View.inflate(getContext(), R.layout.view_header, null);
// 设置初始的布局参数
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, 0);
headerView.setLayoutParams(params);
this.addView(headerView);
header_arrow = headerView.findViewById(R.id.iv_head_view_arrow);
header_progressBar = headerView.findViewById(R.id.pb_head_view);
header_hint_txt = headerView.findViewById(R.id.tv_view_header_title);
header_time = headerView.findViewById(R.id.tv_view_header_time);
// 初始化向上旋转动画
animation_up = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation_up.setDuration(100);
animation_up.setFillAfter(true);
// 初始化向下旋转动画
animation_down = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
animation_down.setDuration(100);
animation_down.setFillAfter(true);
}
public void setState(int state) {
// mState表示上一次的状态值
if (state == mState) {
return;
}
if (state == STATE_REFRESHING) {
header_progressBar.setVisibility(View.VISIBLE);
header_arrow.setVisibility(View.INVISIBLE);
header_arrow.clearAnimation();
} else {
header_progressBar.setVisibility(View.INVISIBLE);
header_arrow.setVisibility(View.VISIBLE);
}
switch (state) {
case STATE_NORMAL:
if (mState == STATE_REFRESHING) {
// 如果之前是正在刷新
// header_arrow.startAnimation(animation_down);
} else if (mState == STATE_READY) {
// 如果之前是准备刷新
header_arrow.startAnimation(animation_down);
}
header_hint_txt.setText("下拉刷新...");
break;
case STATE_REFRESHING:
header_hint_txt.setText("正在刷新...");
break;
case STATE_READY:
header_arrow.startAnimation(animation_up);
header_hint_txt.setText("松开刷新...");
break;
}
// 状态置位
mState = state;
}
/**
* 设置当前headerView的可见高度
* @param height
*/
public void setVisibleHeight(int height) {
// 生成布局参数
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, height);
headerView.setLayoutParams(layoutParams);
}
/**
* 获取当前headerView的可见高度
* @return
*/
public int getVisibleHeight() {
return headerView.getLayoutParams().height;
}
}
MyListView.class
public class MyListView extends ListView implements AbsListView.OnScrollListener {
private MyHeaderView myHeaderView;
private int header_height;
private float lastY = -1;
private int firstVisbleItem;
private float ratio=1.8f;
private OnMyListViewListener listener;
public MyListView(Context context) {
super(context);
initHeadView();
// 对MyListView设置滑动监听事件
this.setOnScrollListener(this);
}
public MyListView(Context context, AttributeSet attrs) {
super(context, attrs);
initHeadView();
// 对MyListView设置滑动监听事件
this.setOnScrollListener(this);
}
public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initHeadView();
// 对MyListView设置滑动监听事件
this.setOnScrollListener(this);
}
private void initHeadView() {
myHeaderView = new MyHeaderView(getContext());
// 添加headerView
this.addHeaderView(myHeaderView);
final View header_content = myHeaderView.findViewById(R.id.ll_content);
// 直接获取控件高度,获取不到
// header_content.getHeight();
/**
* 给布局添加监听,当视图可见状态改变的时候,回调到onGlobalLayout()
*/
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
header_height = header_content.getHeight();
getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: // 按下
if (lastY == -1) {
// 获取当前按下的y坐标
lastY = ev.getRawY();
}
break;
case MotionEvent.ACTION_MOVE: // 滑动
// 手指滑动的距离 = 滑动的y减去按下的y坐标
float gapY = ev.getRawY() - lastY;
if (firstVisbleItem == 0 && (gapY > 0 || myHeaderView.getVisibleHeight() > 0)) {
updateHeaderHeight(gapY/ratio);
}
// 将当前的y坐标赋值给上一次
lastY = ev.getRawY();
break;
case MotionEvent.ACTION_UP: // 放开
case MotionEvent.ACTION_CANCEL:
lastY = -1;
if (firstVisbleItem == 0) {
// 如果当前手指抬起时,headerView的可见高度大于headerView的高度,正在刷新
if (myHeaderView.getVisibleHeight() > header_height) {
myHeaderView.setState(MyHeaderView.STATE_REFRESHING);
if (listener != null) {
listener.setRefreshing();
}
}
// 重置headerView的可见高度
resetHeaderViewHeight();
}
break;
default:
break;
}
return super.onTouchEvent(ev);
}
/**
* 重置可视高度
*/
private void resetHeaderViewHeight() {
if (firstVisbleItem == 0) {
if (myHeaderView.getVisibleHeight() <= 0) {
return;
}
if (myHeaderView.getVisibleHeight() > header_height) {
myHeaderView.setVisibleHeight(header_height);
} else {
myHeaderView.setVisibleHeight(0);
}
}
}
/**
* 更新头的高度
*
* @param gapY
*/
private void updateHeaderHeight(float gapY) {
if (firstVisbleItem == 0) {
myHeaderView.setVisibleHeight((int) (myHeaderView.getVisibleHeight() + gapY));
// Log.d(TAG,(int) (myHeaderView.getVisibleHeight() + gapY)+"" );
// 当头部可视的高度大于headerView的高度时,状态为ready状态
if (myHeaderView.getVisibleHeight() > header_height) {
myHeaderView.setState(MyHeaderView.STATE_READY);
} else {
myHeaderView.setState(MyHeaderView.STATE_NORMAL);
}
}
// 设置当前选中的条目索引
setSelection(0);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.firstVisbleItem = firstVisibleItem;
}
public void setOnMyListViewListener(OnMyListViewListener listener) {
this.listener = listener;
}
public interface OnMyListViewListener {
void setRefreshing();
}
/**
* 当刷新结束之后,重置headerView的高度
*/
public void setRefreshingFinish() {
resetHeaderViewHeight();
}
}
RefreshActivity.class(演示效果的活动)
public class RefreshActivity extends AppCompatActivity implements MyListView.OnMyListViewListener{
private MyListView listView;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_refresh);
listView = findViewById(R.id.lv_refresh_list);
listView.setOnMyListViewListener(this);
listView.setAdapter(new RefreshListAdapter(this));
}
@Override
public void setRefreshing() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
//重置高度
listView.setRefreshingFinish();
}
}, 2000);
}
}
这个活动的xml我就不贴出来了,因为里面就放了一个组件,就是这个自定义的ListView,还有RefreshListAdapter的代码我也不贴出来了,就是继承BaseAdapter,然后加载了一个自己写的布局,这个布局如效果图所示,就一个TextView。