Android仿XListView支持下拉刷新和上划加载更多的自定义RecyclerView

首先给大家展示下效果图,感觉还不错,请继续往下阅读:

下拉刷新:

      

上划加载

      

在项目更新的过程中,遇到了一个将XListView换成recyclerView的需求,而且更换完之后大体效果不能变,但是对于下拉刷新这样的效果,谷歌给出的解决方案是把RecyclerView放在一个SwipeRefreshLayout中,但是这样其实是拉下一个小圆形控件实现的,和XListView的header效果不同。在网上找了很多的别人代码,都没有实现我想要的效果,于是自己动手写了一个。

具体实现的效果有以下几条

下拉刷新功能:

1、实现一个有弹性的拖出效果:思路参考XListView,recyclerView的position=0的位置放一个header布局,这个布局的margin top默认为负的布局高度,所以这块布局就一直处于屏幕外部,在下拉的时候通过onTouchListener根据手指的移动动态修改margin top,慢慢的拖出,当拖出的距离也就是margin top变为正数以后,就盖面header布局的状态,改变箭头的方向并改变提示语

2、实现有弹性的回弹效果:用timerTask写了一个动态修改的header布局的margin top的动画,每隔一定的时间减小margin top的值,当用户松手的时候通过一个函数updateHeaderHeight()来执行这个动画。

3、实现用户非手动拖拉的自动刷新效果:这个recyclerView还有一个方法叫forceRefresh(),就是不需要用户手动下拉,头部自己滚动出来,然后刷新完再自己收回去,自动下拉也是用一个timerTask每隔十几毫秒增加margin top的值让头部慢慢露出来

上划加载更多功能:

1、实现滚动到底部自动停住效果: 有时候recyclerVIew滚动太快,滚到底部的时候会根据惯性向上飘,这个地方到底的时候监控recyclerView滚动速度,如果非常快说明是惯性滚动,就不修改footer布局的高度

2、实现向上拖动效果:复写了recyclerView的onScrollListener,在手指向上滚动的时候,通过updateFooterHeight()方法动态修改底部footerView的margin bottom,同headerView一样,在手指移动的时候让这个margin跟着变大,以增加footer布局的高度,而且手指移动的越网上,增加的margin的高度就越小,实现一个有弹性的上拉效果,防止误操作。

3、实现自动回弹的效果:通过监控footer布局的margin bottom来确定松手的时候是否需要开始刷新,如果margin bottom大于某个值得时候就修改footer布局的状态从normal变成ready,在ready状态下松手就开始刷新操作,回弹也像header布局一样通过一个timerTask每隔十几毫秒修改margin的大小来实现回弹效果

注意事项:

1、为了实现头部和底部的代码分离,头部用的是onTouchListener,底部用的是onScrollListener

2、本recyclerVIew里面已经内置了一个layoutManager,所以不要给recyclerView再设置layoutManager,否则会出现头部不出来,下拉报空指针的情况,底部出现但是滑动没有效果

3、这个recyclerView内置了一个抽象类作为adapter,请继承这个内置的AlxDragRecyclerViewAdapter使用,或者按照这里面的逻辑重新写adapter

有其他的问题欢迎问我

4、一些常用的功能,比如设置该控件是否能够支持下拉加载和上拉刷新,等等api接口,请直接参考XListView的用法即可

使用方法:

继承AlxDragRecyclerViewAdapter写一个adapter,然后写两个类分别实现OnRefreshListener和LoadMoreListener,把具体的刷新逻辑写在里面,在准备好显示数据后调用adapter的notifyDataSetChanged()方法或notifyItemInserted()方法,并执行该recyclerView的stopLoadmore()方法和stopRefresh()方法。

下面是代码,这个控件有很多的内部类,包括头部,底部的布局控件(CustomDragHeaderView,CustomDragFooterView),adapter,layoutmanager都已经以内部类的方式集成在里面,减少迁移时候的复杂度

import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.xxx.app.R;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
/**
 * Created by Alex on 2016/1/27.
 */
