自定义控件-下拉刷新

时间:2021-02-06 20:39:02


开发中最常用的下拉刷新控件

package com.pull.refresh;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

public class Pull2RefreshListView extends ListView {
 /**
  * 下拉刷新状态
  */
 public static final int PULL_REFRESH_STATE = 0x1;
 /**
  * 松开刷新状态
  */
 public static final int DISENTANGLE_REFRESH_STATE = 0x2;
 /**
  * 刷新状态
  */
 public static final int REFRESHING_STATE = 0x3;

 /**
  * 当前状态
  */
 private int mCurrentState = PULL_REFRESH_STATE;

 /**
  * 头布局
  */
 private View mHeaderView;
 /**
  * 箭头
  */
 private ImageView arrow;
 /**
  * 进度圆
  */
 private ProgressBar round;
 /**
  * 下拉刷新
  */
 private TextView pull;
 /**
  * 时间
  */
 private TextView date;
 /**
  * 布局高度
  */
 private int mHeight;
 /**
  * 按下时竖直方向的开始坐标
  */
 private int startY;
 /**
  * 向上旋转动画
  */
 private RotateAnimation animUp;
 /**
  * 向下旋转动画
  */
 private RotateAnimation animDown;

 public Pull2RefreshListView(Context context) {
  this(context, null);
 }

 public Pull2RefreshListView(Context context, AttributeSet attrs) {
  this(context, attrs, 0);
 }

 public Pull2RefreshListView(Context context, AttributeSet attrs,
   int defStyle) {
  super(context, attrs, defStyle);
  initHeaderView();
 }

 /**
  * 初始化头布局
  */
 private void initHeaderView() {
  mHeaderView = View.inflate(getContext(), R.layout.list_header, null);
  arrow = (ImageView) mHeaderView.findViewById(R.id.iv_arrow);
  round = (ProgressBar) mHeaderView.findViewById(R.id.pb_round);
  pull = (TextView) mHeaderView.findViewById(R.id.tv_pull);
  date = (TextView) mHeaderView.findViewById(R.id.tv_date);
  this.addHeaderView(mHeaderView);
  initData();
  initAnim();
 }

 /**
  * 初始化数据
  */
 private void initData() {
  mHeaderView.measure(0, 0);
  mHeight = mHeaderView.getMeasuredHeight();
  mHeaderView.setPadding(0, -mHeight, 0, 0);
 }

 /**
  * 滑动事件
  */
 @Override
 public boolean onTouchEvent(MotionEvent ev) {
  switch (ev.getAction()) {
  case MotionEvent.ACTION_DOWN:// 按下获取竖直方向的开始坐标
   startY = (int) ev.getY();

   break;
  case MotionEvent.ACTION_MOVE:// 移动获取竖直方西的差值
   if (startY == -1) {
    // 当用户按住头条新闻的viewpager进行下拉时,ACTION_DOWN可能被消费掉,
    // 导致startY没有赋值,此处需要重新获取一下
    startY = (int) ev.getY();
   }
   if (mCurrentState == REFRESHING_STATE) {
    // 如果是正在刷新, 跳出循环
    break;
   }

   int endY = (int) ev.getY();
   int dy = endY - startY;
   int firstVisiblePosition = getFirstVisiblePosition();// 当前显示的第一个item的位置

   int padding = -mHeight + dy;// 计算当前下拉控件的padding值
   if (padding > 20) {//限制下拉的距离
    padding = 20;
   }
   // 必须下拉,并且当前显示的是第一个item
   if (dy > 0 && firstVisiblePosition == 0) {
    mHeaderView.setPadding(0, padding, 0, 0);

    if (padding > 0 && mCurrentState != DISENTANGLE_REFRESH_STATE) {// 松开刷新,把当前状态改为松开刷新
     mCurrentState = DISENTANGLE_REFRESH_STATE;
     refreshState();
    } else if (padding < 0 && mCurrentState != PULL_REFRESH_STATE) {// 下拉刷新状态,把当前状态改为改为下拉刷新
     mCurrentState = PULL_REFRESH_STATE;
     refreshState();
    }

    return true;
   }
   break;
  case MotionEvent.ACTION_UP:// 抬起
   startY = -1;
   if (mCurrentState == DISENTANGLE_REFRESH_STATE) {
    mCurrentState = REFRESHING_STATE;
    refreshState();
    // 完整展示头布局
    mHeaderView.setPadding(0, 0, 0, 0);
    // 进行回调
    if (listener != null) {
     listener.onRefresh();
    }

   } else if (mCurrentState == PULL_REFRESH_STATE) {
    // 隐藏头布局
    mHeaderView.setPadding(0, -mHeight, 0, 0);
   }
   break;
  default:
   break;
  }
  return super.onTouchEvent(ev);
 }

 /**
  * 根据当前状态刷新界面
  */
 private void refreshState() {
  switch (mCurrentState) {
  case PULL_REFRESH_STATE:
   pull.setText("下拉刷新");
   round.setVisibility(View.INVISIBLE);
   arrow.setVisibility(View.VISIBLE);
   arrow.startAnimation(animDown);
   break;
  case DISENTANGLE_REFRESH_STATE:
   pull.setText("松开刷新");
   round.setVisibility(View.INVISIBLE);
   arrow.setVisibility(View.VISIBLE);
   arrow.startAnimation(animUp);
   break;
  case REFRESHING_STATE:
   pull.setText("正在刷新...");
   arrow.clearAnimation();// 清除箭头动画,否则无法隐藏
   round.setVisibility(View.VISIBLE);
   arrow.setVisibility(View.INVISIBLE);
   break;

  default:
   break;
  }
 }

 /**
  * 设置箭头动画
  */
 private void initAnim() {
  animUp = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f,
    Animation.RELATIVE_TO_SELF, 0.5f);
  animUp.setDuration(200);
  animUp.setFillAfter(true);

  animDown = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF,
    0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
  animDown.setDuration(200);
  animDown.setFillAfter(true);
 }

 /**
  * 下拉刷新的回调接口
  */
 public interface OnRefreshListener {
  /**
   * 刷新状态时调用
   */
  public void onRefresh();

  /**
   * 下拉加载更多
   */
  public void onLoadMore();
 }

 /**
  * 接口引用
  */
 private OnRefreshListener listener;

 /**
  * 暴露接口,设置监听
  */
 public void setOnRefreshListener(OnRefreshListener listener) {
  this.listener = listener;
 }

 /**
  * 刷新结束,隐藏头布局,还原状态
  */
 public void onRefreshComplete() {
  mHeaderView.setPadding(0, -mHeight, 0, 0);
  mCurrentState = PULL_REFRESH_STATE;
  pull.setText("下拉刷新");
  round.setVisibility(View.INVISIBLE);
  arrow.setVisibility(View.VISIBLE);

 }

}


头布局

<?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:orientation="horizontal" >

    <FrameLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp" >

        <ImageView
            android:id="@+id/iv_arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/common_listview_headview_red_arrow" />

        <ProgressBar
            android:id="@+id/pb_round"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="invisible"/>
    </FrameLayout>

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

        <TextView
            android:id="@+id/tv_pull"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:gravity="center"
            android:text="下拉刷新"
            android:textSize="20dp" />

        <TextView
            android:id="@+id/tv_date"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:text="2016-09-02 15-23-43"
            android:textSize="16dp" />
    </LinearLayout>

</LinearLayout>