向上拖动时,可以惯性滑动显示到下一页的控件DragLayout

仿照淘宝和聚美优品,在商品详情页,向上拖动时,可以加载下一页。使用ViewDragHelper,滑动比较流畅。 scrollView滑动到底部的时候,再行向上拖动时,添加了一些阻力。

只支持两页!




Java代码   收藏代码
  1. import android.annotation.SuppressLint;  
  2. import android.content.Context;  
  3. import android.support.v4.view.GestureDetectorCompat;  
  4. import android.support.v4.view.ViewCompat;  
  5. import android.support.v4.widget.ViewDragHelper;  
  6. import android.util.AttributeSet;  
  7. import android.view.GestureDetector.SimpleOnGestureListener;  
  8. import android.view.MotionEvent;  
  9. import android.view.View;  
  10. import android.view.ViewGroup;  
  11.   
  12. /** 
  13.  * 这是一个viewGroup容器,实现上下两个frameLayout拖动切换 
  14.  *  
  15.  * @author sistone.Zhang 
  16.  */  
  17. @SuppressLint("NewApi")  
  18. public class DragLayout extends ViewGroup {  
  19.   
  20.     /* 拖拽工具类 */  
  21.     private final ViewDragHelper mDragHelper;  
  22.     private GestureDetectorCompat gestureDetector;  
  23.   
  24.     /* 上下两个frameLayout,在Activity中注入fragment */  
  25.     private View frameView1, frameView2;  
  26.     private int viewHeight;  
  27.     private static final int VEL_THRESHOLD = 100// 滑动速度的阈值,超过这个绝对值认为是上下  
  28.     private static final int DISTANCE_THRESHOLD = 100// 单位是像素,当上下滑动速度不够时,通过这个阈值来判定是应该粘到顶部还是底部  
  29.     private int downTop1; // 手指按下的时候,frameView1的getTop值  
  30.     private ShowNextPageNotifier nextPageListener; // 手指松开是否加载下一页的notifier  
  31.   
  32.     public DragLayout(Context context) {  
  33.         this(context, null);  
  34.     }  
  35.   
  36.     public DragLayout(Context context, AttributeSet attrs) {  
  37.         this(context, attrs, 0);  
  38.     }  
  39.   
  40.     public DragLayout(Context context, AttributeSet attrs, int defStyle) {  
  41.         super(context, attrs, defStyle);  
  42.         mDragHelper = ViewDragHelper  
  43.                 .create(this, 10f, new DragHelperCallback());  
  44.         mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_BOTTOM);  
  45.         gestureDetector = new GestureDetectorCompat(context,  
  46.                 new YScrollDetector());  
  47.     }  
  48.   
  49.     @Override  
  50.     protected void onFinishInflate() {  
  51.         // 跟findviewbyId一样,初始化上下两个view  
  52.         frameView1 = getChildAt(0);  
  53.         frameView2 = getChildAt(1);  
  54.     }  
  55.   
  56.     class YScrollDetector extends SimpleOnGestureListener {  
  57.   
  58.         @Override  
  59.         public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx,  
  60.                 float dy) {  
  61.             // 垂直滑动时dy>dx,才被认定是上下拖动  
  62.             return Math.abs(dy) > Math.abs(dx);  
  63.         }  
  64.     }  
  65.   
  66.     @Override  
  67.     public void computeScroll() {  
  68.         if (mDragHelper.continueSettling(true)) {  
  69.             ViewCompat.postInvalidateOnAnimation(this);  
  70.         }  
  71.     }  
  72.   
  73.     /** 
  74.      * 这是拖拽效果的主要逻辑 
  75.      */  
  76.     private class DragHelperCallback extends ViewDragHelper.Callback {  
  77.   
  78.         @Override  
  79.         public void onViewPositionChanged(View changedView, int left, int top,  
  80.                 int dx, int dy) {  
  81.             int childIndex = 1;  
  82.             if (changedView == frameView2) {  
  83.                 childIndex = 2;  
  84.             }  
  85.   
  86.             // 一个view位置改变,另一个view的位置要跟进  
  87.             onViewPosChanged(childIndex, top);  
  88.         }  
  89.   
  90.         @Override  
  91.         public boolean tryCaptureView(View child, int pointerId) {  
  92.             // 两个子View都需要跟踪,返回true  
  93.             return true;  
  94.         }  
  95.   
  96.         @Override  
  97.         public int getViewVerticalDragRange(View child) {  
  98.             // 这个用来控制拖拽过程中松手后,自动滑行的速度,暂时给一个随意的数值  
  99.             return 1;  
  100.         }  
  101.   
  102.         @Override  
  103.         public void onViewReleased(View releasedChild, float xvel, float yvel) {  
  104.             // 滑动松开后,需要向上或者乡下粘到特定的位置  
  105.             animTopOrBottom(releasedChild, yvel);  
  106.         }  
  107.   
  108.         @Override  
  109.         public int clampViewPositionVertical(View child, int top, int dy) {  
  110.             int finalTop = top;  
  111.             if (child == frameView1) {  
  112.                 // 拖动的时第一个view  
  113.                 if (top > 0) {  
  114.                     // 不让第一个view往下拖,因为顶部会白板  
  115.                     finalTop = 0;  
  116.                 }  
  117.             } else if (child == frameView2) {  
  118.                 // 拖动的时第二个view  
  119.                 if (top < 0) {  
  120.                     // 不让第二个view网上拖,因为底部会白板  
  121.                     finalTop = 0;  
  122.                 }  
  123.             }  
  124.   
  125.             // finalTop代表的是理论上应该拖动到的位置。此处计算拖动的距离除以一个参数(3),是让滑动的速度变慢。数值越大,滑动的越慢  
  126.             return child.getTop() + (finalTop - child.getTop()) / 3;  
  127.         }  
  128.     }  
  129.   
  130.     /** 
  131.      * 滑动时view位置改变协调处理 
  132.      *  
  133.      * @param viewIndex 
  134.      *            滑动view的index(1或2) 
  135.      * @param posTop 
  136.      *            滑动View的top位置 
  137.      */  
  138.     private void onViewPosChanged(int viewIndex, int posTop) {  
  139.         if (viewIndex == 1) {  
  140.             int offsetTopBottom = viewHeight + frameView1.getTop()  
  141.                     - frameView2.getTop();  
  142.             frameView2.offsetTopAndBottom(offsetTopBottom);  
  143.         } else if (viewIndex == 2) {  
  144.             int offsetTopBottom = frameView2.getTop() - viewHeight  
  145.                     - frameView1.getTop();  
  146.             frameView1.offsetTopAndBottom(offsetTopBottom);  
  147.         }  
  148.   
  149.         // 有的时候会默认白板,这个很恶心。后面有时间再优化  
  150.         invalidate();  
  151.     }  
  152.   
  153.     private void animTopOrBottom(View releasedChild, float yvel) {  
  154.         int finalTop = 0// 默认是粘到最顶端  
  155.         if (releasedChild == frameView1) {  
  156.             // 拖动第一个view松手  
  157.             if (yvel < -VEL_THRESHOLD  
  158.                     || (downTop1 == 0 && frameView1.getTop() < -DISTANCE_THRESHOLD)) {  
  159.                 // 向上的速度足够大,就滑动到顶端  
  160.                 // 向上滑动的距离超过某个阈值,就滑动到顶端  
  161.                 finalTop = -viewHeight;  
  162.   
  163.                 // 下一页可以初始化了  
  164.                 if (null != nextPageListener) {  
  165.                     nextPageListener.onDragNext();  
  166.                 }  
  167.             }  
  168.         } else {  
  169.             // 拖动第二个view松手  
  170.             if (yvel > VEL_THRESHOLD  
  171.                     || (downTop1 == -viewHeight && releasedChild.getTop() > DISTANCE_THRESHOLD)) {  
  172.                 // 保持原地不动  
  173.                 finalTop = viewHeight;  
  174.             }  
  175.         }  
  176.   
  177.         if (mDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) {  
  178.             ViewCompat.postInvalidateOnAnimation(this);  
  179.         }  
  180.     }  
  181.   
  182.     /* touch事件的拦截与处理都交给mDraghelper来处理 */  
  183.     @Override  
  184.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  185.   
  186.         if (frameView1.getBottom() > 0 && frameView1.getTop() < 0) {  
  187.             // view粘到顶部或底部,正在动画中的时候,不处理touch事件  
  188.             return false;  
  189.         }  
  190.   
  191.         boolean yScroll = gestureDetector.onTouchEvent(ev);  
  192.         boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev);  
  193.         int action = ev.getActionMasked();  
  194.   
  195.         if (action == MotionEvent.ACTION_DOWN) {  
  196.             // action_down时就让mDragHelper开始工作,否则有时候导致异常 他大爷的  
  197.             mDragHelper.processTouchEvent(ev);  
  198.             downTop1 = frameView1.getTop();  
  199.         }  
  200.   
  201.         return shouldIntercept && yScroll;  
  202.     }  
  203.   
  204.     @Override  
  205.     public boolean onTouchEvent(MotionEvent e) {  
  206.         // 统一交给mDragHelper处理,由DragHelperCallback实现拖动效果  
  207.         mDragHelper.processTouchEvent(e); // 该行代码可能会抛异常,正式发布时请将这行代码加上try catch  
  208.         return true;  
  209.     }  
  210.   
  211.     @Override  
  212.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  213.         // 只在初始化的时候调用  
  214.         // 一些参数作为全局变量保存起来  
  215.         frameView1.layout(l, 0, r, b - t);  
  216.         frameView2.layout(l, 0, r, b - t);  
  217.   
  218.         viewHeight = frameView1.getMeasuredHeight();  
  219.         frameView2.offsetTopAndBottom(viewHeight);  
  220.     }  
  221.   
  222.     @Override  
  223.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  224.         measureChildren(widthMeasureSpec, heightMeasureSpec);  
  225.   
  226.         int maxWidth = MeasureSpec.getSize(widthMeasureSpec);  
  227.         int maxHeight = MeasureSpec.getSize(heightMeasureSpec);  
  228.         setMeasuredDimension(  
  229.                 resolveSizeAndState(maxWidth, widthMeasureSpec, 0),  
  230.                 resolveSizeAndState(maxHeight, heightMeasureSpec, 0));  
  231.     }  
  232.   
  233.     /** 
  234.      * 这是View的方法,该方法不支持android低版本(2.2、2.3)的操作系统,所以手动复制过来以免强制退出 
  235.      */  
  236.     public static int resolveSizeAndState(int size, int measureSpec,  
  237.             int childMeasuredState) {  
  238.         int result = size;  
  239.         int specMode = MeasureSpec.getMode(measureSpec);  
  240.         int specSize = MeasureSpec.getSize(measureSpec);  
  241.         switch (specMode) {  
  242.         case MeasureSpec.UNSPECIFIED:  
  243.             result = size;  
  244.             break;  
  245.         case MeasureSpec.AT_MOST:  
  246.             if (specSize < size) {  
  247.                 result = specSize | MEASURED_STATE_TOO_SMALL;  
  248.             } else {  
  249.                 result = size;  
  250.             }  
  251.             break;  
  252.         case MeasureSpec.EXACTLY:  
  253.             result = specSize;  
  254.             break;  
  255.         }  
  256.         return result | (childMeasuredState & MEASURED_STATE_MASK);  
  257.     }  
  258.   
  259.     public void setNextPageListener(ShowNextPageNotifier nextPageListener) {  
  260.         this.nextPageListener = nextPageListener;  
  261.     }  
  262.   
  263.     public interface ShowNextPageNotifier {  
  264.         public void onDragNext();  
  265.     }  
  266. }  