public class AlxRefreshLoadMoreRecyclerView extends RecyclerView {
 private int footerHeight = -1;
 LinearLayoutManager layoutManager;
 // -- footer view
 private CustomDragRecyclerFooterView mFooterView;
 private boolean mEnablePullLoad;
 private boolean mPullLoading;
 private boolean isBottom;
 private boolean mIsFooterReady = false;
 private LoadMoreListener loadMoreListener;
 // -- header view
 private CustomDragHeaderView mHeaderView;
 private boolean mEnablePullRefresh = true;
 private boolean mIsRefreshing;
 private boolean isHeader;
 private boolean mIsHeaderReady = false;
 private Timer timer;
 private float oldY;
 Handler handler = new Handler();
 private OnRefreshListener refreshListener;
 private AlxDragRecyclerViewAdapter adapter;
 private int maxPullHeight = 50;//最多下拉高度的px值
 private static final int HEADER_HEIGHT = 68;//头部高度68dp
 private static final int MAX_PULL_LENGTH = 150;//最多下拉150dp
 private OnClickListener footerClickListener;
 public AlxRefreshLoadMoreRecyclerView(Context context) {
  super(context);
  initView(context);
 }
 public AlxRefreshLoadMoreRecyclerView(Context context, AttributeSet attrs) {
  super(context, attrs);
  initView(context);
 }
 public AlxRefreshLoadMoreRecyclerView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  initView(context);
 }
 public void setAdapter(AlxDragRecyclerViewAdapter adapter){
  super.setAdapter(adapter);
  this.adapter = adapter;
 }
 public boolean ismPullLoading() {
  return mPullLoading;
 }
 public boolean ismIsRefreshing() {
  return mIsRefreshing;
 }
 private void updateFooterHeight(float delta) {
  if(mFooterView==null)return;
  int bottomMargin = mFooterView.getBottomMargin();
//  Log.i("Alex3","初始delta是"+delta);
  if(delta>50)delta = delta/6;
  if(delta>0) {//越往下滑越难滑
   if(bottomMargin>maxPullHeight)delta = delta*0.65f;
   else if(bottomMargin>maxPullHeight * 0.83333f)delta = delta*0.7f;
   else if(bottomMargin>maxPullHeight * 0.66667f)delta = delta*0.75f;
   else if(bottomMargin>maxPullHeight >> 1)delta = delta*0.8f;
   else if(bottomMargin>maxPullHeight * 0.33333f)delta = delta*0.85f;
   else if(bottomMargin>maxPullHeight * 0.16667F && delta > 20)delta = delta*0.2f;//如果是因为惯性向下迅速的俯冲
   else if(bottomMargin>maxPullHeight * 0.16667F)delta = delta*0.9f;
//   Log.i("Alex3","bottomMargin是"+mFooterView.getBottomMargin()+" delta是"+delta);
  }
  int height = mFooterView.getBottomMargin() + (int) (delta+0.5);
  if (mEnablePullLoad && !mPullLoading) {
   if (height > 150){//必须拉超过一定距离才加载更多
//   if (height > 1){//立即刷新
    mFooterView.setState(CustomDragRecyclerFooterView.STATE_READY);
    mIsFooterReady = true;
//    Log.i("Alex2", "ready");
   } else {
    mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);
    mIsFooterReady = false;
//    Log.i("Alex2", "nomal");
   }
  }
  mFooterView.setBottomMargin(height);
 }
 private void resetFooterHeight() {
  int bottomMargin = mFooterView.getBottomMargin();
  if (bottomMargin > 20) {
   Log.i("Alex2", "准备重置高度,margin是" + bottomMargin + "自高是" + footerHeight);
   this.smoothScrollBy(0,-bottomMargin);
   //一松手就立即开始加载
   if(mIsFooterReady){
    startLoadMore();
   }
  }
 }
 public void setLoadMoreListener(LoadMoreListener listener){
  this.loadMoreListener = listener;
 }
 public void initView(Context context){
  layoutManager = new LinearLayoutManager(context);//自带layoutManager,请勿设置
  layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
  WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
  int height = wm.getDefaultDisplay().getHeight();
  layoutManager.offsetChildrenVertical(height*2);//预加载2/3的卡片
  this.setLayoutManager(layoutManager);
//  Log.i("Alex", "屏幕密度为" + getContext().getResources().getDisplayMetrics().density);
  maxPullHeight = dp2px(getContext().getResources().getDisplayMetrics().density,MAX_PULL_LENGTH);//最多下拉150dp
  this.footerClickListener = new footerViewClickListener();
  this.addOnScrollListener(new RecyclerView.OnScrollListener() {
   @Override
   public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    super.onScrollStateChanged(recyclerView, newState);
    switch (newState){
     case RecyclerView.SCROLL_STATE_IDLE:
//      Log.i("Alex2", "停下了||放手了");
      if(isBottom)resetFooterHeight();
      break;
     case RecyclerView.SCROLL_STATE_DRAGGING:
//      Log.i("Alex2", "开始拖了,现在margin是" + (mFooterView == null ? "" : mFooterView.getBottomMargin()));
      break;
     case RecyclerView.SCROLL_STATE_SETTLING:
//      Log.i("Alex2", "开始惯性移动");
      break;
    }
   }
   @Override
   public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    super.onScrolled(recyclerView, dx, dy);
    int lastItemPosition = layoutManager.findLastVisibleItemPosition();
//    Log.i("Alex2","mEnable是"+mEnablePullLoad+"lastitemPosition是"+lastItemPosition+" itemcount是"+layoutManager.getItemCount());
    if(lastItemPosition == layoutManager.getItemCount()-1 && mEnablePullLoad) {//如果到了最后一个
     isBottom = true;
     mFooterView = (CustomDragRecyclerFooterView)layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());//一开始还不能hide,因为hide得到最后一个可见的就不是footerview了
//     Log.i("Alex2","到底啦!!"+"mfooterView是"+mFooterView);
     if(mFooterView!=null) mFooterView.setOnClickListener(footerClickListener);
     if(footerHeight==-1 && mFooterView!=null){
      mFooterView.show();
      mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);
      footerHeight = mFooterView.getMeasuredHeight();//这里的测量一般不会出问题
//      Log.i("Alex2", "底部高度为" + footerHeight);
     }
     updateFooterHeight(dy);
    }else if(lastItemPosition == layoutManager.getItemCount()-1 && mEnablePullLoad){//如果到了倒数第二个
     startLoadMore();//开始加载更多
    }
    else {
     isBottom = false;
    }
   }
  });
 }
 /**
  * 设置是否开启上拉加载更多的功能
  *
  * @param enable
  */
 public void setPullLoadEnable(boolean enable) {
  mPullLoading = false;
  mEnablePullLoad = enable;
  if(adapter!=null)adapter.setPullLoadMoreEnable(enable);//adapter和recyclerView要同时设置
  if(mFooterView==null)return;
  if (!mEnablePullLoad) {
//   this.smoothScrollBy(0,-footerHeight);
   mFooterView.hide();
   mFooterView.setOnClickListener(null);
   mFooterView.setBottomMargin(0);
   //make sure "pull up" don't show a line in bottom when listview with one page
  } else {
   mFooterView.show();
   mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);
   mFooterView.setVisibility(VISIBLE);
   //make sure "pull up" don't show a line in bottom when listview with one page
   // both "pull up" and "click" will invoke load more.
   mFooterView.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
     startLoadMore();
    }
   });
  }
 }
 /**
  * 停止loadmore
  */
 public void stopLoadMore() {
  if (mPullLoading == true) {
   mPullLoading = false;
   if(mFooterView==null)return;
   mFooterView.show();
   mFooterView.setState(CustomDragRecyclerFooterView.STATE_ERROR);
  }
 }
 private void startLoadMore() {
  if(mPullLoading)return;
  mPullLoading = true;
  if(mFooterView!=null)mFooterView.setState(CustomDragRecyclerFooterView.STATE_LOADING);
  Log.i("Alex2", "现在开始加载");
  mIsFooterReady = false;
  if (loadMoreListener != null) {
   loadMoreListener.onLoadMore();
  }
 }
 /**
  * 在刷新时要执行的方法
  */
 public interface LoadMoreListener{
  public void onLoadMore();
 }
 /**
  * 点击loadMore后要执行的事件
  */
 class footerViewClickListener implements OnClickListener {
  @Override
  public void onClick(View v) {
   startLoadMore();
  }
 }
 private void updateHeaderHeight(float delta) {
  mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);
