您的位置:首页 > 其它

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

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

只支持两页!



Java代码  


import android.annotation.SuppressLint;  
import android.content.Context;  
import android.support.v4.view.GestureDetectorCompat;  
import android.support.v4.view.ViewCompat;  
import android.support.v4.widget.ViewDragHelper;  
import android.util.AttributeSet;  
import android.view.GestureDetector.SimpleOnGestureListener;  
import android.view.MotionEvent;  
import android.view.View;  
import android.view.ViewGroup;  
  
/** 
 * 这是一个viewGroup容器,实现上下两个frameLayout拖动切换 
 *  
 * @author sistone.Zhang 
 */  
@SuppressLint("NewApi")  
public class DragLayout extends ViewGroup {  
  
    /* 拖拽工具类 */  
    private final ViewDragHelper mDragHelper;  
    private GestureDetectorCompat gestureDetector;  
  
    /* 上下两个frameLayout,在Activity中注入fragment */  
    private View frameView1, frameView2;  
    private int viewHeight;  
    private static final int VEL_THRESHOLD = 100; // 滑动速度的阈值,超过这个绝对值认为是上下  
    private static final int DISTANCE_THRESHOLD = 100; // 单位是像素,当上下滑动速度不够时,通过这个阈值来判定是应该粘到顶部还是底部  
    private int downTop1; // 手指按下的时候,frameView1的getTop值  
    private ShowNextPageNotifier nextPageListener; // 手指松开是否加载下一页的notifier  
  
    public DragLayout(Context context) {  
        this(context, null);  
    }  
  
    public DragLayout(Context context, AttributeSet attrs) {  
        this(context, attrs, 0);  
    }  
  
    public DragLayout(Context context, AttributeSet attrs, int defStyle) {  
        super(context, attrs, defStyle);  
        mDragHelper = ViewDragHelper  
                .create(this, 10f, new DragHelperCallback());  
        mDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_BOTTOM);  
        gestureDetector = new GestureDetectorCompat(context,  
                new YScrollDetector());  
    }  
  
    @Override  
    protected void onFinishInflate() {  
        // 跟findviewbyId一样,初始化上下两个view  
        frameView1 = getChildAt(0);  
        frameView2 = getChildAt(1);  
    }  
  
    class YScrollDetector extends SimpleOnGestureListener {  
  
        @Override  
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx,  
                float dy) {  
            // 垂直滑动时dy>dx,才被认定是上下拖动  
            return Math.abs(dy) > Math.abs(dx);  
        }  
    }  
  
    @Override  
    public void computeScroll() {  
        if (mDragHelper.continueSettling(true)) {  
            ViewCompat.postInvalidateOnAnimation(this);  
        }  
    }  
  
    /** 
     * 这是拖拽效果的主要逻辑 
     */  
    private class DragHelperCallback extends ViewDragHelper.Callback {  
  
        @Override  
        public void onViewPositionChanged(View changedView, int left, int top,  
                int dx, int dy) {  
            int childIndex = 1;  
            if (changedView == frameView2) {  
                childIndex = 2;  
            }  
  
            // 一个view位置改变,另一个view的位置要跟进  
            onViewPosChanged(childIndex, top);  
        }  
  
        @Override  
        public boolean tryCaptureView(View child, int pointerId) {  
            // 两个子View都需要跟踪,返回true  
            return true;  
        }  
  
        @Override  
        public int getViewVerticalDragRange(View child) {  
            // 这个用来控制拖拽过程中松手后,自动滑行的速度,暂时给一个随意的数值  
            return 1;  
        }  
  
        @Override  
        public void onViewReleased(View releasedChild, float xvel, float yvel) {  
            // 滑动松开后,需要向上或者乡下粘到特定的位置  
            animTopOrBottom(releasedChild, yvel);  
        }  
  
        @Override  
        public int clampViewPositionVertical(View child, int top, int dy) {  
            int finalTop = top;  
            if (child == frameView1) {  
                // 拖动的时第一个view  
                if (top > 0) {  
                    // 不让第一个view往下拖,因为顶部会白板  
                    finalTop = 0;  
                }  
            } else if (child == frameView2) {  
                // 拖动的时第二个view  
                if (top < 0) {  
                    // 不让第二个view网上拖,因为底部会白板  
                    finalTop = 0;  
                }  
            }  
  
            // finalTop代表的是理论上应该拖动到的位置。此处计算拖动的距离除以一个参数(3),是让滑动的速度变慢。数值越大,滑动的越慢  
            return child.getTop() + (finalTop - child.getTop()) / 3;  
        }  
    }  
  
    /** 
     * 滑动时view位置改变协调处理 
     *  
     * @param viewIndex 
     *            滑动view的index(1或2) 
     * @param posTop 
     *            滑动View的top位置 
     */  
    private void onViewPosChanged(int viewIndex, int posTop) {  
        if (viewIndex == 1) {  
            int offsetTopBottom = viewHeight + frameView1.getTop()  
                    - frameView2.getTop();  
            frameView2.offsetTopAndBottom(offsetTopBottom);  
        } else if (viewIndex == 2) {  
            int offsetTopBottom = frameView2.getTop() - viewHeight  
                    - frameView1.getTop();  
            frameView1.offsetTopAndBottom(offsetTopBottom);  
        }  
  
        // 有的时候会默认白板,这个很恶心。后面有时间再优化  
        invalidate();  
    }  
  
    private void animTopOrBottom(View releasedChild, float yvel) {  
        int finalTop = 0; // 默认是粘到最顶端  
        if (releasedChild == frameView1) {  
            // 拖动第一个view松手  
            if (yvel < -VEL_THRESHOLD  
                    || (downTop1 == 0 && frameView1.getTop() < -DISTANCE_THRESHOLD)) {  
                // 向上的速度足够大,就滑动到顶端  
                // 向上滑动的距离超过某个阈值,就滑动到顶端  
                finalTop = -viewHeight;  
  
                // 下一页可以初始化了  
                if (null != nextPageListener) {  
                    nextPageListener.onDragNext();  
                }  
            }  
        } else {  
            // 拖动第二个view松手  
            if (yvel > VEL_THRESHOLD  
                    || (downTop1 == -viewHeight && releasedChild.getTop() > DISTANCE_THRESHOLD)) {  
                // 保持原地不动  
                finalTop = viewHeight;  
            }  
        }  
  
        if (mDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) {  
            ViewCompat.postInvalidateOnAnimation(this);  
        }  
    }  
  
    /* touch事件的拦截与处理都交给mDraghelper来处理 */  
    @Override  
    public boolean onInterceptTouchEvent(MotionEvent ev) {  
  
        if (frameView1.getBottom() > 0 && frameView1.getTop() < 0) {  
            // view粘到顶部或底部,正在动画中的时候,不处理touch事件  
            return false;  
        }  
  
        boolean yScroll = gestureDetector.onTouchEvent(ev);  
        boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev);  
        int action = ev.getActionMasked();  
  
        if (action == MotionEvent.ACTION_DOWN) {  
            // action_down时就让mDragHelper开始工作,否则有时候导致异常 他大爷的  
            mDragHelper.processTouchEvent(ev);  
            downTop1 = frameView1.getTop();  
        }  
  
        return shouldIntercept && yScroll;  
    }  
  
    @Override  
    public boolean onTouchEvent(MotionEvent e) {  
        // 统一交给mDragHelper处理,由DragHelperCallback实现拖动效果  
        mDragHelper.processTouchEvent(e); // 该行代码可能会抛异常,正式发布时请将这行代码加上try catch  
        return true;  
    }  
  
    @Override  
    protected void onLayout(boolean changed, int l, int t, int r, int b) {  
        // 只在初始化的时候调用  
        // 一些参数作为全局变量保存起来  
        frameView1.layout(l, 0, r, b - t);  
        frameView2.layout(l, 0, r, b - t);  
  
        viewHeight = frameView1.getMeasuredHeight();  
        frameView2.offsetTopAndBottom(viewHeight);  
    }  
  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        measureChildren(widthMeasureSpec, heightMeasureSpec);  
  
        int maxWidth = MeasureSpec.getSize(widthMeasureSpec);  
        int maxHeight = MeasureSpec.getSize(heightMeasureSpec);  
        setMeasuredDimension(  
                resolveSizeAndState(maxWidth, widthMeasureSpec, 0),  
                resolveSizeAndState(maxHeight, heightMeasureSpec, 0));  
    }  
  
    /** 
     * 这是View的方法,该方法不支持android低版本(2.2、2.3)的操作系统,所以手动复制过来以免强制退出 
     */  
    public static int resolveSizeAndState(int size, int measureSpec,  
            int childMeasuredState) {  
        int result = size;  
        int specMode = MeasureSpec.getMode(measureSpec);  
        int specSize = MeasureSpec.getSize(measureSpec);  
        switch (specMode) {  
        case MeasureSpec.UNSPECIFIED:  
            result = size;  
            break;  
        case MeasureSpec.AT_MOST:  
            if (specSize < size) {  
                result = specSize | MEASURED_STATE_TOO_SMALL;  
            } else {  
                result = size;  
            }  
            break;  
        case MeasureSpec.EXACTLY:  
            result = specSize;  
            break;  
        }  
        return result | (childMeasuredState & MEASURED_STATE_MASK);  
    }  
  
    public void setNextPageListener(ShowNextPageNotifier nextPageListener) {  
        this.nextPageListener = nextPageListener;  
    }  
  
    public interface ShowNextPageNotifier {  
        public void onDragNext();  
    }  
}  

