android ListView的上部下拉刷新下部点击加载更多具体实现及拓展

来源:互联网 发布:mac 最大化 快捷键 编辑:程序博客网 时间:2024/05/01 06:17

这次就不上图了,例子太多太多了,想必大家都见过.这个功能的实现,简直是开发者必备的.

我也不过多介绍了,网上详细介绍的博客太多太多了,若想深入了解,请参考网上其他博文.

在这里,我只是按照自己的理解,模拟实现了一个,顺便代码贡献出来.

我对之详细标明的注释,想必如果不懂的同学们,看注释也应该明白,前提是,你要耐心看,因为代码有点多,但是我整理过了,还算清晰.

详细代码:

[java] view plaincopy
  1. package com.jj.drag;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.util.Log;  
  6. import android.view.LayoutInflater;  
  7. import android.view.MotionEvent;  
  8. import android.view.View;  
  9. import android.view.View.OnClickListener;  
  10. import android.view.ViewGroup;  
  11. import android.view.animation.Animation;  
  12. import android.view.animation.LinearInterpolator;  
  13. import android.view.animation.RotateAnimation;  
  14. import android.widget.AbsListView;  
  15. import android.widget.AbsListView.OnScrollListener;  
  16. import android.widget.ImageView;  
  17. import android.widget.LinearLayout;  
  18. import android.widget.ListView;  
  19. import android.widget.ProgressBar;  
  20. import android.widget.RelativeLayout;  
  21. import android.widget.TextView;  
  22.   
  23. /*** 
  24.  * 自定义拖拉ListView 
  25.  *  
  26.  * @author zhangjia 
  27.  *  
  28.  */  
  29. public class DragListView extends ListView implements OnScrollListener,  
  30.         OnClickListener {  
  31.     // 拖拉ListView枚举所有状态  
  32.     private enum DListViewState {  
  33.         LV_NORMAL, // 普通状态  
  34.         LV_PULL_REFRESH, // 下拉状态(为超过mHeadViewHeight)  
  35.         LV_RELEASE_REFRESH, // 松开可刷新状态(超过mHeadViewHeight)  
  36.         LV_LOADING;// 加载状态  
  37.     }  
  38.   
  39.     // 点击加载更多枚举所有状态  
  40.     private enum DListViewLoadingMore {  
  41.         LV_NORMAL, // 普通状态  
  42.         LV_LOADING, // 加载状态  
  43.         LV_OVER; // 结束状态  
  44.     }  
  45.   
  46.     private View mHeadView;// 头部headView  
  47.     private TextView mRefreshTextview; // 刷新msg(mHeadView)  
  48.     private TextView mLastUpdateTextView;// 更新事件(mHeadView)  
  49.     private ImageView mArrowImageView;// 下拉图标(mHeadView)  
  50.     private ProgressBar mHeadProgressBar;// 刷新进度体(mHeadView)  
  51.   
  52.     private int mHeadViewWidth; // headView的宽(mHeadView)  
  53.     private int mHeadViewHeight;// headView的高(mHeadView)  
  54.   
  55.     private View mFootView;// 尾部mFootView  
  56.     private View mLoadMoreView;// mFootView 的view(mFootView)  
  57.     private TextView mLoadMoreTextView;// 加载更多.(mFootView)  
  58.     private View mLoadingView;// 加载中...View(mFootView)  
  59.   
  60.     private Animation animation, reverseAnimation;// 旋转动画,旋转动画之后旋转动画.  
  61.   
  62.     private int mFirstItemIndex = -1;// 当前视图能看到的第一个项的索引  
  63.   
  64.     // 用于保证startY的值在一个完整的touch事件中只被记录一次  
  65.     private boolean mIsRecord = false;  
  66.   
  67.     private int mStartY, mMoveY;// 按下是的y坐标,move时的y坐标  
  68.   
  69.     private DListViewState mlistViewState = DListViewState.LV_NORMAL;// 拖拉状态.(自定义枚举)  
  70.   
  71.     private DListViewLoadingMore loadingMoreState = DListViewLoadingMore.LV_NORMAL;// 加载更多默认状态.  
  72.   
  73.     private final static int RATIO = 2;// 手势下拉距离比.  
  74.   
  75.     private boolean mBack = false;// headView是否返回.  
  76.   
  77.     private OnRefreshLoadingMoreListener onRefreshLoadingMoreListener;// 下拉刷新接口(自定义)  
  78.   
  79.     private boolean isScroller = true;// 是否屏蔽ListView滑动。  
  80.   
  81.     public DragListView(Context context) {  
  82.         super(context, null);  
  83.         initDragListView(context);  
  84.     }  
  85.   
  86.     public DragListView(Context context, AttributeSet attrs) {  
  87.         super(context, attrs);  
  88.         initDragListView(context);  
  89.     }  
  90.   
  91.     // 注入下拉刷新接口  
  92.     public void setOnRefreshListener(  
  93.             OnRefreshLoadingMoreListener onRefreshLoadingMoreListener) {  
  94.         this.onRefreshLoadingMoreListener = onRefreshLoadingMoreListener;  
  95.     }  
  96.   
  97.     /*** 
  98.      * 初始化ListView 
  99.      */  
  100.     public void initDragListView(Context context) {  
  101.   
  102.         String time = "1994.12.05";// 更新时间  
  103.   
  104.         initHeadView(context, time);// 初始化该head.  
  105.   
  106.         initLoadMoreView(context);// 初始化footer  
  107.   
  108.         setOnScrollListener(this);// ListView滚动监听  
  109.     }  
  110.   
  111.     /*** 
  112.      * 初始话头部HeadView 
  113.      *  
  114.      * @param context 
  115.      *            上下文 
  116.      * @param time 
  117.      *            上次更新时间 
  118.      */  
  119.     public void initHeadView(Context context, String time) {  
  120.         mHeadView = LayoutInflater.from(context).inflate(R.layout.head, null);  
  121.         mArrowImageView = (ImageView) mHeadView  
  122.                 .findViewById(R.id.head_arrowImageView);  
  123.         mArrowImageView.setMinimumWidth(60);  
  124.   
  125.         mHeadProgressBar = (ProgressBar) mHeadView  
  126.                 .findViewById(R.id.head_progressBar);  
  127.   
  128.         mRefreshTextview = (TextView) mHeadView  
  129.                 .findViewById(R.id.head_tipsTextView);  
  130.   
  131.         mLastUpdateTextView = (TextView) mHeadView  
  132.                 .findViewById(R.id.head_lastUpdatedTextView);  
  133.         // 显示更新事件  
  134.         mLastUpdateTextView.setText("最近更新:" + time);  
  135.   
  136.         measureView(mHeadView);  
  137.         // 获取宽和高  
  138.         mHeadViewWidth = mHeadView.getMeasuredWidth();  
  139.         mHeadViewHeight = mHeadView.getMeasuredHeight();  
  140.   
  141.         addHeaderView(mHeadView, nullfalse);// 将初始好的ListView add进拖拽ListView  
  142.         // 在这里我们要将此headView设置到顶部不显示位置.  
  143.         mHeadView.setPadding(0, -1 * mHeadViewHeight, 00);  
  144.   
  145.         initAnimation();// 初始化动画  
  146.     }  
  147.   
  148.     /*** 
  149.      * 初始化底部加载更多控件 
  150.      */  
  151.     private void initLoadMoreView(Context context) {  
  152.         mFootView = LayoutInflater.from(context).inflate(R.layout.footer, null);  
  153.   
  154.         mLoadMoreView = mFootView.findViewById(R.id.load_more_view);  
  155.   
  156.         mLoadMoreTextView = (TextView) mFootView  
  157.                 .findViewById(R.id.load_more_tv);  
  158.   
  159.         mLoadingView = (LinearLayout) mFootView  
  160.                 .findViewById(R.id.loading_layout);  
  161.   
  162.         mLoadMoreView.setOnClickListener(this);  
  163.   
  164.         addFooterView(mFootView);  
  165.     }  
  166.   
  167.     /*** 
  168.      * 初始化动画 
  169.      */  
  170.     private void initAnimation() {  
  171.         // 旋转动画  
  172.         animation = new RotateAnimation(0, -180,  
  173.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  174.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  175.         animation.setInterpolator(new LinearInterpolator());// 匀速  
  176.         animation.setDuration(250);  
  177.         animation.setFillAfter(true);// 停留在最后状态.  
  178.         // 反向旋转动画  
  179.         reverseAnimation = new RotateAnimation(-1800,  
  180.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f,  
  181.                 RotateAnimation.RELATIVE_TO_SELF, 0.5f);  
  182.         reverseAnimation.setInterpolator(new LinearInterpolator());  
  183.         reverseAnimation.setDuration(250);  
  184.         reverseAnimation.setFillAfter(true);  
  185.     }  
  186.   
  187.     /*** 
  188.      * 作用:测量 headView的宽和高. 
  189.      *  
  190.      * @param child 
  191.      */  
  192.     private void measureView(View child) {  
  193.         ViewGroup.LayoutParams p = child.getLayoutParams();  
  194.         if (p == null) {  
  195.             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
  196.                     ViewGroup.LayoutParams.WRAP_CONTENT);  
  197.         }  
  198.         int childWidthSpec = ViewGroup.getChildMeasureSpec(00 + 0, p.width);  
  199.         int lpHeight = p.height;  
  200.         int childHeightSpec;  
  201.         if (lpHeight > 0) {  
  202.             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,  
  203.                     MeasureSpec.EXACTLY);  
  204.         } else {  
  205.             childHeightSpec = MeasureSpec.makeMeasureSpec(0,  
  206.                     MeasureSpec.UNSPECIFIED);  
  207.         }  
  208.         child.measure(childWidthSpec, childHeightSpec);  
  209.     }  
  210.   
  211.     /*** 
  212.      * touch 事件监听 
  213.      */  
  214.     @Override  
  215.     public boolean onTouchEvent(MotionEvent ev) {  
  216.         switch (ev.getAction()) {  
  217.         // 按下  
  218.         case MotionEvent.ACTION_DOWN:  
  219.             doActionDown(ev);  
  220.             break;  
  221.         // 移动  
  222.         case MotionEvent.ACTION_MOVE:  
  223.             doActionMove(ev);  
  224.             break;  
  225.         // 抬起  
  226.         case MotionEvent.ACTION_UP:  
  227.             doActionUp(ev);  
  228.             break;  
  229.         default:  
  230.             break;  
  231.         }  
  232.         /*** 
  233.          * 如果是ListView本身的拉动,那么返回true,这样ListView不可以拖动. 
  234.          * 如果不是ListView的拉动,那么调用父类方法,这样就可以上拉执行. 
  235.          */  
  236.         if (isScroller) {  
  237.             return super.onTouchEvent(ev);  
  238.         } else {  
  239.             return true;  
  240.         }  
  241.   
  242.     }  
  243.   
  244.     /*** 
  245.      * 摁下操作 
  246.      *  
  247.      * 作用:获取摁下是的y坐标 
  248.      *  
  249.      * @param event 
  250.      */  
  251.     void doActionDown(MotionEvent event) {  
  252.         if (mIsRecord == false && mFirstItemIndex == 0) {  
  253.             mStartY = (int) event.getY();  
  254.             mIsRecord = true;  
  255.         }  
  256.     }  
  257.   
  258.     /*** 
  259.      * 拖拽移动操作 
  260.      *  
  261.      * @param event 
  262.      */  
  263.     void doActionMove(MotionEvent event) {  
  264.         mMoveY = (int) event.getY();// 获取实时滑动y坐标  
  265.         // 检测是否是一次touch事件.  
  266.         if (mIsRecord == false && mFirstItemIndex == 0) {  
  267.             mStartY = (int) event.getY();  
  268.             mIsRecord = true;  
  269.         }  
  270.         /*** 
  271.          * 如果touch关闭或者正处于Loading状态的话 return. 
  272.          */  
  273.         if (mIsRecord == false || mlistViewState == DListViewState.LV_LOADING) {  
  274.             return;  
  275.         }  
  276.         // 向下啦headview移动距离为y移动的一半.(比较友好)  
  277.         int offset = (mMoveY - mStartY) / RATIO;  
  278.   
  279.         switch (mlistViewState) {  
  280.         // 普通状态  
  281.         case LV_NORMAL: {  
  282.             // 如果<0,则意味着上滑动.  
  283.             if (offset > 0) {  
  284.                 // 设置headView的padding属性.  
  285.                 mHeadView.setPadding(0, offset - mHeadViewHeight, 00);  
  286.                 switchViewState(DListViewState.LV_PULL_REFRESH);// 下拉状态  
  287.             }  
  288.   
  289.         }  
  290.             break;  
  291.         // 下拉状态  
  292.         case LV_PULL_REFRESH: {  
  293.             setSelection(0);// 时时保持在顶部.  
  294.             // 设置headView的padding属性.  
  295.             mHeadView.setPadding(0, offset - mHeadViewHeight, 00);  
  296.             if (offset < 0) {  
  297.                 /*** 
  298.                  * 要明白为什么isScroller = false; 
  299.                  */  
  300.                 isScroller = false;  
  301.                 switchViewState(DListViewState.LV_NORMAL);// 普通状态  
  302.                 Log.e("jj""isScroller=" + isScroller);  
  303.             } else if (offset > mHeadViewHeight) {// 如果下拉的offset超过headView的高度则要执行刷新.  
  304.                 switchViewState(DListViewState.LV_RELEASE_REFRESH);// 更新为可刷新的下拉状态.  
  305.             }  
  306.         }  
  307.             break;  
  308.         // 可刷新状态  
  309.         case LV_RELEASE_REFRESH: {  
  310.             setSelection(0);时时保持在顶部  
  311.             // 设置headView的padding属性.  
  312.             mHeadView.setPadding(0, offset - mHeadViewHeight, 00);  
  313.             // 下拉offset>0,但是没有超过headView的高度.那么要goback 原装.  
  314.             if (offset >= 0 && offset <= mHeadViewHeight) {  
  315.                 mBack = true;  
  316.                 switchViewState(DListViewState.LV_PULL_REFRESH);  
  317.             } else if (offset < 0) {  
  318.                 switchViewState(DListViewState.LV_NORMAL);  
  319.             } else {  
  320.   
  321.             }  
  322.         }  
  323.             break;  
  324.         default:  
  325.             return;  
  326.         }  
  327.         ;  
  328.     }  
  329.   
  330.     /*** 
  331.      * 手势抬起操作 
  332.      *  
  333.      * @param event 
  334.      */  
  335.     public void doActionUp(MotionEvent event) {  
  336.         mIsRecord = false;// 此时的touch事件完毕,要关闭。  
  337.         isScroller = true;// ListView可以Scrooler滑动.  
  338.         mBack = false;  
  339.         // 如果下拉状态处于loading状态.  
  340.         if (mlistViewState == DListViewState.LV_LOADING) {  
  341.             return;  
  342.         }  
  343.         // 处理相应状态.  
  344.         switch (mlistViewState) {  
  345.         // 普通状态  
  346.         case LV_NORMAL:  
  347.   
  348.             break;  
  349.         // 下拉状态  
  350.         case LV_PULL_REFRESH:  
  351.             mHeadView.setPadding(0, -1 * mHeadViewHeight, 00);  
  352.             switchViewState(mlistViewState.LV_NORMAL);  
  353.             break;  
  354.         // 刷新状态  
  355.         case LV_RELEASE_REFRESH:  
  356.             mHeadView.setPadding(0000);  
  357.             switchViewState(mlistViewState.LV_LOADING);  
  358.             onRefresh();// 下拉刷新  
  359.             break;  
  360.         }  
  361.   
  362.     }  
  363.   
  364.     // 切换headview视图  
  365.     private void switchViewState(DListViewState state) {  
  366.   
  367.         switch (state) {  
  368.         // 普通状态  
  369.         case LV_NORMAL: {  
  370.             mArrowImageView.clearAnimation();// 清除动画  
  371.             mArrowImageView.setImageResource(R.drawable.arrow);  
  372.         }  
  373.             break;  
  374.         // 下拉状态  
  375.         case LV_PULL_REFRESH: {  
  376.             mHeadProgressBar.setVisibility(View.GONE);// 隐藏进度条  
  377.             mArrowImageView.setVisibility(View.VISIBLE);// 下拉图标  
  378.             mRefreshTextview.setText("下拉可以刷新");  
  379.             mArrowImageView.clearAnimation();// 清除动画  
  380.   
  381.             // 是有可刷新状态(LV_RELEASE_REFRESH)转为这个状态才执行,其实就是你下拉后在上拉会执行.  
  382.             if (mBack) {  
  383.                 mBack = false;  
  384.                 mArrowImageView.clearAnimation();// 清除动画  
  385.                 mArrowImageView.startAnimation(reverseAnimation);// 启动反转动画  
  386.             }  
  387.         }  
  388.             break;  
  389.         // 松开刷新状态  
  390.         case LV_RELEASE_REFRESH: {  
  391.             mHeadProgressBar.setVisibility(View.GONE);// 隐藏进度条  
  392.             mArrowImageView.setVisibility(View.VISIBLE);// 显示下拉图标  
  393.             mRefreshTextview.setText("松开获取更多");  
  394.             mArrowImageView.clearAnimation();// 清除动画  
  395.             mArrowImageView.startAnimation(animation);// 启动动画  
  396.         }  
  397.             break;  
  398.         // 加载状态  
  399.         case LV_LOADING: {  
  400.             Log.e("!!!!!!!!!!!""convert to IListViewState.LVS_LOADING");  
  401.             mHeadProgressBar.setVisibility(View.VISIBLE);  
  402.             mArrowImageView.clearAnimation();  
  403.             mArrowImageView.setVisibility(View.GONE);  
  404.             mRefreshTextview.setText("载入中...");  
  405.         }  
  406.             break;  
  407.         default:  
  408.             return;  
  409.         }  
  410.         // 切记不要忘记时时更新状态。  
  411.         mlistViewState = state;  
  412.   
  413.     }  
  414.   
  415.     /*** 
  416.      * 下拉刷新 
  417.      */  
  418.     private void onRefresh() {  
  419.         if (onRefreshLoadingMoreListener != null) {  
  420.             onRefreshLoadingMoreListener.onRefresh();  
  421.         }  
  422.     }  
  423.   
  424.     /*** 
  425.      * 下拉刷新完毕 
  426.      */  
  427.     public void onRefreshComplete() {  
  428.         mHeadView.setPadding(0, -1 * mHeadViewHeight, 00);// 回归.  
  429.         switchViewState(mlistViewState.LV_NORMAL);//  
  430.     }  
  431.   
  432.     /*** 
  433.      * 点击加载更多 
  434.      *  
  435.      * @param flag 
  436.      *            数据是否已全部加载完毕 
  437.      */  
  438.     public void onLoadMoreComplete(boolean flag) {  
  439.         if (flag) {  
  440.             updateLoadMoreViewState(DListViewLoadingMore.LV_OVER);  
  441.         } else {  
  442.             updateLoadMoreViewState(DListViewLoadingMore.LV_NORMAL);  
  443.         }  
  444.   
  445.     }  
  446.   
  447.     // 更新Footview视图  
  448.     private void updateLoadMoreViewState(DListViewLoadingMore state) {  
  449.         switch (state) {  
  450.         // 普通状态  
  451.         case LV_NORMAL:  
  452.             mLoadingView.setVisibility(View.GONE);  
  453.             mLoadMoreTextView.setVisibility(View.VISIBLE);  
  454.             mLoadMoreTextView.setText("查看更多");  
  455.             break;  
  456.         // 加载中状态  
  457.         case LV_LOADING:  
  458.             mLoadingView.setVisibility(View.VISIBLE);  
  459.             mLoadMoreTextView.setVisibility(View.GONE);  
  460.             break;  
  461.         // 加载完毕状态  
  462.         case LV_OVER:  
  463.             mLoadingView.setVisibility(View.GONE);  
  464.             mLoadMoreTextView.setVisibility(View.VISIBLE);  
  465.             mLoadMoreTextView.setText("加载完毕");  
  466.             break;  
  467.         default:  
  468.             break;  
  469.         }  
  470.         loadingMoreState = state;  
  471.     }  
  472.   
  473.     /*** 
  474.      * ListView 滑动监听 
  475.      */  
  476.     @Override  
  477.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
  478.   
  479.     }  
  480.   
  481.     @Override  
  482.     public void onScroll(AbsListView view, int firstVisibleItem,  
  483.             int visibleItemCount, int totalItemCount) {  
  484.         mFirstItemIndex = firstVisibleItem;  
  485.     }  
  486.   
  487.     /*** 
  488.      * 底部点击事件 
  489.      */  
  490.     @Override  
  491.     public void onClick(View v) {  
  492.         // 防止重复点击  
  493.         if (onRefreshLoadingMoreListener != null  
  494.                 && loadingMoreState == DListViewLoadingMore.LV_NORMAL) {  
  495.             updateLoadMoreViewState(DListViewLoadingMore.LV_LOADING);  
  496.             onRefreshLoadingMoreListener.onLoadMore();// 对外提供方法加载更多.  
  497.         }  
  498.   
  499.     }  
  500.   
  501.     /*** 
  502.      * 自定义接口 
  503.      */  
  504.     public interface OnRefreshLoadingMoreListener {  
  505.         /*** 
  506.          * // 下拉刷新执行 
  507.          */  
  508.         void onRefresh();  
  509.   
  510.         /*** 
  511.          * 点击加载更多 
  512.          */  
  513.         void onLoadMore();  
  514.     }  
  515.   
  516. }  