//  Log.i("Alex2", "现在在头部!!!! header自高是" + mHeaderView.getHeight() + " margin 是" + mHeaderView.getTopMargin());//自高一般不会算错
//  Log.i("Alex2", "正在设置margin" + mHeaderView.getTopMargin() +"delta是"+delta);
  if(delta>0){//如果是往下拉
   int topMargin = mHeaderView.getTopMargin();
   if(topMargin>maxPullHeight * 0.33333f)delta = delta*0.5f;
   else if(topMargin>maxPullHeight * 0.16667F)delta = delta*0.55f;
   else if(topMargin>0)delta = delta*0.6f;
   else if(topMargin<0)delta = delta*0.6f;//如果没有被完全拖出来
   mHeaderView.setTopMargin(mHeaderView.getTopMargin() + (int)delta);
  } else{//如果是推回去
   if(!mIsRefreshing || mHeaderView.getTopMargin()>0) {//在刷新的时候不把margin设为负值以在惯性滑动的时候能滑回去
    this.scrollBy(0, (int) delta);//禁止既滚动,又同时减少触摸
//    Log.i("Alex2", "正在往回推" + delta);
    mHeaderView.setTopMargin(mHeaderView.getTopMargin() + (int) delta);
   }
  }
  if(mHeaderView.getTopMargin()>0 && !mIsRefreshing){
   mIsHeaderReady = true;
   mHeaderView.setState(CustomDragHeaderView.STATE_READY);
  }//设置为ready状态
  else if(!mIsRefreshing){
   mIsHeaderReady = false;
   mHeaderView.setState(CustomDragHeaderView.STATE_NORMAL);
  }//设置为普通状态并且缩回去
 }
 @Override
 public void smoothScrollToPosition(final int position) {
  super.smoothScrollToPosition(position);
  final Timer scrollTimer = new Timer();
  TimerTask timerTask = new TimerTask() {
   @Override
   public void run() {
    int bottomCardPosition = layoutManager.findLastVisibleItemPosition();
    if(bottomCardPosition<position+1){//如果要向下滚
     handler.post(new Runnable() {
      @Override
      public void run() {
       smoothScrollBy(0,50);
      }
     });
    }else if(bottomCardPosition>position){//如果要向上滚
     handler.post(new Runnable() {
      @Override
      public void run() {
       smoothScrollBy(0,-50);
      }
     });
    }else {
     if(scrollTimer!=null)scrollTimer.cancel();
    }
   }
  };
  scrollTimer.schedule(timerTask,0,20);
 }
 /**
  * 在用户非手动强制刷新的时候,通过一个动画把头部一点点冒出来
  */
 private void smoothShowHeader(){
  if(mHeaderView==null)return;
//  if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完毕的时候用户没有注视header
//   mHeaderView.setTopMargin(0);
//   return;
//  }
  if(timer!=null)timer.cancel();
  final TimerTask timerTask = new TimerTask() {
   @Override
   public void run() {
    if(mHeaderView==null){
     if(timer!=null)timer.cancel();
     return;
    }
//    Log.i("Alex2","topMargin是"+mHeaderView.getTopMargin()+" height是"+mHeaderView.getHeight());
    if(mHeaderView.getTopMargin()<0){
     handler.post(new Runnable() {
      @Override
      public void run() {
       if (mIsRefreshing) {//如果目前是ready状态或者正在刷新状态
        mHeaderView.setTopMargin(mHeaderView.getTopMargin() +2);
       }
      }
     });
    } else if(timer!=null){//如果已经完全缩回去了,但是动画还没有结束,就结束掉动画
     timer.cancel();
    }
   }
  };
  timer = new Timer();
  timer.scheduleAtFixedRate(timerTask,0,16);
 }
 /**
  * 在用户松手的时候让头部自动收缩回去
  */
 private void resetHeaderHeight() {
  if(mHeaderView==null)mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);
  if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完毕的时候用户没有注视header
   mHeaderView.setTopMargin(-mHeaderView.getRealHeight());
   return;
  }
  if(timer!=null)timer.cancel();
  final TimerTask timerTask = new TimerTask() {
   @Override
   public void run() {
    if(mHeaderView==null)return;
//    Log.i("Alex2","topMargin是"+mHeaderView.getTopMargin()+" height是"+mHeaderView.getHeight());
    if(mHeaderView.getTopMargin()>-mHeaderView.getRealHeight()){//如果header没有完全缩回去
     handler.post(new Runnable() {
      @Override
      public void run() {
       if (mIsHeaderReady || mIsRefreshing) {//如果目前是ready状态或者正在刷新状态
//        Log.i("Alex2", "现在是ready状态");
        int delta = mHeaderView.getTopMargin() / 9;
        if (delta < 5) delta = 5;
        if (mHeaderView.getTopMargin() > 0)
         mHeaderView.setTopMargin(mHeaderView.getTopMargin() - delta);
       } else {//如果是普通状态
//        Log.i("Alex2", "现在是普通状态");
        mHeaderView.setTopMargin(mHeaderView.getTopMargin() - 5);
       }
      }
     });
    } else if(timer!=null){//如果已经完全缩回去了,但是动画还没有结束,就结束掉动画
     timer.cancel();
     handler.post(new Runnable() {
      @Override
      public void run() {
       mHeaderView.setState(mHeaderView.STATE_FINISH);
      }
     });
    }
   }
  };
  timer = new Timer();
  timer.scheduleAtFixedRate(timerTask,0,10);
 }
 /**
  * 头部是通过onTouchEvent控制的
  * @param event
  * @return
  */
 @Override
 public boolean onTouchEvent(MotionEvent event) {
  switch (event.getAction()) {
   case MotionEvent.ACTION_MOVE:
    int delta = (int)(event.getY()-oldY);
    oldY = event.getY();
    if (layoutManager.findViewByPosition(0) instanceof CustomDragHeaderView) {
     isHeader = true;
     updateHeaderHeight(delta);//更新margin高度
    }else{
     isHeader = false;
     if(mHeaderView!=null && !mIsRefreshing)mHeaderView.setTopMargin(-mHeaderView.getRealHeight());
    }
    break;
//   case MotionEvent.ACTION_DOWN:
//    Log.i("Alex", "touch down");
//    oldY = event.getY();
//    if(timer!=null)timer.cancel();
//    break;
   case MotionEvent.ACTION_UP:
//    Log.i("Alex", "抬手啦!!!! touch up ");
    if(mIsHeaderReady && !mIsRefreshing)startRefresh();
    if(isHeader)resetHeaderHeight();//抬手之后恢复高度
    break;
   case MotionEvent.ACTION_CANCEL:
//    Log.i("Alex", "touch cancel");
    break;
  }
  return super.onTouchEvent(event);
 }
 /**
  * 因为设置了子元素的onclickListener之后,ontouch方法的down失效,所以要在分发前获取手指的位置
  * @param ev
  * @return
  */
 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
  // TODO Auto-generated method stub
  switch (ev.getAction()) {
   case MotionEvent.ACTION_DOWN:
//    Log.i("Alex", "touch down分发前");
    oldY = ev.getY();
    if (timer != null) timer.cancel();
    break;
  }
  return super.dispatchTouchEvent(ev);
 }
 public void setOnRefreshListener(OnRefreshListener listener){
  this.refreshListener = listener;
 }
 /**
  * 设置是否支持下啦刷新的功能
  *
  * @param enable
  */
 public void setPullRefreshEnable(boolean enable) {
  mIsRefreshing = false;
  mEnablePullRefresh = enable;
  if(mHeaderView==null)return;
  if (!mEnablePullRefresh) {
   mHeaderView.setOnClickListener(null);
  } else {
   mHeaderView.setState(CustomDragFooterView.STATE_NORMAL);
   mHeaderView.setVisibility(VISIBLE);
  }
 }
 /**
  * 停止下拉刷新,并且通过动画让头部自己缩回去
  */
 public void stopRefresh() {
  if (mIsRefreshing == true) {
   mIsRefreshing = false;
   mIsHeaderReady = false;
   if(mHeaderView==null)return;
   mHeaderView.setState(CustomDragFooterView.STATE_NORMAL);
   resetHeaderHeight();
  }
 }
 /**
  * 在用户没有用手控制的情况下,通过动画把头部露出来并且执行刷新
  */
 public void forceRefresh(){
  if(mHeaderView==null)mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);
  if(mHeaderView!=null)mHeaderView.setState(CustomDragHeaderView.STATE_REFRESHING);
  mIsRefreshing = true;
  Log.i("Alex2", "现在开始强制刷新");
  mIsHeaderReady = false;
  smoothShowHeader();
  if (refreshListener != null)refreshListener.onRefresh();
 }
 private void startRefresh() {
  mIsRefreshing = true;
  mHeaderView.setState(CustomDragHeaderView.STATE_REFRESHING);
  Log.i("Alex2", "现在开始加载");
  mIsHeaderReady = false;
  if (refreshListener != null) refreshListener.onRefresh();
 }
 public interface OnRefreshListener{
  public void onRefresh();
 }
 /**
  * 适用于本recycler的头部下拉刷新view
  */
 public static class CustomDragHeaderView extends LinearLayout {
  public final static int STATE_NORMAL = 0;
  public final static int STATE_READY = 1;
  public final static int STATE_REFRESHING = 2;
  public final static int STATE_FINISH = 3;
  public float screenDensity;
  private final int ROTATE_ANIM_DURATION = 180;
  private Context mContext;
  private View mContentView;
  private View mProgressBar;
  private ImageView mArrowImageView;
  private TextView mHintTextView;
  private Animation mRotateUpAnim;
  private Animation mRotateDownAnim;
  public CustomDragHeaderView(Context context) {
   super(context);
   initView(context);
  }
  public CustomDragHeaderView(Context context, AttributeSet attrs) {
   super(context, attrs);
   initView(context);
  }
  private int mState;
  public void setState(int state) {
   if (state == mState)
    return;
   if (state == STATE_REFRESHING) { // 显示进度
    mArrowImageView.clearAnimation();
    mArrowImageView.setVisibility(View.INVISIBLE);
    mProgressBar.setVisibility(View.VISIBLE);
   } else { // 显示箭头图片
    mArrowImageView.setVisibility(View.VISIBLE);
    mProgressBar.setVisibility(View.INVISIBLE);
   }
   switch (state) {
    case STATE_NORMAL:
     if (mState == STATE_READY) {
      mArrowImageView.startAnimation(mRotateDownAnim);
      mHintTextView.setText(R.string.xlistview_header_hint_normal);
     }
     else if (mState == STATE_REFRESHING) {//如果是从刷新状态过来
//      mArrowImageView.clearAnimation();
      mArrowImageView.setVisibility(INVISIBLE);
      mHintTextView.setText("load completed");
     }
     break;
    case STATE_READY:
     if (mState != STATE_READY) {
      mArrowImageView.clearAnimation();
      mArrowImageView.startAnimation(mRotateUpAnim);
     }
      mHintTextView.setText(R.string.xlistview_header_hint_ready);
     break;
    case STATE_REFRESHING:
      mHintTextView.setText(R.string.xlistview_header_hint_loading);
     break;
    case STATE_FINISH:
     mArrowImageView.setVisibility(View.VISIBLE);
     mHintTextView.setText(R.string.xlistview_header_hint_normal);
     break;
    default:
   }
   mState = state;
  }
  public void setTopMargin(int height) {
   if (mContentView==null) return ;
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   lp.topMargin = height;
   mContentView.setLayoutParams(lp);
  }
  //
  public int getTopMargin() {
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   return lp.topMargin;
  }
  public void setHeight(int height){
   if (mContentView==null) return ;
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   lp.height = height;
   mContentView.setLayoutParams(lp);
  }
  private int realHeight;
  /**
   * 得到这个headerView真实的高度,而且这个高度是自己定的
   * @return
   */
  public int getRealHeight(){
   return realHeight;
  }
  private void initView(Context context) {
   mContext = context;
   this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));//recyclerView里不加这句话的话宽度就会比较窄
   LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.xlistview_header, null);
   addView(moreView);
   moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
   mContentView = moreView.findViewById(R.id.xlistview_header_content);
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   Log.i("Alex", "初始height是" + mContentView.getHeight());
   lp.height = 150;//手动设置高度,如果要手动加载更多的时候才设置
   screenDensity = getContext().getResources().getDisplayMetrics().density;//设置屏幕密度,用来px向dp转化
   lp.height = dp2px(screenDensity,HEADER_HEIGHT);//头部高度75dp
   realHeight = lp.height;
   lp.topMargin = -lp.height;
   mContentView.setLayoutParams(lp);
   mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);
   mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);
   mHintTextView.setPadding(0,dp2px(screenDensity,3),0,0);//不知道为什么这个文字总会向上偏一下,所以要补回来
   mProgressBar = findViewById(R.id.xlistview_header_progressbar);
   mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
   mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);
   mRotateUpAnim.setFillAfter(true);
   mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
   mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);
   mRotateDownAnim.setFillAfter(true);
  }
 }
 public static int dp2px(float density, int dp) {
  if (dp == 0) {
   return 0;
  }
  return (int) (dp * density + 0.5f);
 }
 public static class CustomDragRecyclerFooterView extends LinearLayout {
  public final static int STATE_NORMAL = 0;
  public final static int STATE_READY = 1;
  public final static int STATE_LOADING = 2;
  public final static int STATE_ERROR = 3;
  private Context mContext;
  private View mContentView;
  private View mProgressBar;
  private TextView mHintView;
  public CustomDragRecyclerFooterView(Context context) {
   super(context);
   initView(context);
  }
  public CustomDragRecyclerFooterView(Context context, AttributeSet attrs) {
   super(context, attrs);
   initView(context);
  }
  public void setState(int state) {
   mProgressBar.setVisibility(View.INVISIBLE);
//   mHintView.setVisibility(View.INVISIBLE);
   if (state == STATE_READY) {
    mHintView.setVisibility(View.VISIBLE);
    mHintView.setText("松手加载更多");
   } else if (state == STATE_LOADING) {
    mProgressBar.setVisibility(View.VISIBLE);
    mHintView.setVisibility(INVISIBLE);
   } else if(state == STATE_ERROR){
    mProgressBar.setVisibility(GONE);
    mHintView.setVisibility(VISIBLE);
    mHintView.setText(R.string.xlistview_footer_hint_ready);
   }
   else {
    mHintView.setVisibility(View.VISIBLE);
    mHintView.setText(R.string.xlistview_footer_hint_normal);
    mHintView.setText("Load more");
   }
  }
  public void setBottomMargin(int height) {
   if (height < 0) return ;
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   lp.bottomMargin = height;
   mContentView.setLayoutParams(lp);
  }
  public int getBottomMargin() {
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   return lp.bottomMargin;
  }
  /**
   * normal status
   */
  public void normal() {
   mHintView.setVisibility(View.VISIBLE);
   mProgressBar.setVisibility(View.GONE);
  }
  /**
   * loading status
   */
  public void loading() {
   mHintView.setVisibility(View.GONE);
   mProgressBar.setVisibility(View.VISIBLE);
  }
  /**
   * hide footer when disable pull load more
   */
  public void hide() {
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   lp.height = 1;//这里如果设为0那么layoutManger就会抓不到
   mContentView.setLayoutParams(lp);
   mContentView.setBackgroundColor(Color.BLACK);//这里的颜色要和自己的背景色一致
  }
  /**
   * show footer
   */
  public void show() {
   LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();
   lp.height = LayoutParams.WRAP_CONTENT;
   lp.width = LayoutParams.MATCH_PARENT;
   mContentView.setLayoutParams(lp);
   mContentView.setBackgroundColor(Color.WHITE);//这里的颜色要和自己的背景色一致
  }
  private void initView(Context context) {
   mContext = context;
   this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
   LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_customdragfooterview, null);
   addView(moreView);
   moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
   mContentView = moreView.findViewById(R.id.rlContentView);
   mProgressBar = moreView.findViewById(R.id.pbContentView);
   mHintView = (TextView)moreView.findViewById(R.id.ctvContentView);
   mHintView.setText("load more");
//   mProgressBar.setVisibility(VISIBLE);//一直会显示转圈,自动加载更多时使用
  }
 }
 /**
  * 为了防止代码上的混乱,使用这个recyclerView自己内置的一个adapter
  * @param <T>
  */
 public static abstract class AlxDragRecyclerViewAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
  protected static final int TYPE_HEADER = 436874;
  protected static final int TYPE_ITEM = 256478;
  protected static final int TYPE_FOOTER = 9621147;
  private int ITEM;
  private ViewHolder vhItem;
  protected boolean loadMore;
  private List<T> dataList;
  public List<T> getDataList() {
   return dataList;
  }
  public void setDataList(List<T> dataList) {
   this.dataList = dataList;
  }
  public AlxDragRecyclerViewAdapter(List<T> dataList,int itemLayout,boolean pullEnable){
   this.dataList = dataList;
   this.ITEM = itemLayout;
   this.loadMore = pullEnable;
  }
  public abstract ViewHolder setItemViewHolder(View itemView);
  private T getObject(int position){
   if(dataList!=null && dataList.size()>=position)return dataList.get(position-1);//如果有header
   return null;
  }
  @Override
  public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   if (viewType == TYPE_ITEM) {
    //inflate your layout and pass it to view holder
    View itemView = LayoutInflater.from(parent.getContext()).inflate(ITEM,null);
    Log.i("Alex","itemView是"+itemView);
    this.vhItem = setItemViewHolder(itemView);
    Log.i("Alex","vhItem是"+vhItem);
    return vhItem;
   } else if (viewType == TYPE_HEADER) {
    //inflate your layout and pass it to view holder
    View headerView = new CustomDragHeaderView(parent.getContext());
    return new VHHeader(headerView);
   } else if(viewType==TYPE_FOOTER){
    CustomDragRecyclerFooterView footerView = new CustomDragRecyclerFooterView(parent.getContext());
    return new VHFooter(footerView);
   }
   throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
  }
  public void setPullLoadMoreEnable(boolean enable){
   this.loadMore = enable;
  }
  public boolean getPullLoadMoreEnable(){return loadMore;}
  @Override
  public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {//相当于getView
   Log.i("Alex","正在绑定"+position+" "+holder.getClass());
   if (vhItem!=null && holder.getClass() == vhItem.getClass()) {
    //cast holder to VHItem and set data
    initItemView(holder,position,getObject(position));
   }else if (holder instanceof AlxDragRecyclerViewAdapter.VHHeader) {
    //cast holder to VHHeader and set data for header.
   }else if(holder instanceof AlxDragRecyclerViewAdapter.VHFooter){
    if(!loadMore)((VHFooter)holder).footerView.hide();//第一次初始化显示的时候要不要显示footerView
   }
  }
  @Override
  public int getItemCount() {
   return (dataList==null ||dataList.size()==0)?1:dataList.size() + 2;//如果有header,若list不存在或大小为0就没有footView,反之则有
  }//这里要考虑到头尾部,多以要加2
  /**
   * 根据位置判断这里该用哪个ViewHolder
   * @param position
   * @return
   */
  @Override
  public int getItemViewType(int position) {
   if (position == 0)return TYPE_HEADER;
   else if(isPositonFooter(position))return TYPE_FOOTER;
   return TYPE_ITEM;
  }
  protected boolean isPositonFooter(int position){//这里的position从0算起
    if (dataList == null && position == 1) return true;//如果没有item
    return position == dataList.size() + 1;//如果有item(也许为0)
  }
