下拉刷新 上拉加载

本文介绍了一种在Android应用中实现RecyclerView下拉刷新和上划加载更多功能的方法。通过对RecyclerView进行自定义扩展,实现了类似XListView的弹性拖拽效果及自动回弹等特性。文中详细解释了如何通过监听触摸事件实现下拉刷新功能,以及如何利用滚动监听器实现上划加载更多功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下拉刷新:

      


上划加载

      

项目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
  在CODE上查看代码片 派生到我的代码片
  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<position+1){//如果要向下滚  
  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(0instanceof 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==nullreturn ;  
  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==nullreturn ;  
  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 < 0return ;  
  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 <T> 
  783.      */  
  784.     public static abstract class AlxDragRecyclerViewAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder>{  
  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<T> dataList;  
  795.   
  796.         public List<T> getDataList() {  
  797.             return dataList;  
  798.         }  
  799.   
  800.         public void setDataList(List<T> dataList) {  
  801.             this.dataList = dataList;  
  802.         }  
  803.   
  804.         public AlxDragRecyclerViewAdapter(List<T> 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 == 1return 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. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值