实现的功能:自定义控件实现顶部下拉刷新,底部加载更多的功能。
实现的过程:首先自定义一个控件RefreshListView让其自定义ListView,然后分别创建RefreshListView的头部布局和尾部布局(即下拉刷新和上拉加载时显示的布局),然后分别将两个布局添加到RefreshListView控件中,然后所要做的事就是在代码中设置什么时候隐藏这两个控件什么时候显示控件,最后模拟下拉刷新上拉加载 动态的向RefreshListView添加数据。
首先自定义控件,在自定义控件中实现两个构造方法和初始化UI的方法,
public class RefreshListView extends ListView implements AbsListView.OnScrollListener {
<pre name="code" class="java"> /**
* xml布局中创建调用这个构造方法
*
* @param context
* @param attrs
*/
public RefreshListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
/***
* java代码中创建调用这个构造方法
*
* @param context
*/
public RefreshListView(Context context) {
super(context);
init();
}
<pre name="code" class="java"> private void init() {
//滑动监听事件
setOnScrollListener(this);
initHeaderView();//初始化头布局
initRotateAnimation();//添加动画效果
initFooterView();//初始化尾布局
}
}
然后创建三个XML文件,首先主界面布局:
<span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.example.administrator.demo_34.View.RefreshListView
android:id="@+id/refreshListView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.example.administrator.demo_34.View.RefreshListView>
</RelativeLayout>
</span>
然后分别是头部和尾部的布局
。
<?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="center_horizontal"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp">
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/indicator_arrow"/>
<ProgressBar
android:id="@+id/pb_rotate"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:indeterminateDrawable="@drawable/indeterminate_drawable"
android:indeterminateDuration="1000"
android:visibility="invisible"
/>
</RelativeLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="10dp"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_state"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下拉刷新"
android:textColor="#aa000000"
android:textSize="20sp"/>
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="最后刷新:"
android:textColor="@android:color/darker_gray"
android:textSize="14sp"/>
</LinearLayout>
</LinearLayout>
<?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="center"
android:orientation="horizontal" >
<ProgressBar
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:indeterminate="true"
android:indeterminateDrawable="@drawable/indeterminate_drawable"
android:indeterminateDuration="1000" />
<TextView android:layout_width="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_height="wrap_content"
android:textColor="#aa000000"
android:layout_marginLeft="15dp"
android:textSize="20sp"
android:text="加载更多..."/>
</LinearLayout>
首先进入主界面,模拟出一些数据放在界面中,然后分别添加头部空间和尾部空间并隐藏,然后根据手指滑动进行相应的操作,上拉刷新这里有三个状态,分别是:"下拉刷新","松开刷新","正在刷新",根据滑动的距离分别显示三种状态,当"正在刷新"状态完毕之后添加数据,添加和隐藏头部,尾部控件的代码。
/**
* 初始化下拉加载更多
*/
private void initFooterView() {
mFooterView = View.inflate(getContext(), R.layout.layout_footer, null);
//通知系统去测量该空间,然后就可以获取该空间的宽高
mFooterView.measure(0, 0);
mFooterViewHeight = mFooterView.getHeight();
//设置隐藏
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
//添加至尾部
addFooterView(mFooterView);
}
/**
* 初始化HeaderView
*/
private void initHeaderView() {
mHeaderView = View.inflate(getContext(), R.layout.layout_header, null);
ivArrow = (ImageView) mHeaderView.findViewById(R.id.iv_arrow);
pbRotate = (ProgressBar) mHeaderView.findViewById(R.id.pb_rotate);
tvState = (TextView) mHeaderView.findViewById(R.id.tv_state);
tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time);
//通知系统去测量该空间,然后就可以获取该空间的宽高
mHeaderView.measure(0, 0);
//获取控件的高度
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
//设置隐藏
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
addHeaderView(mHeaderView);
}
实现上述功能之后,主界面就有了头部和尾部的空间,只不过被设置成了不可见,即把控件的位置移出了屏幕,然后监听手指按下的事件,根据手指上滑
还是下滑,如果上滑到一定距离则进行刷新数据,如果下滑若到了底部则进行加载数据。
private int downY; //手机按下Y的坐标
private final int PULL_REFRESH = 0;//下拉刷新的状态
private final int RELEASE_REFRESH = 1;//松开刷新的状态
private final int REFRESHING = 2;//正在刷新的状态
private int currentState = PULL_REFRESH;//当前的状态
<pre name="code" class="java"> private boolean isLoadingMore = false;//当前是否正在处于加载更多
<pre name="code" class="java">/**
* 监听手指按下
*
* @param ev
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
//手指按下
case MotionEvent.ACTION_DOWN:
downY = (int) ev.getY();
break;
//手指移动
case MotionEvent.ACTION_MOVE:
if (currentState == REFRESHING) {
break;
}
int deltaY = (int) (ev.getY() - downY);
int paddingTop = -mHeaderViewHeight + deltaY;
if (paddingTop > -mHeaderViewHeight && getFirstVisiblePosition() == 0) {
mHeaderView.setPadding(0, paddingTop, 0, 0);
if (paddingTop >= 0 && currentState == PULL_REFRESH) {
//从下拉刷新进入松开刷新状态
currentState = RELEASE_REFRESH;
refreshHeaderView();
} else if (paddingTop < 0 && currentState == RELEASE_REFRESH) {
//进入下拉刷新状态
currentState = PULL_REFRESH;
refreshHeaderView();
}
return true;//拦截TouchMove,不让listview处理该次move事件,会造成listview无法滑动
}
break;
//手指离开
case MotionEvent.ACTION_UP:
if (currentState == PULL_REFRESH) {
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
} else if (currentState == RELEASE_REFRESH) {
mHeaderView.setPadding(0, 0, 0, 0);
currentState = REFRESHING;
refreshHeaderView();
if (mListener != null) {
mListener.onPullRefresh();
}
}
break;
}
return super.onTouchEvent(ev);
}
/**
* 根据currentState来更新headerView
*/
private void refreshHeaderView() {
switch (currentState) {
case PULL_REFRESH:
//下拉刷新状态
tvState.setText("下拉刷新");
ivArrow.startAnimation(downAnimation);
break;
case RELEASE_REFRESH:
//松开刷新状态
tvState.setText("松开刷新");
ivArrow.startAnimation(upAnimation);
break;
case REFRESHING:
//正在刷新状态
tvState.setText("正在刷新...");
ivArrow.clearAnimation();//因为向上的旋转动画有可能没有执行完
ivArrow.setVisibility(INVISIBLE);
pbRotate.setVisibility(VISIBLE);
break;
}
<pre name="code" class="java"> /**
* SCROLL_STATE_IDLE:闲置状态,就是手指松开
* SCROLL_STATE_TOUCH_SCROLL:手指触摸滑动,就是按着来滑动
* SCROLL_STATE_FLING:快速滑动后松开
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == SCROLL_STATE_IDLE && getLastVisiblePosition() == (getCount() - 1)
&& !isLoadingMore) {
isLoadingMore = true;
mFooterView.setPadding(0, 0, 0, 0);
setSelection(getCount());//让listview最后一条显示出来
if (mListener != null) {
mListener.onLoadMore();
}
}
}
为头布局增加动画
<span style="font-size:14px;">private RotateAnimation upAnimation, downAnimation;//定义两个动画
</span><pre name="code" class="java"><span style="font-size:14px;"> /**
* 初始化旋转动画
*/
private void initRotateAnimation() {
upAnimation = new RotateAnimation(0, -180,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
upAnimation.setDuration(300);
upAnimation.setFillAfter(true);
downAnimation = new RotateAnimation(-180, -360,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
downAnimation.setDuration(300);
downAnimation.setFillAfter(true);
}</span>
然后在该空间中创建一个借口,让其在主界面中进行刷新和加载时调用,在主界面进行调用时要在自定义RefreshListView中进行刷新当前的控件状态,即对头部空间和尾部空间进行隐藏。
<span style="font-size:14px;">/**
* 获取当前系统时间,并格式化
*
* @return
*/
private String getCurrentTime() {
SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
return format.format(new Date());
}
public OnRefreshListener mListener;
public void setOnRefreshLisetener(OnRefreshListener listener) {
this.mListener = listener;
}
public interface OnRefreshListener {
void onPullRefresh();
void onLoadMore();
}
这是主界面的代码:
</span><pre name="code" class="java"> mRefreshListView.setOnRefreshLisetener(new RefreshListView.OnRefreshListener() {
@Override
public void onPullRefresh() {
requestDataFromServer(false);
}
@Override
public void onLoadMore() {
requestDataFromServer(true);
}
});
}
/**
* 模拟向服务器请求数据
*/
private void requestDataFromServer(final boolean isLoadMore) {
new Thread() {
public void run() {
SystemClock.sleep(3000);//模拟请求服务器的一个时间长度
if (isLoadMore){
list.add("加载更多的数据-1");
list.add("加载更多的数据-2");
list.add("加载更多的数据-3");
}else {
list.add(0, "下拉刷新的数据");
}
//在UI线程更新UI
mHandler.sendEmptyMessage(0);
}
;
}.start();
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
mAdapter.notifyDataSetChanged();
mRefreshListView.completeRefresh();
}
};
添加了数据之后在RefreshListView中进行刷新当前的控件状态,
<span style="font-size:14px;">/**
* 完成刷新操作,重置状态,在你获取完数据并更新完adater之后,去在UI线程中调用该方法
*/
public void completeRefresh() {
if (isLoadingMore) {
//重置footerView状态
mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);
isLoadingMore = false;
} else {
//重置headerView状态
mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
currentState = PULL_REFRESH;
tvState.setText("下拉刷新");
ivArrow.setVisibility(VISIBLE);
pbRotate.setVisibility(INVISIBLE);
tvTime.setText("最后刷新:" + getCurrentTime());
}
}</span>
demo代码下载地址:http://pan.baidu.com/s/1eROWzbs 密码:ssxg