ListView下拉刷新

来源:互联网 发布:epica软件下载 编辑:程序博客网 时间:2024/06/07 02:55

自定义ListView下拉刷新及加载功能

       通过自定义LinearLayout,动态增加HeadView及ListView,FootView增加到listview中。通过onInterceptTouchEvent来判段是否下拉刷新及加载,最终的操作在onTouchEvent中实现。

 

一、准备页面布局

 1、headview : refresh_head_view.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="match_parent"
    android:gravity="center"
    android:id="@+id/ly_head_view"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/head_pull_iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/ic_pulltorefresh_arrow" />

    <ProgressBar
        android:id="@+id/head_progress_pb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="center">

        <TextView
            android:id="@+id/head_refresh_tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/pull_down_refresh" >
        </TextView>

        <TextView
            android:id="@+id/head_updatetime_tip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="上次更新时间:2012-01-01 00:00:00" >
        </TextView>
    </LinearLayout>

</LinearLayout>

 

 

2、footview : refresh_footer_view.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ly_footer_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:gravity="center"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/footer_pull_tips"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="@string/load_more" >
    </TextView>

    <ProgressBar
        android:id="@+id/footer_progress_pb"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:visibility="gone" />

</LinearLayout>

3、listview : ly_lv.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="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/refresh_lv"
        android:layout_width="match_parent"
        android:layout_height="fill_parent" >
    </ListView>

</LinearLayout>

 

4、自定义LinearLayout : refresh_layout.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="match_parent"
    android:orientation="vertical" >

    <com.example.customrefreshlistview.widget.PullRefreshView
        android:id="@+id/pullRefreshView"
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
>
    </com.example.customrefreshlistview.widget.PullRefreshView>

</LinearLayout>

5、主布局 : activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >


<include layout="@layout/refresh_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
</LinearLayout>

 

在主布局中只要引用refresh_layout布局就可以了。

 

二、功能实现  PullRefreshView

package com.example.customrefreshlistview.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.example.customrefreshlistview.R;

public class PullRefreshView extends LinearLayout {
 // head
 private View headView;
 private ProgressBar pbHeadRefresh;
 private ImageView ivHeadTip;
 private TextView tvHeadTip;
 private TextView tvHeadUpdateTime;

 // footer
 private View footerView;
 private ProgressBar pbFooterRefresh;
 private TextView tvFooterTip;

 private ListView lv;
 private View lvView;

 private float downY;

 private int headViewHeight;

 private RefreshListener refreshListener;

 private static final int PULL_DOWN_STATE = 0;
 private static final int PULL_UP_STATE = 1;

 private static final int PULL_REFRESHING = 2;
 private static final int PULL_DOWN_REFRESH = 3;
 private static final int PULL_RELEASE_REFRESH = 4;

 private static final int PULL_UP_REFRESH = 5;
 private static final int PULL_UP_RELEASE_REFRESH = 6;
 private static final int PULL_UP_REFRESHING = 7;

 private int mPullState = PULL_DOWN_STATE;
 private int mPullHeadState = PULL_DOWN_REFRESH;
 private int mPullFooterState = PULL_UP_REFRESH;

 private RotateAnimation mFlipAnimation;
 private RotateAnimation mReverseFlipAnimation;

 private static final int PULL_UP_DISTANCE = 20;

 public ListView getListView() {
  return lv;
 }

 public PullRefreshView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public PullRefreshView(Context context) {
  super(context);
  init();
 }

