Android实现简单的下拉刷新pulltorefresh

网上下拉刷新的DEMO很多,但是总有各种不满意的地方,有些会下拉卡住,有些回弹不流畅,有些性能太低会各种卡顿,有些emptyView无法下拉...... 

自己写的才是最合适自己的,代码很简单,也很容易修改,稍微阅读下代码就能改出自己需要的各种效果。

首先,重写ListView,自定义Touch事件,为了使emptyView也可下拉,emptyView也加上Touch事件。 如果要实现GridView,把这里的ListView改成GridView即可。

PullableListView :

public class PullableListView extends ListView {
  private boolean inited;
  private float density;
  private int mDownY, mMoveY;
  private int mPullY;
  private boolean isPull;
  private PullListener mPullListener;
  private VelocityTracker mVelocityTracker;

  public interface PullListener {

    public boolean onPullDownStart();

    public void onPullDown(int moveY);

    public void onPullDownDrop();
  }

  public PullableListView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
  }

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

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

  private void init() {
    if (!inited) {
      density = getResources().getDisplayMetrics().density;
    }
  }

  public void setPullListener(PullListener mPullListener) {
    this.mPullListener = mPullListener;
  }

  public boolean isPulling() {
    return isPull;
  }

  @Override
  public void setEmptyView(View emptyView) {
    super.setEmptyView(emptyView);
    // 重写emptyView的Touch事件,使显示emptyView时也可以下拉刷新
    emptyView.setOnTouchListener(new OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent ev) {
        if (mVelocityTracker == null) {
          mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);
        switch (ev.getAction()) {
          case MotionEvent.ACTION_DOWN:
            mDownY = (int) ev.getY();
            break;
          case MotionEvent.ACTION_MOVE:
            mMoveY = (int) ev.getY();
            if (!isPull) {
              mVelocityTracker.computeCurrentVelocity(1000, 8000f);
              if (mVelocityTracker.getYVelocity() > 500 // 下拉速度大于500
                  && Math.abs(mMoveY - mDownY) > 20 * density) { // 下拉距离超过20dp
                mPullY = mMoveY;
                if (mPullListener.onPullDownStart()) {
                  isPull = true;
                }
              }
            } else {
              // 阻尼下拉(随着下拉距离增加,阻力增加)
              mPullListener.onPullDown(mMoveY - mPullY + v.getScrollY());
              // 等阻力下拉(阻力恒定,不随下拉距离增加而增加)
              // mPullListener.onPullDown(mMoveY - mPullY);
              if (mMoveY < mPullY) {
                isPull = false;
              }
              return true;
            }
            break;
          case MotionEvent.ACTION_UP:
            if (mVelocityTracker != null) {
              mVelocityTracker.clear();
              mVelocityTracker.recycle();
              mVelocityTracker = null;
            }
            if (isPull) {
              mPullY = 0;
              isPull = false;
              mPullListener.onPullDownDrop();
              return true;
            }
            break;
        }
        return true;
      }
    });
  }

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (isPull) {
      // 正在下拉时,阻住Touch事件向下传递,同时会向各个ChildView发送ACTION_CANLE事件,
      // 使之前捕捉到了ACTION_DOWN事件的ChildView回复到正常状态
      return true;
    }

    return super.onInterceptTouchEvent(ev);
  }

  @Override
  public boolean onTouchEvent(MotionEvent ev) {
    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
    mVelocityTracker.addMovement(ev);
    switch (ev.getAction()) {
      case MotionEvent.ACTION_DOWN:
        mDownY = (int) ev.getY();
        break;
      case MotionEvent.ACTION_MOVE:
        mMoveY = (int) ev.getY();
        if (!isPull) {
          if (getFirstVisiblePosition() == 0) {
            View view = getChildAt(0);
            mVelocityTracker.computeCurrentVelocity(1000, 8000f);
            if (mVelocityTracker.getYVelocity() > 500// 下拉速度大于500
                && (view == null || view.getTop() == getPaddingTop()) // 已拉动到顶部
                && Math.abs(mMoveY - mDownY) > 15 * density) { // 下拉距离超过20dp
              mPullY = mMoveY;
              if (mPullListener.onPullDownStart()) {
                // 根据返回值确认是否进入下拉状态
                isPull = true;
              }
            }
          }
        } else {
          // 阻尼下拉(随着下拉距离增加,阻力增加)
          mPullListener.onPullDown(mMoveY - mPullY);
          // 等阻力下拉(阻力恒定,不随下拉距离增加而增加)
          // mPullListener.onPullDown(mMoveY - mPullY - getScrollY());
          if (mMoveY < mPullY) {
            isPull = false;
          }
          return true;
        }
        break;
      case MotionEvent.ACTION_UP:
        if (mVelocityTracker != null) {
          mVelocityTracker.clear();
          mVelocityTracker.recycle();
          mVelocityTracker = null;
        }
        if (isPull) {
          mPullY = 0;
          isPull = false;
          mPullListener.onPullDownDrop();
          return true;
        }
        break;
      case MotionEvent.ACTION_CANCEL:
        break;
    }
    return super.onTouchEvent(ev);
  }
}