上面就是全部代码,其实重要的是明白理解,这样我们还可以进行拓展.

具体应用:(只需要这样引用即可.)

[java] view plaincopy
  1. <com.jj.drag.DragListView  
  2.     android:id="@+id/dlv_main"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:cacheColorHint="#00000000" />  

在Activity中的调用,相比大家都清楚,开个异步或线程进行加载数据,这里我简单说一下异步使用,线程同理.

代码如下:

[java] view plaincopy
  1. /*** 
  2.      * 执行类 异步 
  3.      *  
  4.      * @author zhangjia 
  5.      *  
  6.      */  
  7.     class MyAsyncTask extends AsyncTask<Void, Void, Void> {  
  8.         private Context context;  
  9.         private int index;// 用于判断是下拉刷新还是点击加载更多  
  10.   
  11.         public MyAsyncTask(Context context, int index) {  
  12.             this.context = context;  
  13.             this.index = index;  
  14.         }  
  15.   
  16.         @Override  
  17.         protected Void doInBackground(Void... params) {  
  18.             try {  
  19.                 Thread.sleep(2000);  
  20.             } catch (InterruptedException e1) {  
  21.                 e1.printStackTrace();  
  22.             }  
  23.   
  24.             return null;  
  25.         }  
  26.   
  27.         @Override  
  28.         protected void onPreExecute() {  
  29.             super.onPreExecute();  
  30.         }  
  31.   
  32.         @Override  
  33.         protected void onPostExecute(Void result) {  
  34.             super.onPostExecute(result);  
  35.             if (index == DRAG_INDEX)  
  36.                 dlv_main.onRefreshComplete();  
  37.             else if (index == LOADMORE_INDEX)  
  38.                 dlv_main.onLoadMoreComplete(false);  
  39.         }  
  40.   
  41.     }  