 private void init() {
  headView = LayoutInflater.from(getContext()).inflate(
    R.layout.refresh_head_view, null);
  ivHeadTip = (ImageView) headView.findViewById(R.id.head_pull_iv);
  pbHeadRefresh = (ProgressBar) headView
    .findViewById(R.id.head_progress_pb);
  tvHeadTip = (TextView) headView.findViewById(R.id.head_refresh_tip);
  tvHeadUpdateTime = (TextView) headView
    .findViewById(R.id.head_updatetime_tip);

  lvView = LayoutInflater.from(getContext())
    .inflate(R.layout.ly_lv, null);
  lv = (ListView) lvView.findViewById(R.id.refresh_lv);

  footerView = LayoutInflater.from(getContext()).inflate(
    R.layout.refresh_footer_view, null);
  pbFooterRefresh = (ProgressBar) footerView
    .findViewById(R.id.footer_progress_pb);
  tvFooterTip = (TextView) footerView.findViewById(R.id.footer_pull_tips);

  addView(headView);
  addView(lvView);

  mFlipAnimation = new RotateAnimation(0, -180,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  mFlipAnimation.setInterpolator(new LinearInterpolator());
  mFlipAnimation.setDuration(250);
  mFlipAnimation.setFillAfter(true);
  mReverseFlipAnimation = new RotateAnimation(-180, 0,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f);
  mReverseFlipAnimation.setInterpolator(new LinearInterpolator());
  mReverseFlipAnimation.setDuration(250);
  mReverseFlipAnimation.setFillAfter(true);

 }

 @Override
 protected void onFinishInflate() {
  LinearLayout.LayoutParams lvParam = (LinearLayout.LayoutParams) lvView
    .getLayoutParams();
  lvParam.weight = 1;
  lvView.setLayoutParams(lvParam);

  lv.addFooterView(footerView);
   lv.setFooterDividersEnabled(false);
  lv.setHeaderDividersEnabled(false);

  measureHeadViewHeight();
  resetHeadView();

  footerViewVisible(0);
  resetFooterView();
  
 }

 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
  switch (ev.getAction()) {
  case MotionEvent.ACTION_DOWN:
   downY = ev.getRawY();
   break;
  case MotionEvent.ACTION_MOVE:
   float moveY = ev.getRawY();
   int deltaY = (int) (moveY - downY);
   if (isRefresh(deltaY)) {
    return true;
   }
   break;
  }
  return false;
 }