然后是外层的LinearyLayer,监听PullableListView的下拉回调,实现下拉效果。同时提供ListView(GridView)的外部接口,如 setEmptyView(View view),setAdapter(ListAdapter adapter)...等等,这里只提供部分我需要使用的,可以根据自身需求去提供外部接口。 
代码中R.drawable.pulltorefresh 和 R.drawable.loading 分别是下拉箭头 和 刷新滚动条 的图片,这里不提供了,自己随意找两张图片贴上就行了。 

PullToRefreshView: 

public class PullToRefreshView extends LinearLayout {

  protected static final String TAG = "PullToRefreshView";

  /**
   * 下拉阻力系数
   */
  private static final float SCALL_PULL_DOWW = 2.0f;

  private View mView;

  private PullableListView mListView;

  private TextView mPullTv;

  private ImageView mProgressBar;

  private View mPullV;

  private View mEmptyView;

  private boolean isInited;

  private boolean canRefresh;

  private boolean isRefreshing;

  private boolean isPullable = true;

  private int mOrMargin;

  private ObjectAnimator mArrowRotateAnimator;

  private Animation mProAnimation;

  private PullToRefreshListener mPullToRefreshListener;

  public PullToRefreshView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initView(context);
  }

  public PullToRefreshView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView(context);
  }

  public PullToRefreshView(Context context) {
    super(context);
    initView(context);
  }

  public interface PullToRefreshListener {
    /**
     * do data refresh here
     */
    public void onRefreshStart();

    /**
     * do view update here
     */
    public void onRefreshFinished();
  }

  private void initView(Context context) {
    if (!isInited) {
      isInited = true;
      mView = LayoutInflater.from(context).inflate(R.layout.view_pulltorefresh, null);
      mProgressBar = (ImageView) mView.findViewById(R.id.iv_pulltorefresh_arrow);
      mProgressBar.setImageResource(R.drawable.pulltorefresh);
      mPullTv = (TextView) mView.findViewById(R.id.tv_pulltorefresh);
      mPullV = mView.findViewById(R.id.ly_pulltorefresh_pull);
      mListView = (PullableListView) mView.findViewById(R.id.gv_smarturc_urcs);
      mListView.setPullListener(mPullListener);
      LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
          LayoutParams.MATCH_PARENT);
      addView(mView, lp);
      LayoutParams lParams = (LayoutParams) mPullV.getLayoutParams();
      mOrMargin = lParams.topMargin;
      mProAnimation = AnimationUtils.loadAnimation(getContext(),
          R.anim.anim_progressbar);
    }
  }

  private PullListener mPullListener = new PullListener() {

    @Override
    public boolean onPullDownStart() {
      if (isRefreshing || !isPullable) {
        return false;
      }
      mPullTv.setText("下拉刷新");
      mProgressBar.setRotation(0f);
      mProgressBar.setImageResource(R.drawable.pulltorefresh);
      if (mProgressBar.getAnimation() != null) {
        mProgressBar.clearAnimation();
      }
      return true;
    }

    @Override
    public void onPullDown(int moveY) {
      if (isRefreshing || !isPullable) {
        return;
      }
      moveY = (int) Math.max(0, moveY / SCALL_PULL_DOWW);
      mView.scrollTo(0, -moveY);
      mEmptyView.scrollTo(0, -moveY);
      if (!canRefresh
          && Math.abs(mView.getScrollY()) > Math.abs(mOrMargin)) {
        mPullTv.setText("松开刷新");
        canRefresh = true;
        if (mArrowRotateAnimator != null) {
          mArrowRotateAnimator.cancel();
        }
        float rotation = mProgressBar.getRotation();
        mArrowRotateAnimator = ObjectAnimator.ofFloat(mProgressBar, "rotation",
            rotation, 180f);
        mArrowRotateAnimator.setDuration(100).start();
      } else if (canRefresh
          && Math.abs(mView.getScrollY()) <= Math.abs(mOrMargin)) {
        mPullTv.setText("下拉刷新");
        canRefresh = false;
        if (mArrowRotateAnimator != null) {
          mArrowRotateAnimator.cancel();
        }
        float rotation = mProgressBar.getRotation();
        mArrowRotateAnimator = ObjectAnimator.ofFloat(mProgressBar, "rotation",
            rotation, 0f);
        mArrowRotateAnimator.setDuration(100).start();
      }
    }

    @Override
    public void onPullDownDrop() {
      if (canRefresh) {
        setRefreshing();
      } else {
        isRefreshing = false;
        backTo(mView.getScrollY(), 0);
      }
    }
  };

  private void backTo(final int from, final int to) {
    ObjectAnimator.ofInt(mView, "scrollY", from, to).setDuration(300)
        .start();
    ObjectAnimator.ofInt(mEmptyView, "scrollY", from, to).setDuration(300)
        .start();
  }

  /**
   * 设置为正在刷新状态
   */
  public void setRefreshing() {
    isRefreshing = true;
    mProgressBar.setImageResource(R.drawable.loading);
    mProgressBar.startAnimation(mProAnimation);
    mPullTv.setText("正在刷新");
    backTo(mView.getScrollY(), mOrMargin);
    if (mPullToRefreshListener != null) {
      mPullToRefreshListener.onRefreshStart();
    }
  }

  /**
   * 刷新完成
   */
  public void setRrefreshFinish() {
    if (isRefreshing) {
      isRefreshing = false;
      backTo(mView.getScrollY(), 0);
    }
    if (mPullToRefreshListener != null) {
      mPullToRefreshListener.onRefreshFinished();
    }
  }

  public void setPullable(boolean pullable) {
    isPullable = pullable;
  }

  public void setPullToRefreshListener(
      PullToRefreshListener mPullToRefreshListener) {
    this.mPullToRefreshListener = mPullToRefreshListener;
  }

  public void setAdapter(ListAdapter adapter) {
    mListView.setAdapter(adapter);
  }

  public void setEmptyView(View emptyView) {
    mListView.setEmptyView(emptyView);
    this.mEmptyView = emptyView;
  }

  public void setOnItemClickListener(OnItemClickListener itemClickListener) {
    mListView.setOnItemClickListener(itemClickListener);
  }

  public void setOnItemLongClickListener(OnItemLongClickListener itemLongClickListener) {
    mListView.setOnItemLongClickListener(itemLongClickListener);
  }
}