先声明一点,这个只是个示例,所以这部分代码写的不够友好,也请见谅.

就说道这里,最后展示一下效果:

                

至于如果显示,如何adapter.notifyDataSetChanged();那就要大家开发时候自己调理了.

最后说明一点:网上有好多介绍下拉刷新的例子,但是他们没有对滑动进行处理,比如,我下拉的时候现在不想刷新了,这时我又向上滑动,正常的处理,应该滑动到FirstItemIndex=1就是顶部,滑动就结束了.(意思就是要下拉和listview正常滑动要分开)可是网上一些案例都没有对之处理,用起来不友好,大家可以看看成功案例,那些新浪,腾讯,百度等.

解决方法:(这是onTouch方法中的一部分.)

[java] view plaincopy
  1. /*** 
  2.          * 如果是ListView本身的拉动,那么返回true,这样ListView不可以拖动. 
  3.          * 如果不是ListView的拉动,那么调用父类方法,这样就可以上拉执行. 
  4.          */  
  5.         if (isScroller) {  
  6.             return super.onTouchEvent(ev);  
  7.         } else {  
  8.             return true;  
  9.         }  
要问Why的话,那么你就要去详细看Touch种种事件,记住,这里用到的不是分发与拦截,分发拦截流程如下:

Activity 的dispatchTouchEvent开始分发给子的View,如果该View是ViewGroup的话,那么执行其dispatchTouchEvent进行分发,在执行相应的onInterceptTouchEvent拦截.如果要想实现上诉说的那种效果,那么在自定义ListView中对拦截分发方法是无效的,只有在ListView的上一层进行处理,比我我们在外层自定义一个布局,等等,实现起来总之麻烦一个字,其实我们也可以考虑考虑onTouchEvent事件的实现,