用法:
Java代码   收藏代码
  1. private void initView() {  
  2.         fragment1 = new VerticalFragment1();  
  3.         fragment2 = new VerticalFragment2();  
  4. //      fragment3 = new VerticalFragment3();  
  5.   
  6.         getSupportFragmentManager().beginTransaction()  
  7.                 .add(R.id.first, fragment1)  
  8.                 .add(R.id.second, fragment2)  
  9. //              .add(R.id.second, fragment3)//只支持两页  
  10.                 .commit();  
  11.   
  12.         ShowNextPageNotifier nextIntf = new ShowNextPageNotifier() {  
  13.             @Override  
  14.             public void onDragNext() {  
  15. //              fragment3.initView();  
  16.             }  
  17.         };  
  18.         draglayout = (DragLayout) findViewById(R.id.draglayout);  
  19.         draglayout.setNextPageListener(nextIntf);  
  20.     }  


布局:
Xml代码   收藏代码
  1. <com.stone.verticalslide.DragLayout  
  2.         android:id="@+id/draglayout"  
  3.         android:layout_width="match_parent"  
  4.         android:layout_height="match_parent"  
  5.         >  
  6.   
  7.         <FrameLayout  
  8.             android:id="@+id/first"  
  9.             android:layout_width="fill_parent"  
  10.             android:layout_height="fill_parent" />  
  11.   
  12.         <FrameLayout  
  13.             android:id="@+id/second"  
  14.             android:layout_width="fill_parent"  
  15.             android:layout_height="fill_parent" />  
  16.     </com.stone.verticalslide.DragLayout>  



这是一个多功能的扩展GridView 可展开,可拖动,可排序,可删除。 固定更多按钮。 展开合并支持动画。 支持箭头图标移动。 数据的处理和显示使用Bean。 来自于500彩票Andorid客户端首页功能。
http://www.jcodecraeer.com/a/opensource/2015/0827/3376.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值