用法:

Java代码  


private void initView() {  
        fragment1 = new VerticalFragment1();  
        fragment2 = new VerticalFragment2();  
//      fragment3 = new VerticalFragment3();  
  
        getSupportFragmentManager().beginTransaction()  
                .add(R.id.first, fragment1)  
                .add(R.id.second, fragment2)  
//              .add(R.id.second, fragment3)//只支持两页  
                .commit();  
  
        ShowNextPageNotifier nextIntf = new ShowNextPageNotifier() {  
            @Override  
            public void onDragNext() {  
//              fragment3.initView();  
            }  
        };  
        draglayout = (DragLayout) findViewById(R.id.draglayout);  
        draglayout.setNextPageListener(nextIntf);  
    }  

布局:

Xml代码  


<com.stone.verticalslide.DragLayout  
        android:id="@+id/draglayout"  
        android:layout_width="match_parent"  
        android:layout_height="match_parent"  
        >  
  
        <FrameLayout  
            android:id="@+id/first"  
            android:layout_width="fill_parent"  
            android:layout_height="fill_parent" />  
  
        <FrameLayout  
            android:id="@+id/second"  
            android:layout_width="fill_parent"  
            android:layout_height="fill_parent" />  
    </com.stone.verticalslide.DragLayout>  

////////////////////////////////////////////////////////////////////

这是一个多功能的扩展GridView 可展开,可拖动,可排序,可删除。 固定更多按钮。 展开合并支持动画。 支持箭头图标移动。 数据的处理和显示使用Bean。 来自于500彩票Andorid客户端首页功能。
http://www.jcodecraeer.com/a/opensource/2015/0827/3376.html

android-vertical-slide-view-master.zip (2.6 MB)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