ListView.java

[java] view plaincopy
  1. @Override  
  2.    public boolean onTouchEvent(MotionEvent ev) {  
  3.        if (mItemsCanFocus && ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {  
  4.            // Don't handle edge touches immediately -- they may actually belong to one of our  
  5.            // descendants.  
  6.            return false;  
  7.        }  
  8.        return super.onTouchEvent(ev);  
  9.    }  

继续点击查看父类,这里就不显示了,自己可以查看源码,其实就是我们ListView滑动的具体实现,而此时我们只是想临时屏蔽掉此滑动,那么我们只需要不调用父类的onTouchEvent不就OK的,是的,确实如此,而何时进行屏蔽,大家就仔细看上面源码实现吧,解释的也很清楚,这样大家都明白了吧。注:有疑问请留言!之前这个例子android 自定义ScrollView实现反弹效果(以及解决和ListView之间的冲突)没有解决这个问题,因为处境不同.(不过正在完善,相信也会完美的实现这些效果,因为原理上是行的通的。)


知识拓展:

首先我们还是看一些案例:

      

效果就是可以上下拖拽.而用在最多的地方就是ListView,而普通的布局拖拽直接自定义布局就OK了,详情请参考上面连接那篇文章.

实现起来也不是很麻烦,就是对上面那个自定义类稍作修改,把底部也做成动态拖拽效果就OK了.

这里不详细讲解,因为注释相当明确,如有疑问,请指出.

代码如下

[java] view plaincopy
  1. package com.jj.drag;  
  2.   
  3. import android.content.Context;  
  4. import android.os.AsyncTask;  
  5. import android.util.AttributeSet;  
  6. import android.util.Log;  
  7. import android.view.LayoutInflater;  
  8. import android.view.MotionEvent;  
  9. import android.view.View;  
  10. import android.view.View.OnClickListener;  
  11. import android.view.ViewGroup;  
  12. import android.view.animation.Animation;  
  13. import android.view.animation.LinearInterpolator;  
  14. import android.view.animation.RotateAnimation;  
  15. import android.widget.AbsListView;  
  16. import android.widget.AbsListView.OnScrollListener;  
  17. import android.widget.ImageView;  
  18. import android.widget.LinearLayout;  
  19. import android.widget.ListView;  
  20. import android.widget.ProgressBar;  
  21. import android.widget.RelativeLayout;  
  22. import android.widget.TextView;  
  23.   
  24. /*** 
  25.  * 自定义拖拉ListView 
  26.  *  
  27.  * @author zhangjia 
  28.  *  
  29.  */  
  30. public class DragListView extends ListView implements OnScrollListener,  
  31.         OnClickListener {  
  32.     // 下拉ListView枚举所有状态  
  33.     private enum DListViewState {  
  34.         LV_NORMAL, // 普通状态  
  35.         LV_PULL_REFRESH, // 下拉状态(为超过mHeadViewHeight)  
  36.   
  37.     }  
  38.   
  39.     // 点击加载更多枚举所有状态  
  40.     private enum DListViewLoadingMore {  
  41.         LV_NORMAL, // 普通状态  
  42.         LV_PULL_REFRESH, // 上拉状态(为超过mHeadViewHeight)  
  43.     }  
  44.   
  45.     private View mHeadView, mFootView;// 头部headView  
  46.   
  47.     private int mHeadViewWidth; // headView的宽(mHeadView)  
  48.     private int mHeadViewHeight;// headView的高(mHeadView)  
  49.   
  50.     private int mFirstItemIndex = -1;// 当前视图能看到的第一个项的索引  
  51.   
  52.     private int mLastItemIndex = -1;// 当前视图中是否是最后一项.  
  53.   
  54.     // 用于保证startY的值在一个完整的touch事件中只被记录一次  
  55.     private boolean mIsRecord = false;// 针对下拉  
  56.   
  57.     private boolean mIsRecord_B = false;// 针对上拉  
  58.   
  59.     private int mStartY, mMoveY;// 按下是的y坐标,move时的y坐标  
  60.   
  61.     private DListViewState mlistViewState = DListViewState.LV_NORMAL;// 拖拉状态.(自定义枚举)  
  62.   
  63.     private DListViewLoadingMore loadingMoreState = DListViewLoadingMore.LV_NORMAL;// 加载更多默认状态.  
  64.   
  65.     private final static int RATIO = 2;// 手势下拉距离比.  
  66.   
  67.     private boolean isScroller = true;// 是否屏蔽ListView滑动。  
  68.   
  69.     private MyAsynTask myAsynTask;// 任务  
  70.     private final static int DRAG_UP = 1, DRAG_DOWN = 2;  
  71.   
  72.     public DragListView(Context context) {  
  73.         super(context, null);  
  74.         initDragListView(context);  
  75.     }  
  76.   
  77.     public DragListView(Context context, AttributeSet attrs) {  
  78.         super(context, attrs);  
  79.         initDragListView(context);  
  80.     }  
  81.   
  82.     /*** 
  83.      * 初始化ListView 
  84.      */  
  85.     public void initDragListView(Context context) {  
  86.   
  87.         initHeadView(context);// 初始化该head.  
  88.   
  89.         initFooterView(context);// 初始化footer  
  90.   
  91.         setOnScrollListener(this);// ListView滚动监听  
  92.     }  
  93.   
  94.     /*** 
  95.      * 初始话头部HeadView 
  96.      *  
  97.      * @param context 
  98.      *            上下文 
  99.      * @param time 
  100.      *            上次更新时间 
  101.      */  
  102.     public void initHeadView(Context context) {  
  103.         mHeadView = LayoutInflater.from(context).inflate(R.layout.head, null);  
  104.         measureView(mHeadView);  
  105.         // 获取宽和高  
  106.         mHeadViewWidth = mHeadView.getMeasuredWidth();  
  107.         mHeadViewHeight = mHeadView.getMeasuredHeight();  
  108.   
  109.         addHeaderView(mHeadView, nullfalse);// 将初始好的ListView add进拖拽ListView  
  110.         // 在这里我们要将此headView设置到顶部不显示位置.  
  111.         mHeadView.setPadding(0, -1 * mHeadViewHeight, 00);  
  112.   
  113.     }  
  114.   
  115.     /*** 
  116.      * 初始化底部加载更多控件 
  117.      */  
  118.     private void initFooterView(Context context) {  
  119.         mFootView = LayoutInflater.from(context).inflate(R.layout.head, null);  
  120.         addFooterView(mFootView, nullfalse);// 将初始好的ListView add进拖拽ListView  
  121.         // 在这里我们要将此FooterView设置到底部不显示位置.  
  122.         mFootView.setPadding(0, -1 * mHeadViewHeight, 00);  
  123.     }  
  124.   
  125.     /*** 
  126.      * 作用:测量 headView的宽和高. 
  127.      *  
  128.      * @param child 
  129.      */  
  130.     private void measureView(View child) {  
  131.         ViewGroup.LayoutParams p = child.getLayoutParams();  
  132.         if (p == null) {  
  133.             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,  
  134.                     ViewGroup.LayoutParams.WRAP_CONTENT);  
  135.         }  
  136.         int childWidthSpec = ViewGroup.getChildMeasureSpec(00 + 0, p.width);  
  137.         int lpHeight = p.height;  
  138.         int childHeightSpec;  
  139.         if (lpHeight > 0) {  
  140.             childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,  
  141.                     MeasureSpec.EXACTLY);  
  142.         } else {  
  143.             childHeightSpec = MeasureSpec.makeMeasureSpec(0,  
  144.                     MeasureSpec.UNSPECIFIED);  
  145.         }  
  146.         child.measure(childWidthSpec, childHeightSpec);  
  147.     }  
  148.   
  149.     /*** 
  150.      * touch 事件监听 
  151.      */  
  152.     @Override  
  153.     public boolean onTouchEvent(MotionEvent ev) {  
  154.   
  155.         switch (ev.getAction()) {  
  156.         // 按下  
  157.         case MotionEvent.ACTION_DOWN:  
  158.             doActionDown_B(ev);  
  159.             doActionDown(ev);  
  160.             break;  
  161.         // 移动  
  162.         case MotionEvent.ACTION_MOVE:  
  163.             doActionMove_B(ev);  
  164.             doActionMove(ev);  
  165.             break;  
  166.         // 抬起  
  167.         case MotionEvent.ACTION_UP:  
  168.             doActionUp_B(ev);  
  169.             doActionUp(ev);  
  170.             break;  
  171.         default:  
  172.             break;  
  173.         }  
  174.   
  175.         /*** 
  176.          * 如果是ListView本身的拉动,那么返回true,这样ListView不可以拖动. 
  177.          * 如果不是ListView的拉动,那么调用父类方法,这样就可以上拉执行. 
  178.          */  
  179.         if (isScroller) {  
  180.             return super.onTouchEvent(ev);  
  181.         } else {  
  182.             return true;  
  183.         }  
  184.   
  185.     }  
  186.   
  187.     /*** 
  188.      * 摁下操作 
  189.      *  
  190.      * 作用:获取摁下是的y坐标 
  191.      *  
  192.      * @param event 
  193.      */  
  194.     void doActionDown(MotionEvent event) {  
  195.         // 如果是第一项且是一次touch  
  196.         if (mIsRecord == false && mFirstItemIndex == 0) {  
  197.             mStartY = (int) event.getY();  
  198.             mIsRecord = true;  
  199.         }  
  200.     }  
  201.   
  202.     /*** 
  203.      * 摁下操作 底部 
  204.      *  
  205.      * 作用:获取摁下是的y坐标 
  206.      */  
  207.     void doActionDown_B(MotionEvent event) {  
  208.         // 如果是第一项且是一次touch  
  209.         if (mIsRecord_B == false && mLastItemIndex == getCount()) {  
  210.             mStartY = (int) event.getY();  
  211.             mIsRecord_B = true;  
  212.         }  
  213.     }  
  214.   
  215.     /*** 
  216.      * 拖拽移动操作 
  217.      *  
  218.      * @param event 
  219.      */  
  220.     void doActionMove(MotionEvent event) {  
  221.   
  222.         // 判断是否是第一项,若不是直接返回  
  223.         mMoveY = (int) event.getY();// 获取实时滑动y坐标  
  224.   
  225.         // 检测是否是一次touch事件.  
  226.         if (mIsRecord == false && mFirstItemIndex == 0) {  
  227.             mStartY = (int) event.getY();  
  228.             mIsRecord = true;  
  229.         }  
  230.         // 直接返回说明不是第一项  
  231.         if (mIsRecord == false)  
  232.             return;  
  233.   
  234.         // 向下啦headview移动距离为y移动的一半.(比较友好)  
  235.         int offset = (mMoveY - mStartY) / RATIO;  
  236.   
  237.         switch (mlistViewState) {  
  238.         // 普通状态  
  239.         case LV_NORMAL: {  
  240.             // 说明下拉  
  241.             if (offset > 0) {  
  242.                 // 设置headView的padding属性.  
  243.                 mHeadView.setPadding(0, offset - mHeadViewHeight, 00);  
  244.                 mlistViewState = DListViewState.LV_PULL_REFRESH;// 下拉状态  
  245.             }  
  246.         }  
  247.             break;  
  248.         // 下拉状态  
  249.         case LV_PULL_REFRESH: {  
  250.             setSelection(0);// 时时保持在顶部.  
  251.             // 设置headView的padding属性.  
  252.             mHeadView.setPadding(0, offset - mHeadViewHeight, 00);  
  253.             if (offset < 0) {  
  254.                 /*** 
  255.                  * 要明白为什么isScroller = false; 
  256.                  */  
  257.                 isScroller = false;  
  258.                 mlistViewState = mlistViewState.LV_NORMAL;  
  259.             }  
  260.         }  
  261.             break;  
  262.         default:  
  263.             return;  
  264.         }  
  265.     }  
  266.   
  267.     void doActionMove_B(MotionEvent event) {  
  268.         mMoveY = (int) event.getY();// 获取实时滑动y坐标  
  269.         // 检测是否是一次touch事件.(若mFirstItemIndex为0则要初始化mStartY)  
  270.         if (mIsRecord_B == false && mLastItemIndex == getCount()) {  
  271.             mStartY = (int) event.getY();  
  272.             mIsRecord_B = true;  
  273.         }  
  274.         // 直接返回说明不是最后一项  
  275.         if (mIsRecord_B == false)  
  276.             return;  
  277.   
  278.         // 向下啦headview移动距离为y移动的一半.(比较友好)  
  279.         int offset = (mMoveY - mStartY) / RATIO;  
  280.   
  281.         switch (loadingMoreState) {  
  282.         // 普通状态  
  283.         case LV_NORMAL: {  
  284.             // 说明上拉  
  285.             if (offset < 0) {  
  286.                 int distance = Math.abs(offset);  
  287.                 // 设置headView的padding属性.  
  288.                 mFootView.setPadding(0, distance - mHeadViewHeight, 00);  
  289.                 loadingMoreState = loadingMoreState.LV_PULL_REFRESH;// 下拉状态  
  290.             }  
  291.         }  
  292.             break;  
  293.         // 上拉状态  
  294.         case LV_PULL_REFRESH: {  
  295.             setSelection(getCount() - 1);// 时时保持最底部  
  296.             // 设置headView的padding属性.  
  297.             int distance = Math.abs(offset);  
  298.             mFootView.setPadding(0, distance - mHeadViewHeight, 00);  
  299.             // 说明下滑  
  300.             if (offset > 0) {  
  301.                 /*** 
  302.                  * 要明白为什么isScroller = false; 
  303.                  */  
  304.                 isScroller = false;  
  305.                 loadingMoreState = loadingMoreState.LV_NORMAL;  
  306.             }  
  307.         }  
  308.             break;  
  309.         default:  
  310.             return;  
  311.         }  
  312.     }  
  313.   
  314.     /*** 
  315.      * 手势抬起操作 
  316.      *  
  317.      * @param event 
  318.      */  
  319.     public void doActionUp(MotionEvent event) {  
  320.         mIsRecord = false;// 此时的touch事件完毕,要关闭。  
  321.         mIsRecord_B = false// 此时的touch事件完毕,要关闭。  
  322.         isScroller = true;// ListView可以Scrooler滑动.  
  323.         mlistViewState = mlistViewState.LV_NORMAL;// 状态也回归最初状态  
  324.   
  325.         // 执行相应动画.  
  326.         myAsynTask = new MyAsynTask();  
  327.         myAsynTask.execute(DRAG_UP);  
  328.   
  329.     }  
  330.   
  331.     private void doActionUp_B(MotionEvent event) {  
  332.         mIsRecord = false;// 此时的touch事件完毕,要关闭。  
  333.         isScroller = true;// ListView可以Scrooler滑动.  
  334.   
  335.         loadingMoreState = loadingMoreState.LV_NORMAL;// 状态也回归最初状态  
  336.   
  337.         // 执行相应动画.  
  338.         myAsynTask = new MyAsynTask();  
  339.         myAsynTask.execute(DRAG_DOWN);  
  340.     }  
  341.   
  342.     /*** 
  343.      * ListView 滑动监听 
  344.      */  
  345.     @Override  
  346.     public void onScrollStateChanged(AbsListView view, int scrollState) {  
  347.   
  348.     }  
  349.   
  350.     @Override  
  351.     public void onScroll(AbsListView view, int firstVisibleItem,  
  352.             int visibleItemCount, int totalItemCount) {  
  353.         mFirstItemIndex = firstVisibleItem;  
  354.         mLastItemIndex = firstVisibleItem + visibleItemCount;  
  355.   
  356.     }  
  357.   
  358.     @Override  
  359.     public void onClick(View v) {  
  360.   
  361.     }  
  362.   
  363.     /*** 
  364.      * 用于产生动画 
  365.      *  
  366.      * @author zhangjia 
  367.      *  
  368.      */  
  369.     private class MyAsynTask extends AsyncTask<Integer, Integer, Void> {  
  370.         private final static int STEP = 30;// 步伐  
  371.         private final static int TIME = 5;// 休眠时间  
  372.         private int distance;// 距离(该距离指的是:mHeadView的PaddingTop+mHeadView的高度,及默认位置状态.)  
  373.         private int number;// 循环执行次数.  
  374.         private int disPadding;// 时时padding距离.  
  375.         private int DRAG;  
  376.   
  377.         @Override  
  378.         protected Void doInBackground(Integer... params) {  
  379.             try {  
  380.                 this.DRAG = params[0];  
  381.                 if (params[0] == DRAG_UP) {  
  382.                     // 获取距离.  
  383.                     distance = mHeadView.getPaddingTop()  
  384.                             + Math.abs(mHeadViewHeight);  
  385.                 } else {  
  386.                     // 获取距离.  
  387.                     distance = mFootView.getPaddingTop()  
  388.                             + Math.abs(mHeadViewHeight);  
  389.                 }  
  390.   
  391.                 // 获取循环次数.  
  392.                 if (distance % STEP == 0) {  
  393.                     number = distance / STEP;  
  394.                 } else {  
  395.                     number = distance / STEP + 1;  
  396.                 }  
  397.                 // 进行循环.  
  398.                 for (int i = 0; i < number; i++) {  
  399.                     Thread.sleep(TIME);  
  400.                     publishProgress(STEP);  
  401.                 }  
  402.             } catch (InterruptedException e) {  
  403.                 e.printStackTrace();  
  404.             }  
  405.             return null;  
  406.         }  
  407.   
  408.         @Override  
  409.         protected void onProgressUpdate(Integer... values) {  
  410.             super.onProgressUpdate(values);  
  411.   
  412.             switch (DRAG) {  
  413.             case DRAG_UP:  
  414.                 disPadding = Math.max(mHeadView.getPaddingTop() - STEP, -1  
  415.                         * mHeadViewHeight);  
  416.                 mHeadView.setPadding(0, disPadding, 00);// 回归.  
  417.                 break;  
  418.             case DRAG_DOWN:  
  419.                 disPadding = Math.max(mFootView.getPaddingTop() - STEP, -1  
  420.                         * mHeadViewHeight);  
  421.                 mFootView.setPadding(0, disPadding, 00);// 回归.  
  422.                 break;  
  423.             default:  
  424.                 break;  
  425.             }  
  426.   
  427.         }  
  428.   
  429.     }  
  430.   
  431. }  
运行效果:

                             

         默认效果                                             下拉拖拽效果(会自动回缩)               上拉拖拽效果(会自动回缩)           

前面那章实现起来有点小BUG,正在处理,不过这个实现起来没有发现什么BUG,要说BUG的话,那么就是优化,因为我觉得上面效果是实现了,可是性能觉得有点差,比如说“我每次UP的时候要执行任务,那么就要创建任务对象,你想想看,每次执行都要创建,那么要创建多少对象,虽说JAVA虚拟机会自动回收,但是总觉得不是很完善,嗯,临时就如此了,自己在研究研究看.

至于微信,陌陌等大多数应用都是(数据少的话,就上下都可以拖拽,只是一个人性效果,而数据多的话,上部用于加载过时数据.下部只是个形式.),效果实现起来也不难,只是进行了些判断,效果嘛,其实上面自定义ListView整理下就OK了.

上面我详细给出了两个自定义源码的实现,大家可以直接引用.

原创粉丝点击