//  class VHItem extends RecyclerView.ViewHolder {
//   public VHItem(View itemView) {
//    super(itemView);
//   }
//   public View getItemView(){return itemView;}
//  }
//
  protected class VHHeader extends RecyclerView.ViewHolder {
   public VHHeader(View headerView) {
    super(headerView);
   }
  }
  protected class VHFooter extends RecyclerView.ViewHolder {
   public CustomDragRecyclerFooterView footerView;
   public VHFooter(View itemView) {
    super(itemView);
    footerView = (CustomDragRecyclerFooterView)itemView;
   }
  }
  public abstract void initItemView(ViewHolder itemHolder,int posion,T entity);
 }
}
(0)

相关推荐

  • Android ListView实现下拉加载功能

    本文实例为大家分享了ListView下拉加载展示的具体代码,供大家参考,具体内容如下 1.MyListView.Java public class MyListView extends ListView implements OnScrollListener { private final static int RELEASE_To_REFRESH = 0;// 下拉过程的状态值 private final static int PULL_To_REFRESH = 1; // 从下拉返回到不刷新

  • Android ListView实现上拉加载更多和下拉刷新功能

    本文实例为大家介绍了Android ListView下拉刷新功能的实现方法和功能,供大家参考,具体内容如下 1.ListView优化方式 界面缓存:ViewHolder+convertView 分页加载:上拉刷新 图片缓存 快速滑动ListView禁止刷新 2.效果 3.上拉加载更多原理及实现 当我们手指滑动到listview最后位置的时候,我们触发加载数据的方法.这触发之前我们需要做一些工作,包括: 如何判断滑动到最后? 如何避免重复加载数据? 加载之后如何刷新界面? 1).界面实现AbsLi

  • Android实现上拉加载更多以及下拉刷新功能(ListView)

    首先为大家介绍Andorid5.0原生下拉刷新简单实现. 先上效果图: 相对于上一个19.1.0版本中的横条效果好看了很多.使用起来也很简单. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container" and

  • Android ListView下拉刷新上拉自动加载更多DEMO示例

    代码下载地址已经更新.因为代码很久没更新,已经很落伍了,建议大家使用RecyclerView实现. 参考项目: https://github.com/bingoogolapple/BGARefreshLayout-Android https://github.com/baoyongzhang/android-PullRefreshLayout 下拉刷新,Android中非常普遍的功能.为了方便便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能.设计最初是参考开源中国的And

  • Android ListView实现上拉加载下拉刷新和滑动删除功能

    最近项目需要用到可以滑动删除并且带有上拉加载下拉刷新的Listview,查阅了一些资料,大多都是在SwipeMenuListView的基础上去添加头部和底部View,来扩展上拉加载和下拉刷新的功能,不过需要手动的去绘制UI及处理一些动画效果.用起来也不是特别方便.刚好项目中用到PulltorefreshLibrary库,就尝试着扩展了一个PullToRefreshSwipeMenuListView类来实现需求.先看一下效果: 实现步骤 一.组合Pulltorefresh与SwipeMenuLis

  • ListView实现下拉刷新加载更多的实例代码(直接拿来用)

    ListView Api bixu 好好看看 mNewsAdapter.notifyDataSetChanged();//刷新ListView 自定义的RefreashListView package com.itguang.dell_pc.myapplication.view; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import and

  • Android XListView下拉刷新和上拉加载更多

    市面上有好多的类比ListView刷新数据的开源框架,如:v4包自带的SwipeRefreshLayout ,以及集ListView.GridView甚至WebView于一身的Pulltorefresh等等.前述的两个开源框架目前使用也算频繁.有兴趣的读者可以自行搜索,当然有时间一定回来对所有的使用方式做一个汇总和比较.今天介绍的这款框架,专门针对ListView做下拉刷新与上拉加载的,如果单单是ListView就显得更加简单方便易于理解. 1.首先引入xListView_lib库到自己的Dem

  • Android开发ListView中下拉刷新上拉加载及带列的横向滚动实现方法

    ListView 控件可使用四种不同视图显示项目.通过此控件,可将项目组成带有或不带有列标头的列,并显示伴随的图标和文本. 可使用 ListView 控件将称作 ListItem 对象的列表条目组织成下列四种不同的视图之一:1.大(标准)图标2.小图标3.列表4.报表 View 属性决定在列表中控件使用何种视图显示项目. 还可用 LabelWrap 属性控制列表中与项目关联的标签是否可换行显示.另外,还可管理列表中项目的排序方法和选定项目的外观. 相信有很人做的项目估计都用的到这个.就是List

  • Android程序开发之Listview下拉刷新上拉(滑动分页)加载更多

    最近做的类似于微博的项目中,有个Android功能要使用到listview的向下拉刷新来刷新最新消息,向上拉刷新(滑动分页)来加载更多. 新浪微博就是使用这种方式的典型. 当用户从网络上读取微博的时候,如果一下子全部加载用户未读的微博这将耗费比较长的时间,造成不好的用户体验,同时一屏的内容也不足以显示如此多的内容.这时候,我们就需要用到另一个功能,那就是listview的分页了,其实这个分页可以做成客户端的分页,也可以做成服务器端的分页(点击加载时,从服务器对应的加载第N页就好了!!!).通过分

  • Android自定义listview布局实现上拉加载下拉刷新功能

    listview实现上拉加载以及下拉刷新的方式有很多.下面是我写的一种自定义的布局,复用性也比较的强.首先就是继承的listview的自定义view.   AutoListView.Java: package com.example.mic.testdemo.view; import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.os.Bu

随机推荐