layout-view_pulltorefresh: 




  

    

    
  

  
  



anim-anim_progressbar: 




最后是DEMO ACTIVITY: 

public class PullToRefreshActivity extends Activity {

  private PullToRefreshView mPullToRefreshView;
  private List data = new ArrayList();
  private MyAdapter mAdapter;
  private Handler mHandler;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pulltorefresh);
    mHandler = new Handler();
    mPullToRefreshView = (PullToRefreshView) findViewById(R.id.pullToRefreshView1);
    mAdapter = new MyAdapter();
    mPullToRefreshView.setAdapter(mAdapter);
    mPullToRefreshView.setEmptyView(findViewById(R.id.empty));
    mPullToRefreshView.setOnItemLongClickListener(new OnItemLongClickListener() {
      @Override
      public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {
        Toast.makeText(getApplicationContext(), "Long click : " + data.get(position),
            Toast.LENGTH_SHORT).show();
        return true;
      }
    });
    mPullToRefreshView.setOnItemClickListener(new OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView parent, View view, int position, long id) {
        Toast.makeText(getApplicationContext(), data.get(position), Toast.LENGTH_SHORT)
            .show();
      }
    });
    mPullToRefreshView.setPullToRefreshListener(new PullToRefreshListener() {
      @Override
      public void onRefreshStart() {
        // 模拟刷新数据
        mHandler.postDelayed(new Runnable() {
          @Override
          public void run() {
            data.add(String.valueOf((int) (Math.random() * 1000)));
            mPullToRefreshView.setRrefreshFinish();
          }
        }, 2000);
      }

      @Override
      public void onRefreshFinished() {
        // 更新视图
        mAdapter.notifyDataSetChanged();
      }
    });
//    mHandler.postDelayed(new Runnable() {
//      @Override
//      public void run() {
//        // TODO Auto-generated method stub
//        mPullToRefreshView.setRefreshing();
//      }
//    }, 500);
  }

  public class MyAdapter extends BaseAdapter {

    @Override
    public int getCount() {
      // TODO Auto-generated method stub
      return data.size();
    }

    @Override
    public Object getItem(int position) {
      // TODO Auto-generated method stub
      return data.get(position);
    }

    @Override
    public long getItemId(int position) {
      // TODO Auto-generated method stub
      return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      // TODO Auto-generated method stub
      if (convertView == null) {
        convertView = new TextView(PullToRefreshActivity.this);
      }
      TextView textView = (TextView) convertView;
      textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40f);
      textView.setPadding(30, 30, 30, 30);
      textView.setText(data.get(position));
      return convertView;
    }

  }
}

layout-activity_pulltorefresh: 



  
  

  

    

    
  


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

你可能感兴趣的:(Android实现简单的下拉刷新pulltorefresh)