首先看效果

下拉刷新:

      支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)_第1张图片


上划加载

支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)_第2张图片      支持下拉刷新和上划加载更多的自定义RecyclerView(仿XListView效果)_第3张图片

项目github地址:https://github.com/AlexZhuo/AlxRecyclerView

并且编译好的demo apk包也在github上


在项目更新的过程中,遇到了一个将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都已经以内部类的方式集成在里面,减少迁移时候的复杂度


[java] view plain copy 

  1. import android.content.Context;  

  2. import android.graphics.Color;  

  3. import android.os.Handler;  

  4. import android.support.v7.widget.LinearLayoutManager;  

  5. import android.support.v7.widget.RecyclerView;  

  6. import android.util.AttributeSet;  

  7. import android.util.Log;  

  8. import android.view.LayoutInflater;  

  9. import android.view.MotionEvent;  

  10. import android.view.View;  

  11. import android.view.ViewGroup;  

  12. import android.view.WindowManager;  

  13. import android.view.animation.Animation;  

  14. import android.view.animation.RotateAnimation;  

  15. import android.widget.ImageView;  

  16. import android.widget.LinearLayout;  

  17. import android.widget.TextView;  

  18.   

  19. import com.xxx.app.R;  

  20.   

  21. import java.util.List;  

  22. import java.util.Timer;  

  23. import java.util.TimerTask;  

  24.   

  25. /** 

  26.  * Created by Alex on 2016/1/27. 

  27.  */  

  28. public class AlxRefreshLoadMoreRecyclerView extends RecyclerView {  

  29.     private int footerHeight = -1;  

  30.     LinearLayoutManager layoutManager;  

  31.     // -- footer view  

  32.     private CustomDragRecyclerFooterView mFooterView;  

  33.     private boolean mEnablePullLoad;  

  34.     private boolean mPullLoading;  

  35.     private boolean isBottom;  

  36.     private boolean mIsFooterReady = false;  

  37.     private LoadMoreListener loadMoreListener;  

  38.   

  39.     // -- header view  

  40.     private CustomDragHeaderView mHeaderView;  

  41.     private boolean mEnablePullRefresh = true;  

  42.     private boolean mIsRefreshing;  

  43.     private boolean isHeader;  

  44.     private boolean mIsHeaderReady = false;  

  45.     private Timer timer;  

  46.     private float oldY;  

  47.     Handler handler = new Handler();  

  48.     private OnRefreshListener refreshListener;  

  49.     private AlxDragRecyclerViewAdapter adapter;  

  50.     private int maxPullHeight = 50;//最多下拉高度的px值  

  51.   

  52.     private static final int HEADER_HEIGHT = 68;//头部高度68dp  

  53.     private static final int MAX_PULL_LENGTH = 150;//最多下拉150dp  

  54.     private OnClickListener footerClickListener;  

  55.   

  56.   

  57.     public AlxRefreshLoadMoreRecyclerView(Context context) {  

  58.         super(context);  

  59.         initView(context);  

  60.     }  

  61.   

  62.     public AlxRefreshLoadMoreRecyclerView(Context context, AttributeSet attrs) {  

  63.         super(context, attrs);  

  64.         initView(context);  

  65.     }  

  66.   

  67.     public AlxRefreshLoadMoreRecyclerView(Context context, AttributeSet attrs, int defStyle) {  

  68.         super(context, attrs, defStyle);  

  69.         initView(context);  

  70.     }  

  71.   

  72.     public void setAdapter(AlxDragRecyclerViewAdapter adapter){  

  73.         super.setAdapter(adapter);  

  74.         this.adapter = adapter;  

  75.     }  

  76.   

  77.     public boolean ismPullLoading() {  

  78.         return mPullLoading;  

  79.     }  

  80.   

  81.     public boolean ismIsRefreshing() {  

  82.         return mIsRefreshing;  

  83.     }  

  84.   

  85.     private void updateFooterHeight(float delta) {  

  86.         if(mFooterView==null)return;  

  87.         int bottomMargin = mFooterView.getBottomMargin();  

  88. //        Log.i("Alex3","初始delta是"+delta);  

  89.         if(delta>50)delta = delta/6;  

  90.         if(delta>0) {//越往下滑越难滑  

  91.             if(bottomMargin>maxPullHeight)delta = delta*0.65f;  

  92.             else if(bottomMargin>maxPullHeight * 0.83333f)delta = delta*0.7f;  

  93.             else if(bottomMargin>maxPullHeight * 0.66667f)delta = delta*0.75f;  

  94.             else if(bottomMargin>maxPullHeight >> 1)delta = delta*0.8f;  

  95.             else if(bottomMargin>maxPullHeight * 0.33333f)delta = delta*0.85f;  

  96.             else if(bottomMargin>maxPullHeight * 0.16667F && delta > 20)delta = delta*0.2f;//如果是因为惯性向下迅速的俯冲  

  97.             else if(bottomMargin>maxPullHeight * 0.16667F)delta = delta*0.9f;  

  98. //            Log.i("Alex3","bottomMargin是"+mFooterView.getBottomMargin()+" delta是"+delta);  

  99.         }  

  100.   

  101.         int height = mFooterView.getBottomMargin() + (int) (delta+0.5);  

  102.   

  103.         if (mEnablePullLoad && !mPullLoading) {  

  104.             if (height > 150){//必须拉超过一定距离才加载更多  

  105. //            if (height > 1){//立即刷新  

  106.                 mFooterView.setState(CustomDragRecyclerFooterView.STATE_READY);  

  107.                 mIsFooterReady = true;  

  108. //                Log.i("Alex2", "ready");  

  109.             } else {  

  110.                 mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);  

  111.                 mIsFooterReady = false;  

  112. //                Log.i("Alex2", "nomal");  

  113.             }  

  114.         }  

  115.         mFooterView.setBottomMargin(height);  

  116.   

  117.   

  118.     }  

  119.   

  120.     private void resetFooterHeight() {  

  121.         int bottomMargin = mFooterView.getBottomMargin();  

  122.         if (bottomMargin > 20) {  

  123.             Log.i("Alex2", "准备重置高度,margin是" + bottomMargin + "自高是" + footerHeight);  

  124.             this.smoothScrollBy(0,-bottomMargin);  

  125.             //一松手就立即开始加载  

  126.             if(mIsFooterReady){  

  127.                 startLoadMore();  

  128.             }  

  129.         }  

  130.     }  

  131.   

  132.   

  133.     public void setLoadMoreListener(LoadMoreListener listener){  

  134.         this.loadMoreListener = listener;  

  135.     }  

  136.   

  137.     public void initView(Context context){  

  138.         layoutManager = new LinearLayoutManager(context);//自带layoutManager,请勿设置  

  139.         layoutManager.setOrientation(LinearLayoutManager.VERTICAL);  

  140.         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);  

  141.         int height = wm.getDefaultDisplay().getHeight();  

  142.         layoutManager.offsetChildrenVertical(height*2);//预加载2/3的卡片  

  143.         this.setLayoutManager(layoutManager);  

  144. //        Log.i("Alex", "屏幕密度为" + getContext().getResources().getDisplayMetrics().density);  

  145.         maxPullHeight = dp2px(getContext().getResources().getDisplayMetrics().density,MAX_PULL_LENGTH);//最多下拉150dp  

  146.         this.footerClickListener = new footerViewClickListener();  

  147.         this.addOnScrollListener(new RecyclerView.OnScrollListener() {  

  148.   

  149.             @Override  

  150.             public void onScrollStateChanged(RecyclerView recyclerView, int newState) {  

  151.                 super.onScrollStateChanged(recyclerView, newState);  

  152.                 switch (newState){  

  153.                     case RecyclerView.SCROLL_STATE_IDLE:  

  154. //                        Log.i("Alex2", "停下了||放手了");  

  155.                         if(isBottom)resetFooterHeight();  

  156.                         break;  

  157.                     case RecyclerView.SCROLL_STATE_DRAGGING:  

  158. //                        Log.i("Alex2", "开始拖了,现在margin是" + (mFooterView == null ? "" : mFooterView.getBottomMargin()));  

  159.                         break;  

  160.                     case RecyclerView.SCROLL_STATE_SETTLING:  

  161. //                        Log.i("Alex2", "开始惯性移动");  

  162.                         break;  

  163.                 }  

  164.   

  165.             }  

  166.   

  167.             @Override  

  168.             public void onScrolled(RecyclerView recyclerView, int dx, int dy) {  

  169.                 super.onScrolled(recyclerView, dx, dy);  

  170.                 int lastItemPosition = layoutManager.findLastVisibleItemPosition();  

  171. //                Log.i("Alex2","mEnable是"+mEnablePullLoad+"lastitemPosition是"+lastItemPosition+" itemcount是"+layoutManager.getItemCount());  

  172.                 if(lastItemPosition == layoutManager.getItemCount()-1 && mEnablePullLoad) {//如果到了最后一个  

  173.                     isBottom = true;  

  174.                     mFooterView = (CustomDragRecyclerFooterView)layoutManager.findViewByPosition(layoutManager.findLastVisibleItemPosition());//一开始还不能hide,因为hide得到最后一个可见的就不是footerview了  

  175. //                    Log.i("Alex2","到底啦!!"+"mfooterView是"+mFooterView);  

  176.                     if(mFooterView!=null) mFooterView.setOnClickListener(footerClickListener);  

  177.                     if(footerHeight==-1 && mFooterView!=null){  

  178.                         mFooterView.show();  

  179.                         mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);  

  180.                         footerHeight = mFooterView.getMeasuredHeight();//这里的测量一般不会出问题  

  181. //                        Log.i("Alex2", "底部高度为" + footerHeight);  

  182.                     }  

  183.                     updateFooterHeight(dy);  

  184.                 }else if(lastItemPosition == layoutManager.getItemCount()-1 && mEnablePullLoad){//如果到了倒数第二个  

  185.                     startLoadMore();//开始加载更多  

  186.                 }  

  187.                 else {  

  188.                     isBottom = false;  

  189.                 }  

  190.             }  

  191.         });  

  192.     }  

  193.   

  194.     /** 

  195.      * 设置是否开启上拉加载更多的功能 

  196.      * 

  197.      * @param enable 

  198.      */  

  199.     public void setPullLoadEnable(boolean enable) {  

  200.         mPullLoading = false;  

  201.         mEnablePullLoad = enable;  

  202.         if(adapter!=null)adapter.setPullLoadMoreEnable(enable);//adapter和recyclerView要同时设置  

  203.         if(mFooterView==null)return;  

  204.         if (!mEnablePullLoad) {  

  205. //            this.smoothScrollBy(0,-footerHeight);  

  206.             mFooterView.hide();  

  207.             mFooterView.setOnClickListener(null);  

  208.             mFooterView.setBottomMargin(0);  

  209.             //make sure "pull up" don't show a line in bottom when listview with one page  

  210.         } else {  

  211.             mFooterView.show();  

  212.             mFooterView.setState(CustomDragRecyclerFooterView.STATE_NORMAL);  

  213.             mFooterView.setVisibility(VISIBLE);  

  214.             //make sure "pull up" don't show a line in bottom when listview with one page  

  215.             // both "pull up" and "click" will invoke load more.  

  216.             mFooterView.setOnClickListener(new OnClickListener() {  

  217.                 @Override  

  218.                 public void onClick(View v) {  

  219.                     startLoadMore();  

  220.                 }  

  221.             });  

  222.         }  

  223.     }  

  224.   

  225.     /** 

  226.      * 停止loadmore 

  227.      */  

  228.     public void stopLoadMore() {  

  229.         if (mPullLoading == true) {  

  230.             mPullLoading = false;  

  231.             if(mFooterView==null)return;  

  232.             mFooterView.show();  

  233.             mFooterView.setState(CustomDragRecyclerFooterView.STATE_ERROR);  

  234.         }  

  235.     }  

  236.   

  237.     private void startLoadMore() {  

  238.         if(mPullLoading)return;  

  239.         mPullLoading = true;  

  240.         if(mFooterView!=null)mFooterView.setState(CustomDragRecyclerFooterView.STATE_LOADING);  

  241.         Log.i("Alex2", "现在开始加载");  

  242.         mIsFooterReady = false;  

  243.         if (loadMoreListener != null) {  

  244.             loadMoreListener.onLoadMore();  

  245.         }  

  246.     }  

  247.   

  248.     /** 

  249.      * 在刷新时要执行的方法 

  250.      */  

  251.     public interface LoadMoreListener{  

  252.         public void onLoadMore();  

  253.     }  

  254.   

  255.     /** 

  256.      * 点击loadMore后要执行的事件 

  257.      */  

  258.     class footerViewClickListener implements OnClickListener {  

  259.   

  260.         @Override  

  261.         public void onClick(View v) {  

  262.             startLoadMore();  

  263.         }  

  264.     }  

  265.   

  266.   

  267.     private void updateHeaderHeight(float delta) {  

  268.         mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);  

  269. //        Log.i("Alex2", "现在在头部!!!! header自高是" + mHeaderView.getHeight() + "   margin 是" + mHeaderView.getTopMargin());//自高一般不会算错  

  270. //        Log.i("Alex2", "正在设置margin" + mHeaderView.getTopMargin() +"delta是"+delta);  

  271.         if(delta>0){//如果是往下拉  

  272.             int topMargin = mHeaderView.getTopMargin();  

  273.             if(topMargin>maxPullHeight * 0.33333f)delta = delta*0.5f;  

  274.             else if(topMargin>maxPullHeight * 0.16667F)delta = delta*0.55f;  

  275.             else if(topMargin>0)delta = delta*0.6f;  

  276.             else if(topMargin<0)delta = delta*0.6f;//如果没有被完全拖出来  

  277.             mHeaderView.setTopMargin(mHeaderView.getTopMargin() + (int)delta);  

  278.         } else{//如果是推回去  

  279.             if(!mIsRefreshing || mHeaderView.getTopMargin()>0) {//在刷新的时候不把margin设为负值以在惯性滑动的时候能滑回去  

  280.                 this.scrollBy(0, (int) delta);//禁止既滚动,又同时减少触摸  

  281. //                Log.i("Alex2", "正在往回推" + delta);  

  282.                 mHeaderView.setTopMargin(mHeaderView.getTopMargin() + (int) delta);  

  283.             }  

  284.         }  

  285.         if(mHeaderView.getTopMargin()>0 && !mIsRefreshing){  

  286.             mIsHeaderReady = true;  

  287.             mHeaderView.setState(CustomDragHeaderView.STATE_READY);  

  288.         }//设置为ready状态  

  289.         else if(!mIsRefreshing){  

  290.             mIsHeaderReady = false;  

  291.             mHeaderView.setState(CustomDragHeaderView.STATE_NORMAL);  

  292.         }//设置为普通状态并且缩回去  

  293.     }  

  294.   

  295.     @Override  

  296.     public void smoothScrollToPosition(final int position) {  

  297.         super.smoothScrollToPosition(position);  

  298.         final Timer scrollTimer = new Timer();  

  299.         TimerTask timerTask = new TimerTask() {  

  300.             @Override  

  301.             public void run() {  

  302.                 int bottomCardPosition = layoutManager.findLastVisibleItemPosition();  

  303.                 if(bottomCardPosition

  304.                     handler.post(new Runnable() {  

  305.                         @Override  

  306.                         public void run() {  

  307.                             smoothScrollBy(0,50);  

  308.                         }  

  309.                     });  

  310.                 }else if(bottomCardPosition>position){//如果要向上滚  

  311.                     handler.post(new Runnable() {  

  312.                         @Override  

  313.                         public void run() {  

  314.                             smoothScrollBy(0,-50);  

  315.                         }  

  316.                     });  

  317.                 }else {  

  318.                     if(scrollTimer!=null)scrollTimer.cancel();  

  319.                 }  

  320.             }  

  321.         };  

  322.         scrollTimer.schedule(timerTask,0,20);  

  323.   

  324.     }  

  325.   

  326.     /** 

  327.      * 在用户非手动强制刷新的时候,通过一个动画把头部一点点冒出来 

  328.      */  

  329.     private void smoothShowHeader(){  

  330.         if(mHeaderView==null)return;  

  331. //        if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完毕的时候用户没有注视header  

  332. //            mHeaderView.setTopMargin(0);  

  333. //            return;  

  334. //        }  

  335.         if(timer!=null)timer.cancel();  

  336.         final TimerTask timerTask = new TimerTask() {  

  337.             @Override  

  338.             public void run() {  

  339.                 if(mHeaderView==null){  

  340.                     if(timer!=null)timer.cancel();  

  341.                     return;  

  342.                 }  

  343. //                Log.i("Alex2","topMargin是"+mHeaderView.getTopMargin()+" height是"+mHeaderView.getHeight());  

  344.                 if(mHeaderView.getTopMargin()<0){  

  345.                     handler.post(new Runnable() {  

  346.                         @Override  

  347.                         public void run() {  

  348.                             if (mIsRefreshing) {//如果目前是ready状态或者正在刷新状态  

  349.                                 mHeaderView.setTopMargin(mHeaderView.getTopMargin() +2);  

  350.                             }  

  351.                         }  

  352.                     });  

  353.                 } else if(timer!=null){//如果已经完全缩回去了,但是动画还没有结束,就结束掉动画  

  354.                     timer.cancel();  

  355.                 }  

  356.             }  

  357.         };  

  358.         timer = new Timer();  

  359.         timer.scheduleAtFixedRate(timerTask,0,16);  

  360.     }  

  361.   

  362.     /** 

  363.      * 在用户松手的时候让头部自动收缩回去 

  364.      */  

  365.     private void resetHeaderHeight() {  

  366.         if(mHeaderView==null)mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);  

  367.         if(layoutManager.findFirstVisibleItemPosition()!=0){//如果刷新完毕的时候用户没有注视header  

  368.             mHeaderView.setTopMargin(-mHeaderView.getRealHeight());  

  369.             return;  

  370.         }  

  371.         if(timer!=null)timer.cancel();  

  372.         final TimerTask timerTask = new TimerTask() {  

  373.             @Override  

  374.             public void run() {  

  375.                 if(mHeaderView==null)return;  

  376. //                Log.i("Alex2","topMargin是"+mHeaderView.getTopMargin()+" height是"+mHeaderView.getHeight());  

  377.                 if(mHeaderView.getTopMargin()>-mHeaderView.getRealHeight()){//如果header没有完全缩回去  

  378.                     handler.post(new Runnable() {  

  379.                         @Override  

  380.                         public void run() {  

  381.                             if (mIsHeaderReady || mIsRefreshing) {//如果目前是ready状态或者正在刷新状态  

  382. //                                Log.i("Alex2", "现在是ready状态");  

  383.                                 int delta = mHeaderView.getTopMargin() / 9;  

  384.                                 if (delta < 5) delta = 5;  

  385.                                 if (mHeaderView.getTopMargin() > 0)  

  386.                                     mHeaderView.setTopMargin(mHeaderView.getTopMargin() - delta);  

  387.                             } else {//如果是普通状态  

  388. //                                Log.i("Alex2", "现在是普通状态");  

  389.                                 mHeaderView.setTopMargin(mHeaderView.getTopMargin() - 5);  

  390.                             }  

  391.                         }  

  392.                     });  

  393.                 } else if(timer!=null){//如果已经完全缩回去了,但是动画还没有结束,就结束掉动画  

  394.                     timer.cancel();  

  395.                     handler.post(new Runnable() {  

  396.                         @Override  

  397.                         public void run() {  

  398.                             mHeaderView.setState(mHeaderView.STATE_FINISH);  

  399.                         }  

  400.                     });  

  401.                 }  

  402.             }  

  403.         };  

  404.         timer = new Timer();  

  405.         timer.scheduleAtFixedRate(timerTask,0,10);  

  406.     }  

  407.   

  408.   

  409.     /** 

  410.      * 头部是通过onTouchEvent控制的 

  411.      * @param event 

  412.      * @return 

  413.      */  

  414.     @Override  

  415.     public boolean onTouchEvent(MotionEvent event) {  

  416.   

  417.         switch (event.getAction()) {  

  418.             case MotionEvent.ACTION_MOVE:  

  419.                 int delta = (int)(event.getY()-oldY);  

  420.                 oldY = event.getY();  

  421.                 if (layoutManager.findViewByPosition(0) instanceof CustomDragHeaderView) {  

  422.                     isHeader = true;  

  423.                     updateHeaderHeight(delta);//更新margin高度  

  424.                 }else{  

  425.                     isHeader = false;  

  426.                     if(mHeaderView!=null && !mIsRefreshing)mHeaderView.setTopMargin(-mHeaderView.getRealHeight());  

  427.                 }  

  428.                 break;  

  429. //            case MotionEvent.ACTION_DOWN:  

  430. //                Log.i("Alex", "touch down");  

  431. //                oldY = event.getY();  

  432. //                if(timer!=null)timer.cancel();  

  433. //                break;  

  434.             case MotionEvent.ACTION_UP:  

  435. //                Log.i("Alex", "抬手啦!!!! touch up ");  

  436.                 if(mIsHeaderReady && !mIsRefreshing)startRefresh();  

  437.                 if(isHeader)resetHeaderHeight();//抬手之后恢复高度  

  438.                 break;  

  439.             case MotionEvent.ACTION_CANCEL:  

  440. //                Log.i("Alex", "touch cancel");  

  441.                 break;  

  442.   

  443.         }  

  444.         return super.onTouchEvent(event);  

  445.     }  

  446.   

  447.     /** 

  448.      * 因为设置了子元素的onclickListener之后,ontouch方法的down失效,所以要在分发前获取手指的位置 

  449.      * @param ev 

  450.      * @return 

  451.      */  

  452.     @Override  

  453.     public boolean dispatchTouchEvent(MotionEvent ev) {  

  454.         // TODO Auto-generated method stub  

  455.         switch (ev.getAction()) {  

  456.             case MotionEvent.ACTION_DOWN:  

  457. //                Log.i("Alex", "touch down分发前");  

  458.                 oldY = ev.getY();  

  459.                 if (timer != null) timer.cancel();  

  460.                 break;  

  461.         }  

  462.         return super.dispatchTouchEvent(ev);  

  463.     }  

  464.   

  465.     public void setOnRefreshListener(OnRefreshListener listener){  

  466.         this.refreshListener = listener;  

  467.     }  

  468.   

  469.     /** 

  470.      * 设置是否支持下啦刷新的功能 

  471.      * 

  472.      * @param enable 

  473.      */  

  474.     public void setPullRefreshEnable(boolean enable) {  

  475.         mIsRefreshing = false;  

  476.         mEnablePullRefresh = enable;  

  477.         if(mHeaderView==null)return;  

  478.         if (!mEnablePullRefresh) {  

  479.             mHeaderView.setOnClickListener(null);  

  480.         } else {  

  481.             mHeaderView.setState(CustomDragFooterView.STATE_NORMAL);  

  482.             mHeaderView.setVisibility(VISIBLE);  

  483.         }  

  484.     }  

  485.   

  486.     /** 

  487.      * 停止下拉刷新,并且通过动画让头部自己缩回去 

  488.      */  

  489.     public void stopRefresh() {  

  490.         if (mIsRefreshing == true) {  

  491.             mIsRefreshing = false;  

  492.             mIsHeaderReady = false;  

  493.             if(mHeaderView==null)return;  

  494.             mHeaderView.setState(CustomDragFooterView.STATE_NORMAL);  

  495.             resetHeaderHeight();  

  496.         }  

  497.     }  

  498.   

  499.     /** 

  500.      * 在用户没有用手控制的情况下,通过动画把头部露出来并且执行刷新 

  501.      */  

  502.     public void forceRefresh(){  

  503.         if(mHeaderView==null)mHeaderView = (CustomDragHeaderView) layoutManager.findViewByPosition(0);  

  504.         if(mHeaderView!=null)mHeaderView.setState(CustomDragHeaderView.STATE_REFRESHING);  

  505.         mIsRefreshing = true;  

  506.         Log.i("Alex2", "现在开始强制刷新");  

  507.         mIsHeaderReady = false;  

  508.         smoothShowHeader();  

  509.         if (refreshListener != null)refreshListener.onRefresh();  

  510.   

  511.   

  512.     }  

  513.   

  514.   

  515.     private void startRefresh() {  

  516.         mIsRefreshing = true;  

  517.         mHeaderView.setState(CustomDragHeaderView.STATE_REFRESHING);  

  518.         Log.i("Alex2", "现在开始加载");  

  519.         mIsHeaderReady = false;  

  520.         if (refreshListener != null) refreshListener.onRefresh();  

  521.   

  522.     }  

  523.   

  524.     public interface OnRefreshListener{  

  525.         public void onRefresh();  

  526.     }  

  527.   

  528.   

  529.     /** 

  530.      * 适用于本recycler的头部下拉刷新view 

  531.      */  

  532.     public static class CustomDragHeaderView extends LinearLayout {  

  533.         public final static int STATE_NORMAL = 0;  

  534.         public final static int STATE_READY = 1;  

  535.         public final static int STATE_REFRESHING = 2;  

  536.         public final static int STATE_FINISH = 3;  

  537.   

  538.         public float screenDensity;  

  539.         private final int ROTATE_ANIM_DURATION = 180;  

  540.         private Context mContext;  

  541.   

  542.         private View mContentView;  

  543.         private View mProgressBar;  

  544.         private ImageView mArrowImageView;  

  545.         private TextView mHintTextView;  

  546.         private Animation mRotateUpAnim;  

  547.         private Animation mRotateDownAnim;  

  548.   

  549.         public CustomDragHeaderView(Context context) {  

  550.             super(context);  

  551.             initView(context);  

  552.         }  

  553.   

  554.         public CustomDragHeaderView(Context context, AttributeSet attrs) {  

  555.             super(context, attrs);  

  556.             initView(context);  

  557.         }  

  558.   

  559.   

  560.         private int mState;  

  561.         public void setState(int state) {  

  562.             if (state == mState)  

  563.                 return;  

  564.   

  565.             if (state == STATE_REFRESHING) { // 显示进度  

  566.                 mArrowImageView.clearAnimation();  

  567.                 mArrowImageView.setVisibility(View.INVISIBLE);  

  568.                 mProgressBar.setVisibility(View.VISIBLE);  

  569.             } else { // 显示箭头图片  

  570.                 mArrowImageView.setVisibility(View.VISIBLE);  

  571.                 mProgressBar.setVisibility(View.INVISIBLE);  

  572.             }  

  573.   

  574.             switch (state) {  

  575.                 case STATE_NORMAL:  

  576.                     if (mState == STATE_READY) {  

  577.                         mArrowImageView.startAnimation(mRotateDownAnim);  

  578.                         mHintTextView.setText(R.string.xlistview_header_hint_normal);  

  579.                     }  

  580.                     else if (mState == STATE_REFRESHING) {//如果是从刷新状态过来  

  581. //                        mArrowImageView.clearAnimation();  

  582.                         mArrowImageView.setVisibility(INVISIBLE);  

  583.                         mHintTextView.setText("load completed");  

  584.                     }  

  585.                     break;  

  586.                 case STATE_READY:  

  587.                     if (mState != STATE_READY) {  

  588.                         mArrowImageView.clearAnimation();  

  589.                         mArrowImageView.startAnimation(mRotateUpAnim);  

  590.                     }  

  591.                         mHintTextView.setText(R.string.xlistview_header_hint_ready);  

  592.                     break;  

  593.                 case STATE_REFRESHING:  

  594.                         mHintTextView.setText(R.string.xlistview_header_hint_loading);  

  595.                     break;  

  596.                 case STATE_FINISH:  

  597.                     mArrowImageView.setVisibility(View.VISIBLE);  

  598.                     mHintTextView.setText(R.string.xlistview_header_hint_normal);  

  599.                     break;  

  600.                 default:  

  601.             }  

  602.   

  603.             mState = state;  

  604.         }  

  605.   

  606.         public void setTopMargin(int height) {  

  607.             if (mContentView==null) return ;  

  608.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  609.             lp.topMargin = height;  

  610.             mContentView.setLayoutParams(lp);  

  611.         }  

  612.         //  

  613.         public int getTopMargin() {  

  614.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  615.             return lp.topMargin;  

  616.         }  

  617.   

  618.         public void setHeight(int height){  

  619.             if (mContentView==null) return ;  

  620.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  621.             lp.height = height;  

  622.             mContentView.setLayoutParams(lp);  

  623.         }  

  624.   

  625.         private int realHeight;  

  626.   

  627.         /** 

  628.          * 得到这个headerView真实的高度,而且这个高度是自己定的 

  629.          * @return 

  630.          */  

  631.         public int getRealHeight(){  

  632.             return realHeight;  

  633.         }  

  634.   

  635.         private void initView(Context context) {  

  636.             mContext = context;  

  637.             this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));//recyclerView里不加这句话的话宽度就会比较窄  

  638.             LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.xlistview_header, null);  

  639.             addView(moreView);  

  640.             moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));  

  641.             mContentView = moreView.findViewById(R.id.xlistview_header_content);  

  642.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  643.             Log.i("Alex", "初始height是" + mContentView.getHeight());  

  644.             lp.height = 150;//手动设置高度,如果要手动加载更多的时候才设置  

  645.             screenDensity = getContext().getResources().getDisplayMetrics().density;//设置屏幕密度,用来px向dp转化  

  646.             lp.height = dp2px(screenDensity,HEADER_HEIGHT);//头部高度75dp  

  647.             realHeight = lp.height;  

  648.             lp.topMargin = -lp.height;  

  649.             mContentView.setLayoutParams(lp);  

  650.             mArrowImageView = (ImageView) findViewById(R.id.xlistview_header_arrow);  

  651.             mHintTextView = (TextView) findViewById(R.id.xlistview_header_hint_textview);  

  652.             mHintTextView.setPadding(0,dp2px(screenDensity,3),0,0);//不知道为什么这个文字总会向上偏一下,所以要补回来  

  653.             mProgressBar = findViewById(R.id.xlistview_header_progressbar);  

  654.   

  655.             mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);  

  656.             mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);  

  657.             mRotateUpAnim.setFillAfter(true);  

  658.             mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);  

  659.             mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);  

  660.             mRotateDownAnim.setFillAfter(true);  

  661.         }  

  662.     }  

  663.   

  664.     public static int dp2px(float density, int dp) {  

  665.         if (dp == 0) {  

  666.             return 0;  

  667.         }  

  668.         return (int) (dp * density + 0.5f);  

  669.     }  

  670.   

  671.     public static class CustomDragRecyclerFooterView extends LinearLayout {  

  672.         public final static int STATE_NORMAL = 0;  

  673.         public final static int STATE_READY = 1;  

  674.         public final static int STATE_LOADING = 2;  

  675.         public final static int STATE_ERROR = 3;  

  676.   

  677.         private Context mContext;  

  678.   

  679.         private View mContentView;  

  680.         private View mProgressBar;  

  681.         private TextView mHintView;  

  682.   

  683.         public CustomDragRecyclerFooterView(Context context) {  

  684.             super(context);  

  685.             initView(context);  

  686.         }  

  687.   

  688.         public CustomDragRecyclerFooterView(Context context, AttributeSet attrs) {  

  689.             super(context, attrs);  

  690.             initView(context);  

  691.         }  

  692.   

  693.   

  694.         public void setState(int state) {  

  695.             mProgressBar.setVisibility(View.INVISIBLE);  

  696. //            mHintView.setVisibility(View.INVISIBLE);  

  697.             if (state == STATE_READY) {  

  698.                 mHintView.setVisibility(View.VISIBLE);  

  699.                 mHintView.setText("松手加载更多");  

  700.             } else if (state == STATE_LOADING) {  

  701.                 mProgressBar.setVisibility(View.VISIBLE);  

  702.                 mHintView.setVisibility(INVISIBLE);  

  703.             } else if(state == STATE_ERROR){  

  704.                 mProgressBar.setVisibility(GONE);  

  705.                 mHintView.setVisibility(VISIBLE);  

  706.                 mHintView.setText(R.string.xlistview_footer_hint_ready);  

  707.             }  

  708.             else {  

  709.                 mHintView.setVisibility(View.VISIBLE);  

  710.                 mHintView.setText(R.string.xlistview_footer_hint_normal);  

  711.                 mHintView.setText("Load more");  

  712.             }  

  713.         }  

  714.   

  715.         public void setBottomMargin(int height) {  

  716.             if (height < 0) return ;  

  717.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  718.             lp.bottomMargin = height;  

  719.             mContentView.setLayoutParams(lp);  

  720.         }  

  721.   

  722.         public int getBottomMargin() {  

  723.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  724.             return lp.bottomMargin;  

  725.         }  

  726.   

  727.   

  728.         /** 

  729.          * normal status 

  730.          */  

  731.         public void normal() {  

  732.             mHintView.setVisibility(View.VISIBLE);  

  733.             mProgressBar.setVisibility(View.GONE);  

  734.         }  

  735.   

  736.   

  737.         /** 

  738.          * loading status 

  739.          */  

  740.         public void loading() {  

  741.             mHintView.setVisibility(View.GONE);  

  742.             mProgressBar.setVisibility(View.VISIBLE);  

  743.         }  

  744.   

  745.         /** 

  746.          * hide footer when disable pull load more 

  747.          */  

  748.         public void hide() {  

  749.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  750.             lp.height = 1;//这里如果设为0那么layoutManger就会抓不到  

  751.             mContentView.setLayoutParams(lp);  

  752.             mContentView.setBackgroundColor(Color.BLACK);//这里的颜色要和自己的背景色一致  

  753.         }  

  754.   

  755.         /** 

  756.          * show footer 

  757.          */  

  758.         public void show() {  

  759.             LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();  

  760.             lp.height = LayoutParams.WRAP_CONTENT;  

  761.             lp.width =  LayoutParams.MATCH_PARENT;  

  762.             mContentView.setLayoutParams(lp);  

  763.             mContentView.setBackgroundColor(Color.WHITE);//这里的颜色要和自己的背景色一致  

  764.         }  

  765.   

  766.         private void initView(Context context) {  

  767.             mContext = context;  

  768.             this.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));  

  769.             LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_customdragfooterview, null);  

  770.             addView(moreView);  

  771.             moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));  

  772.             mContentView = moreView.findViewById(R.id.rlContentView);  

  773.             mProgressBar = moreView.findViewById(R.id.pbContentView);  

  774.             mHintView = (TextView)moreView.findViewById(R.id.ctvContentView);  

  775.             mHintView.setText("load more");  

  776. //            mProgressBar.setVisibility(VISIBLE);//一直会显示转圈,自动加载更多时使用  

  777.         }  

  778.     }  

  779.   

  780.     /** 

  781.      * 为了防止代码上的混乱,使用这个recyclerView自己内置的一个adapter 

  782.      * @param  

  783.      */  

  784.     public static abstract class AlxDragRecyclerViewAdapter extends RecyclerView.Adapter{  

  785.         protected static final int TYPE_HEADER = 436874;  

  786.         protected static final int TYPE_ITEM = 256478;  

  787.         protected static final int TYPE_FOOTER = 9621147;  

  788.   

  789.         private int ITEM;  

  790.   

  791.         private ViewHolder vhItem;  

  792.         protected boolean loadMore;  

  793.   

  794.         private List dataList;  

  795.   

  796.         public List getDataList() {  

  797.             return dataList;  

  798.         }  

  799.   

  800.         public void setDataList(List dataList) {  

  801.             this.dataList = dataList;  

  802.         }  

  803.   

  804.         public AlxDragRecyclerViewAdapter(List dataList,int itemLayout,boolean pullEnable){  

  805.             this.dataList = dataList;  

  806.             this.ITEM = itemLayout;  

  807.             this.loadMore = pullEnable;  

  808.         }  

  809.   

  810.         public abstract ViewHolder setItemViewHolder(View itemView);  

  811.   

  812.         private T getObject(int position){  

  813.             if(dataList!=null && dataList.size()>=position)return dataList.get(position-1);//如果有header  

  814.             return null;  

  815.         }  

  816.   

  817.         @Override  

  818.         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  

  819.             if (viewType == TYPE_ITEM) {  

  820.                 //inflate your layout and pass it to view holder  

  821.                 View itemView = LayoutInflater.from(parent.getContext()).inflate(ITEM,null);  

  822.                 Log.i("Alex","itemView是"+itemView);  

  823.                 this.vhItem = setItemViewHolder(itemView);  

  824.                 Log.i("Alex","vhItem是"+vhItem);  

  825.                 return vhItem;  

  826.             } else if (viewType == TYPE_HEADER) {  

  827.                 //inflate your layout and pass it to view holder  

  828.                 View headerView = new CustomDragHeaderView(parent.getContext());  

  829.                 return new VHHeader(headerView);  

  830.             } else if(viewType==TYPE_FOOTER){  

  831.                 CustomDragRecyclerFooterView footerView = new CustomDragRecyclerFooterView(parent.getContext());  

  832.                 return new VHFooter(footerView);  

  833.             }  

  834.   

  835.             throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");  

  836.         }  

  837.   

  838.         public void setPullLoadMoreEnable(boolean enable){  

  839.             this.loadMore = enable;  

  840.         }  

  841.         public boolean getPullLoadMoreEnable(){return loadMore;}  

  842.   

  843.         @Override  

  844.         public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {//相当于getView  

  845.             Log.i("Alex","正在绑定"+position+"    "+holder.getClass());  

  846.             if (vhItem!=null && holder.getClass() == vhItem.getClass()) {  

  847.                 //cast holder to VHItem and set data  

  848.                 initItemView(holder,position,getObject(position));  

  849.             }else if (holder instanceof AlxDragRecyclerViewAdapter.VHHeader) {  

  850.                 //cast holder to VHHeader and set data for header.  

  851.   

  852.             }else if(holder instanceof AlxDragRecyclerViewAdapter.VHFooter){  

  853.                 if(!loadMore)((VHFooter)holder).footerView.hide();//第一次初始化显示的时候要不要显示footerView  

  854.             }  

  855.         }  

  856.   

  857.         @Override  

  858.         public int getItemCount() {  

  859.             return (dataList==null ||dataList.size()==0)?1:dataList.size() + 2;//如果有header,若list不存在或大小为0就没有footView,反之则有  

  860.         }//这里要考虑到头尾部,多以要加2  

  861.   

  862.         /** 

  863.          * 根据位置判断这里该用哪个ViewHolder 

  864.          * @param position 

  865.          * @return 

  866.          */  

  867.         @Override  

  868.         public int getItemViewType(int position) {  

  869.             if (position == 0)return TYPE_HEADER;  

  870.             else if(isPositonFooter(position))return TYPE_FOOTER;  

  871.             return TYPE_ITEM;  

  872.         }  

  873.   

  874.         protected boolean isPositonFooter(int position){//这里的position从0算起  

  875.                 if (dataList == null && position == 1) return true;//如果没有item  

  876.                 return position == dataList.size() + 1;//如果有item(也许为0)  

  877.         }  

  878.   

  879. //        class VHItem extends RecyclerView.ViewHolder {  

  880. //            public VHItem(View itemView) {  

  881. //                super(itemView);  

  882. //            }  

  883. //            public View getItemView(){return itemView;}  

  884. //        }  

  885. //  

  886.         protected class VHHeader extends RecyclerView.ViewHolder {  

  887.             public VHHeader(View headerView) {  

  888.                 super(headerView);  

  889.             }  

  890.         }  

  891.   

  892.         protected class VHFooter extends RecyclerView.ViewHolder {  

  893.             public CustomDragRecyclerFooterView footerView;  

  894.   

  895.             public VHFooter(View itemView) {  

  896.                 super(itemView);  

  897.                 footerView = (CustomDragRecyclerFooterView)itemView;  

  898.             }  

  899.         }  

  900.   

  901.         public abstract void initItemView(ViewHolder itemHolder,int posion,T entity);  

  902.   

  903.     }  

  904.   

  905.   

  906. }