效果图
思考
Listview是viewGroup的子类,它本身提供了方法addHeaderView(View view),addFooterView(View view)去添加头布局和底布局,所以我们只要监听它的onTouchEvent方法,判断头布局 底布局的显示状态。
头布局(headerView)
默认的话,得隐藏。先获取到头布局的高度int headerViewHeight = headerView.getMeasuredHeight();通过设置它的padding(0, -headerViewHeight, 0, 0); 这里要注意:获取头布局高度之前,需要等头布局完全加载上来后,才获取得到,否则为0。可以通过measure()方法主动通知系统去测量该view,也可以通过获取视图树的观察者去增加一个全局的布局监听器。
重写onTouchEvent(MotionEvent event);
手指按下时 获取该view的Y方向的坐标downY。
手指移动时
获取移动的Y方向的距离deltY = (int) (event.getY() - downY);
将移动的距离deltY+(-headerViewHeight)就是头布局的位置paddingTop。
当然这个paddingTop需要限制,比如你上滑时,如果已经到了-headerViewHeight,就不要继续将头布局往上设置了;而且下拉时,如果不是第一个条目,就用listview本身的滑动事件,限制代码:paddingTop > -headerViewHeight && getFirstVisiblePosition() == 0
在此条件下,继续判断:如果paddingTop>0 && currentState==下拉刷新状态,就将currentState设置为松开立即刷新状态并更新头布局内容;如果paddingTop<0 && currentState==松开立即刷新状态,就将currentState设置为下拉刷新状态并更新头布局内容。
手指抬起时
如果是下拉刷新状态,则隐藏头布局
如果是松开立即刷新状态,则显示头布局,将当前状态设置为正在刷新状态并更新头布局内容。同时在这里设置暴露一个接口方法,让用户在该方法中去加载要刷新出来的数据。
类中还应该提供刷新完成重置headerView的方法,由用户在获取完数据并更新完adapter之后,去在UI线程中调用该方法。
底布局(footerView)
默认隐藏。方法同头布局一样
在初始化的时候,为listview设置一个滑动监听。当滑动状态为OnScrollListener.SCROLL_STATE_IDLE (即手指松开) && 是滑动listview最后一个条目 && 不是加载更多,这时,将footerView显示出来,同时在这里设置暴露一个接口方法,让用户在该方法中去加载更多要加载出来的数据。
提供加载完成重置footerView的方法
步骤
1.headerView布局
<?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"> <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:src="@mipmap/indicator_arrow" /> <ProgressBar android:id="@+id/pb_load" android:layout_width="30dp" android:layout_height="30dp" android:layout_centerInParent="true" android:indeterminateDuration="1000" android:indeterminateDrawable="@drawable/indeterminate_drawable" android:visibility="invisible" /> </RelativeLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:orientation="vertical"> <TextView android:id="@+id/tv_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="下拉刷新" android:textColor="#a000" android:textSize="20sp" /> <TextView android:id="@+id/tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="5dp" android:text="最后更新:2015-06-06" android:textColor="@android:color/darker_gray" android:textSize="14sp" /> </LinearLayout> </LinearLayout>
2.footerView布局
<?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_marginBottom="10dp" android:layout_marginTop="10dp" android:indeterminateDrawable="@drawable/indeterminate_drawable" android:indeterminateDuration="1000" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:text="加载更多..." android:textSize="16sp" /> </LinearLayout>
3.自定义listview的代码
public class RefreshListView extends ListView implements AbsListView.OnScrollListener { private static final String TAG = "RefreshListView1"; private View headerView;//头布局 private ImageView iv_arrow; private ProgressBar pb_load; private TextView tv_state; private TextView tv_time; private View footerView;//底布局 private int headerViewHeight;//头布局的高度 private int downY;//手指按下时Y的坐标 private static final int PULL_TO_REFRESH = 0; //下拉刷新 private static final int RELEASE_REFRESH = 1; //松开以刷新 private static final int REFRESHING = 2;//正在刷新 private int currentState = PULL_TO_REFRESH; private RotateAnimation upAnimation; private RotateAnimation downAnimation; private int footerViewHeight;//底布局的高度 private boolean isLoadingMore = false;//当前是否正在处于加载更多 public RefreshListView(Context context) { super(context); init(); } public RefreshListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public RefreshListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { setOnScrollListener(this); initHeaderView(); initRotateAnimation(); initFooterView(); } /** * 初始化头布局 */ private void initHeaderView() { headerView = View.inflate(getContext(), R.layout.layout_header, null); iv_arrow = (ImageView) headerView.findViewById(R.id.iv_arrow); pb_load = (ProgressBar) headerView.findViewById(R.id.pb_load); tv_state = (TextView) headerView.findViewById(R.id.tv_state); tv_time = (TextView) headerView.findViewById(R.id.tv_time); // 第一种方法获取头布局的高度 // final ViewTreeObserver viewTreeObserver = headerView.getViewTreeObserver(); // viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { // @Override // public void onGlobalLayout() { // viewTreeObserver.removeOnGlobalLayoutListener(this); // int headerViewHeight = headerView.getHeight(); // Log.i(TAG, "headerViewHeight:" + headerViewHeight); // headerView.setPadding(0, -headerViewHeight, 0, 0); // addHeaderView(headerView); // } // }); //第二种方法获取头布局的高度 headerView.measure(0, 0);//主动通知系统去测量该view; headerViewHeight = headerView.getMeasuredHeight(); Log.i(TAG, "headerViewHeight:" + headerViewHeight); headerView.setPadding(0, -headerViewHeight, 0, 0); addHeaderView(headerView); } /** * 初始化旋转动画 */ 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); } private void initFooterView() { footerView = View.inflate(getContext(), R.layout.layout_footer, null); footerView.measure(0, 0); footerViewHeight = footerView.getMeasuredHeight(); footerView.setPadding(0, -footerViewHeight, 0, 0); addFooterView(footerView); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) event.getY(); break; case MotionEvent.ACTION_MOVE: if (currentState == REFRESHING) { break; } int deltY = (int) (event.getY() - downY); int paddingTop = -headerViewHeight + deltY; if (paddingTop > -headerViewHeight && getFirstVisiblePosition() == 0) {//下拉的状态 并且 listview第一个条目可见 Log.i(TAG, "paddingTop:" + paddingTop); headerView.setPadding(0, paddingTop, 0, 0); if (paddingTop > 0 && currentState == PULL_TO_REFRESH) {//头布局可见 并且为 下拉刷新状态 //进入松开刷新状态 currentState = RELEASE_REFRESH; refreshHeaderView(); } else if (paddingTop < 0 && currentState == RELEASE_REFRESH) { currentState = PULL_TO_REFRESH; refreshHeaderView(); } return true; } break; case MotionEvent.ACTION_UP: if (currentState == PULL_TO_REFRESH) { //隐藏headerView headerView.setPadding(0, -headerViewHeight, 0, 0); } else if (currentState == RELEASE_REFRESH) { headerView.setPadding(0, 0, 0, 0); currentState = REFRESHING; refreshHeaderView(); if (listener != null) { listener.onPullRefresh(); } } break; } return super.onTouchEvent(event); } /** * 刷新headerView */ private void refreshHeaderView() { switch (currentState) { case PULL_TO_REFRESH: tv_state.setText("下拉刷新"); iv_arrow.startAnimation(downAnimation); break; case RELEASE_REFRESH: tv_state.setText("松开立即刷新"); iv_arrow.startAnimation(upAnimation); break; case REFRESHING: iv_arrow.clearAnimation();//因为向上的旋转动画有可能没有执行完 iv_arrow.setVisibility(View.INVISIBLE); pb_load.setVisibility(View.VISIBLE); tv_state.setText("正在刷新..."); break; } } /** * 完成刷新操作,重置状态,在获取完数据并更新完adapter之后,去在UI线程中调用该方法 */ public void completeRefresh() { if (isLoadingMore) { //重置footerView状态 footerView.setPadding(0, -footerViewHeight, 0, 0); isLoadingMore = false; } else { //重置headerView状态 headerView.setPadding(0, -headerViewHeight, 0, 0); currentState = PULL_TO_REFRESH; pb_load.setVisibility(View.INVISIBLE); iv_arrow.setVisibility(View.VISIBLE); tv_state.setText("下拉刷新"); tv_time.setText("最后刷新:" + getCurrentTime()); } } /** * 获取当前系统时间,并格式化 */ private String getCurrentTime() { SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd HH:mm:ss"); return format.format(new Date()); } private OnRefreshListener listener; public void setOnRefreshListener(OnRefreshListener listener) { this.listener = listener; } public interface OnRefreshListener { void onPullRefresh(); void onLoadingMore(); } /** * SCROLL_STATE_IDLE:闲置状态,手指松开 值为0 * SCROLL_STATE_TOUCH_SCROLL:触摸滑动状态 值为1 * SCROLL_STATE_FLING:快速滑动状态 值为2 */ @Override public void onScrollStateChanged(AbsListView view, int scrollState) { Log.i(TAG, "scrollState = " + scrollState); if (scrollState == OnScrollListener.SCROLL_STATE_IDLE && getLastVisiblePosition() == (getCount() - 1) && !isLoadingMore) { isLoadingMore = true; footerView.setPadding(0, 0, 0, 0);//显示出footerView setSelection(getCount());//让listview最后一条显示出来 if (listener != null) { listener.onLoadingMore(); } } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }
4.主函数调用的布局
<?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"> <com.example.refreshlistview.view.RefreshListView android:id="@+id/my_refresh_listview" android:layout_width="match_parent" android:layout_height="match_parent"> </com.example.refreshlistview.view.RefreshListView> </LinearLayout>
5.主函数调用的代码
public class MainActivity extends Activity { private RefreshListView myRefreshListView; private List<String> list = new ArrayList<String>(); private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); adapter.notifyDataSetChanged(); myRefreshListView.completeRefresh(); } }; private MyAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initViews(); initData(); } private void initViews() { setContentView(R.layout.activity_main); myRefreshListView = (RefreshListView) this.findViewById(R.id.my_refresh_listview); } private void initData() { for (int i = 0; i < 20; i++){ list.add("listview的原始数据:"+i); } initListView(); } private void initListView() { adapter = new MyAdapter(); myRefreshListView.setAdapter(adapter); myRefreshListView.setOnRefreshListener(new RefreshListView.OnRefreshListener() { @Override public void onPullRefresh() { getDataFromServer(false); } @Override public void onLoadingMore() { getDataFromServer(true); } }); } /** * 模拟向服务器请求数据 */ private void getDataFromServer(final boolean isLoadingMore){ new Thread(){ @Override public void run() { SystemClock.sleep(3000);//模拟请求服务器的一个时间长度 if (!isLoadingMore) { list.add(0, "下拉刷新的数据-1"); } else { list.add("加载更多的数据-1"); list.add("加载更多的数据-2"); list.add("加载更多的数据-3"); } //在UI线程更新UI handler.sendEmptyMessage(0); } }.start(); } class MyAdapter extends BaseAdapter{ @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView textView = new TextView(MainActivity.this); textView.setText(list.get(position)); textView.setTextSize(18); textView.setPadding(20, 20 ,20, 20); return textView; } }