 private boolean isRefresh(int deltaY) {
  if (deltaY > 0) {
   if (lv.getChildCount() <= 0) {
    mPullState = PULL_DOWN_STATE;
    return true;
   }

   if (lv.getFirstVisiblePosition() == 0) {
    mPullState = PULL_DOWN_STATE;
    return true;
   }
  } else if (deltaY < 0) {
   if (lv.getChildCount() > 0
     && lv.getLastVisiblePosition() == lv.getCount() - 1) {
    mPullState = PULL_UP_STATE;
    return true;
   }
  }
  return false;
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction() & MotionEvent.ACTION_MASK) {
  case MotionEvent.ACTION_DOWN:
   
   break;
  case MotionEvent.ACTION_MOVE:
   if (isRefreshing()) {
    return super.onTouchEvent(event);
   }
   if (mPullState == PULL_DOWN_STATE) {
    preparePullDownRefresh((int) (event.getRawY() - downY));
   } else if (mPullState == PULL_UP_STATE) {
    int yDistance = Math.abs((int) (event.getRawY() - downY));
    preparePullUpRefresh(yDistance);
   }
   break;
  case MotionEvent.ACTION_UP:
   if (isRefreshing()) {
    return super.onTouchEvent(event);
   }
   if (mPullState == PULL_DOWN_STATE) {
    LinearLayout.LayoutParams ly = (LinearLayout.LayoutParams) headView
      .getLayoutParams();
    if (ly.topMargin > headViewHeight) {
     headRefreshing();
    } else {
     resetHeadView();
    }

   } else if (mPullState == PULL_UP_STATE) {
    if (mPullFooterState == PULL_UP_RELEASE_REFRESH) {
     footerRefreshing();
    } else {
     resetFooterView();
    }
   }
   break;
  }
  return super.onTouchEvent(event);
 }

 private boolean isRefreshing() {
  boolean bPullDownRefresh = mPullHeadState == PULL_REFRESHING ? true
    : false;
  boolean bPullUpRefresh = mPullFooterState == PULL_UP_REFRESHING ? true
    : false;
  return bPullDownRefresh || bPullUpRefresh;
 }

 private void preparePullUpRefresh(int deltaY) {
  int nFootViewHeight = footerView.getHeight();
  setHeadMarginTop(-(deltaY + headViewHeight));
  tvFooterTip.setVisibility(View.VISIBLE);
  pbFooterRefresh.setVisibility(View.GONE);
  if (deltaY < nFootViewHeight && mPullFooterState != PULL_UP_REFRESH) {
   tvFooterTip.setText(R.string.load_more);
   mPullFooterState = PULL_UP_REFRESH;
  } else if (deltaY > nFootViewHeight
    && mPullFooterState != PULL_UP_RELEASE_REFRESH) {
   tvFooterTip.setText(R.string.load_more_done);
   mPullFooterState = PULL_UP_RELEASE_REFRESH;
  }
 }

 private void preparePullDownRefresh(int deltaY) {
  setHeadMarginTop(deltaY - headViewHeight);
  ivHeadTip.setVisibility(View.VISIBLE);
  pbHeadRefresh.setVisibility(View.GONE);
  if (deltaY < headViewHeight && mPullHeadState != PULL_DOWN_REFRESH) {
   ivHeadTip.startAnimation(mFlipAnimation);
   mPullHeadState = PULL_DOWN_REFRESH;
   tvHeadTip.setText(R.string.pull_down_refresh);
  } else if (deltaY > headViewHeight
    && mPullHeadState != PULL_RELEASE_REFRESH) {
   ivHeadTip.startAnimation(mFlipAnimation);
   tvHeadTip.setText(R.string.pull_release_refresh);
   mPullHeadState = PULL_RELEASE_REFRESH;
  }
 }

 private void headRefreshing() {
  setHeadMarginTop(0);
  mPullHeadState = PULL_REFRESHING;
  ivHeadTip.setVisibility(View.GONE);
  pbHeadRefresh.setVisibility(View.VISIBLE);
  tvHeadTip.setText(R.string.loading_data);
  if (null != refreshListener) {
   refreshListener.onRefresh();
  }
 }

 private void footerRefreshing() {
  mPullFooterState = PULL_UP_REFRESHING;
  tvFooterTip.setVisibility(View.GONE);
  pbFooterRefresh.setVisibility(View.VISIBLE);
  setHeadMarginTop(-headViewHeight );
  if (null != refreshListener) {
   refreshListener.onLoadMore();
  }
 }

 private void measureHeadViewHeight() {
  ViewGroup.LayoutParams p = headView.getLayoutParams();
  int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
  int childHeightSpec;
  int lpHeight = p.height;
  if (lpHeight > 0) {
   childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
     MeasureSpec.EXACTLY);
  } else {
   childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,
     MeasureSpec.UNSPECIFIED);
  }
  headView.measure(childWidthSpec, childHeightSpec);
  headViewHeight = headView.getMeasuredHeight();
 }

 public void resetHeadView() {
  setHeadMarginTop(-headViewHeight);
  mPullState = PULL_DOWN_STATE;
  mPullHeadState = PULL_DOWN_REFRESH;
 }

 public void resetFooterView() {
  tvFooterTip.setVisibility(View.VISIBLE);
  pbFooterRefresh.setVisibility(View.GONE);
  tvFooterTip.setText(R.string.load_more);
  mPullState = PULL_UP_STATE;
  mPullFooterState = PULL_UP_REFRESH;
 }

 public void footerViewVisible(int nCount) {
  if (nCount > 0) {
   footerView.setVisibility(View.VISIBLE);
  } else {
   footerView.setVisibility(View.GONE);
  }
  setHeadMarginTop(-headViewHeight);
 }

 // private void setFooterPaddingBottom(int bottomMargin) {
 // footerView.setPadding(footerView.getPaddingLeft(),
 // footerView.getPaddingTop(), footerView.getPaddingRight(),
 // bottomMargin);
 // // LinearLayout.LayoutParams footerParam = (LinearLayout.LayoutParams)
 // // footerView
 // // .getLayoutParams();
 // // footerParam.bottomMargin = bottomMargin;
 // // footerView.setLayoutParams(footerParam);
 // }

 private void setHeadMarginTop(int topMargin) {
  LinearLayout.LayoutParams lyParam = (LinearLayout.LayoutParams) headView
    .getLayoutParams();
  lyParam.topMargin = topMargin;
  headView.setLayoutParams(lyParam);
 }

 public void setRefreshListener(RefreshListener refreshListener) {
  this.refreshListener = refreshListener;
 }

 public interface RefreshListener {
  public void onRefresh();

  public void onLoadMore();
 }
}

 

首先,init()初始化布局,headview,listview 加入LinearLayout布局,footview 加入listview.

然后,重写onInterceptTouchEvent及onTouchEvent。onInterceptTouchEvent返回false,才会触发onTouchEvent,否则自己消化掉。

在onInterceptTouchEvent中,通过

 

 private boolean isRefresh(int deltaY) {
  if (deltaY > 0) {
   if (lv.getChildCount() <= 0) {
    mPullState = PULL_DOWN_STATE;
    return true;
   }

   if (lv.getFirstVisiblePosition() == 0) {
    mPullState = PULL_DOWN_STATE;
    return true;
   }
  } else if (deltaY < 0) {
   if (lv.getChildCount() > 0
     && lv.getLastVisiblePosition() == lv.getCount() - 1) {
    mPullState = PULL_UP_STATE;
    return true;
   }
  }
  return false;
 }

这个函数来判断下拉刷新还是上啦加载。

在onTouchEvent中实现下拉及加载的状态变化及最终的操作。

 

 

 

0 0
原创粉丝点击