Android自定义控件--下拉刷新的实现
Posted qq_20889581
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android自定义控件--下拉刷新的实现相关的知识,希望对你有一定的参考价值。
我们在使用ListView的时候,很多情况下需要用到下拉刷新的功能。为了了解下拉刷新的底层实现原理,我采用自定义ListView控件的方式来实现效果。
实现的基本原理是:自定义ListView,给ListView加载头布局,然后动态的控制头布局的现实与隐藏。ListView初始化的时候,头布局是隐藏的,当手指往下拉的时候,根据手指移动的距离与头布局的高度的关系来控制头布局的显示。具体的控制思路详见后边的代码,代码中的注释很详细。先来看一下效果图:
头布局的实现代码如下:
<?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="horizontal" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="5dp"
android:padding="5dp" >
<ImageView
android:id="@+id/iv_arror"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:padding="10dp"
android:src="@drawable/refresh_listview_arrow" />
<ProgressBar
android:id="@+id/pb_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:indeterminateDrawable="@drawable/custom_progress"
android:visibility="invisible" >
</ProgressBar>
</RelativeLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="25dp"
android:layout_marginTop="12dp"
android:orientation="vertical"
android:padding="5dp" >
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:text="下拉刷新"
android:textColor="#FF0000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="3dp"
android:text="2015-03-10 17:07:07"
android:textColor="@android:color/darker_gray"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
接着定义一个RefreshListView,继承自ListView,重写它的三个构造方法,在RefreshlistView初始化的时候,加载一个头布局,这个头布局默认是隐藏的。
<span style="white-space:pre"> </span>// 初始化头布局
private void initHeaderView() {
// 给当前的ListView添加下拉刷新的头布局
mHeaderView = View.inflate(getContext(), R.layout.list_refresh_header,
null);
tvTitle = (TextView) mHeaderView.findViewById(R.id.tv_title);
tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time);
// 下拉箭头
ivArror = (ImageView) mHeaderView.findViewById(R.id.iv_arror);
// 进度条
pb = (ProgressBar) mHeaderView.findViewById(R.id.pb_list);
this.addHeaderView(mHeaderView);
// 初始化这个下拉刷新的ListView的时候,县隐藏掉下拉刷新的头布局,然后通过代码动态的控制头布局的显示与隐藏
// 隐藏头布局
// 先测量一把头布局
mHeaderView.measure(0, 0);
// 获得测量得到的高度
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
// 设置paddingtop为负高度,隐藏头布局
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
// 初始化完成布局,初始化两个动画效果
initAnimation();
// 设置当前时间
tvTime.setText("最后刷新时间:" + getCurrentTime());
}
当手指触摸屏幕时,触发OnTouchEvent事件,根据手指在Y轴方向上的移动偏移量来动态的控制头布局的显示与隐藏。并即时更新下拉框的显示的状态。具体的代码如下:
<span style="white-space:pre"> </span>/**
* 下拉屏幕的时候,将头布局展示出来,重写一下ListView的触摸屏幕的事件
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// 手指按下的时候获得当前屏幕的Y坐标
startY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
// 手指移动的时候,获得当前屏幕的Y坐标,与开始的Y坐标做比较
if (startY == -1) {
// 确保startY有效
startY = (int) ev.getRawY();
}
int endY = (int) ev.getRawY();
// 屏幕Y方向移动的距离
int dy = endY - startY;
// 只有屏幕向下滑,也就是dy>0,并且当前的ListView显示的是第一个条目,那个隐藏的下拉刷新的头布局才慢慢的随手指滑出来
if (dy > 0 && this.getFirstVisiblePosition() == 0) {
// 允许下拉刷新框出来
// 下拉刷新框出来其实就是给头布局设置padding,首先根据手指移动的距离计算一下padding
int padding = dy - mHeaderViewHeight;//
mHeaderView.setPadding(0, padding, 0, 0);
// 更新刷新框的状态
// 一共有三种状态,下拉刷新、正在刷新、松开刷新
// 定义这三种状态
// 如果padding大于0说明下拉刷新的框已经全部拉出来了,将状态改为松开刷新
if (padding > 0 && mCurrentState != STATE_RELEASE_REFRESH) {
// 状态改为松开刷新,将下拉框显示的状态更新一下
mCurrentState = STATE_RELEASE_REFRESH;
// 更新当前下拉框显示的状态
updateState();
}
if (padding < 0 && mCurrentState != STATE_PULL_REFRESH) {
// 状态改为下拉刷新
mCurrentState = STATE_PULL_REFRESH;
updateState();
}
}
break;
case MotionEvent.ACTION_UP:
// 手指抬起来的时候重置startY。将状态改为正在刷新
startY = -1;
if (mCurrentState == STATE_RELEASE_REFRESH) {
// 如果当前为松开刷新,则手指一抬起来就将状态改为正在
mCurrentState = STATE_REFRESHING;
// 头布局的padding设置为0000
mHeaderView.setPadding(0, 0, 0, 0);
updateState();
} else {
// 没有完全拉出来,就将头布局隐藏
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 更新当前下拉框显示的状态
*/
private void updateState() {
// 判断当前状态是什么,根据当前状态更新下拉框显示
switch (mCurrentState) {
case STATE_PULL_REFRESH:
// 下拉刷新
tvTitle.setText("下拉刷新");
ivArror.setVisibility(View.VISIBLE);
pb.setVisibility(View.INVISIBLE);
// 给箭头设置动画
ivArror.startAnimation(animDown);
break;
case STATE_REFRESHING:
// 正在刷新
tvTitle.setText("正在刷新...");
ivArror.clearAnimation();// 必须先清除动画,才能隐藏
ivArror.setVisibility(View.INVISIBLE);
pb.setVisibility(View.VISIBLE);
// 正在刷新的时候调用接口中的方法
if (mListener != null) {
// 正在刷新
mListener.onRefresh();
}
break;
case STATE_RELEASE_REFRESH:
// 松开刷新
tvTitle.setText("松开刷新");
ivArror.setVisibility(View.VISIBLE);
pb.setVisibility(View.INVISIBLE);
ivArror.startAnimation(animUp);
break;
}
}
为了监听下拉刷新的动作,自定义一个监听接口,当正在刷新的时候执行onRefresh方法。对外提供一个设置监听的方法,监听器由子类实现,具体的正在刷新执行的逻辑由子类在onRefresh中实现。
// 下拉刷新逻辑的实现方式:给当前的ListView设置下拉刷新的监听器,监听器的本质就是一个接口
/**
* 下拉刷新的监听器
*
* @author ZHY
*
*/
public interface onRefreshListener {
// 刷新的时候执行的方法,谁实现这个接口。谁执行此方法
public void onRefresh();
}
onRefreshListener mListener;
// 有了监听器之后,提供一个设置监听的方法
public void setOnRefreshListener(onRefreshListener listener) {
// 这个Listneer是从子类中闯过来的,是子类定义的监听
mListener = listener;
}
刷新完成之后,改变下拉刷新的状态,并且隐藏下拉框
<span style="white-space:pre"> </span>// 刷新完成的时候,将下拉框隐藏
/**
* 调用服务器的数据刷新,刷新完成之后调用的方法 加载完成之后的回调
*
* @param isSuccess
*/
public void onRefreshCompleted(boolean isSuccess) {
// 将状态改成下拉刷新,然后隐藏下拉框
mCurrentState = STATE_PULL_REFRESH;
tvTitle.setText("下拉刷新");
ivArror.setVisibility(View.VISIBLE);
pb.setVisibility(View.INVISIBLE);
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);// 隐藏
// 如果刷新成功,则更新最后一次刷新时间,否则不更新时间
if (isSuccess) {
tvTime.setText("最后刷新时间:" + getCurrentTime());
}
}
这一部分工作做完之后,一个可以实现下拉刷新功能的ListView就定义好了。在MainActivity中就可以直接使用了
以上是关于Android自定义控件--下拉刷新的实现的主要内容,如果未能解决你的问题,请参考以下文章
Android自定义组合控件---教你如何自定义下拉刷新和左滑删除