先上效果图,如果大家感觉不错,请参考实例代码,效果图如下所述:
要实现这个效果有三种方式:
① 手势
② 动画
③ ViewDragHelper
这里我使用的是ViewDragHelper类.
public class ViewDragLayout extends ViewGroup { //垂直方向的滑动速度 private static final int VEL_THRESHOLD = 300; //垂直方向的滑动距离 private static final int DISTANCE_THRESHOLD = 300; //上面可见的View private View mTopView; //下面详情View private View mBottomView; //ViewDragHelper实例 private ViewDragHelper mViewDragHelper; private GestureDetectorCompat mGestureDetectorCompat; private int mFirstHeight; public ViewDragLayout(Context context) { super(context); init(); } public ViewDragLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ViewDragLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public ViewDragLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { mViewDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); mGestureDetectorCompat = new GestureDetectorCompat(getContext(), new YScrollDetector()); } @Override protected void onFinishInflate() { super.onFinishInflate(); mTopView = getChildAt(0); mBottomView = getChildAt(1); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mTopView.getTop() == 0) { mTopView.layout(l, 0, r, b-t ); mBottomView.layout(l, 0, r, b-t ); mFirstHeight = mTopView.getMeasuredHeight(); mBottomView.offsetTopAndBottom(mFirstHeight); }else{ mTopView.layout(l, mTopView.getTop(), r, mTopView.getBottom()); mBottomView.layout(l, mBottomView.getTop(), r, mBottomView.getBottom()); } } @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)); } private class DragHelperCallback extends ViewDragHelper.Callback { @Override public boolean tryCaptureView(View child, int pointerId) { return true; } /** * @param child * @param top * @param dy * @return */ @Override public int clampViewPositionVertical(View child, int top, int dy) { int finalTop=top; if (child == mTopView) { if (top > 0) { finalTop=0; } }else if(child==mBottomView){ if(top<0){ finalTop=0; } } return finalTop; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { if (changedView == mTopView) { mBottomView.offsetTopAndBottom(dy); }else if (changedView==mBottomView){ mTopView.offsetTopAndBottom(dy); } ViewCompat.postInvalidateOnAnimation(ViewDragLayout.this); } /** * * @param releasedChild * @param xvel 水平方向的速度(向右为正) * @param yvel 竖直方向的速度(向下为正) */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { animTopOrBottom(releasedChild, yvel); } } //动画实现滚动 private void animTopOrBottom(View releasedChild, float yvel) { int finalTop=0; if (releasedChild == mTopView) { if (yvel < -VEL_THRESHOLD || (releasedChild.getTop() < -DISTANCE_THRESHOLD)) { finalTop=-mFirstHeight; } } else if (releasedChild == mBottomView) { if (yvel > VEL_THRESHOLD || (releasedChild.getTop() > DISTANCE_THRESHOLD)) { finalTop=mFirstHeight; } } if (mViewDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) { ViewCompat.postInvalidateOnAnimation(this); } } @Override public void computeScroll() { if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } //是否拦截手势操作 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mTopView.getTop() < 0 && mTopView.getBottom() > 0) { return false; } boolean isCanTouch = mGestureDetectorCompat.onTouchEvent(ev); boolean shouldIntercept = mViewDragHelper.shouldInterceptTouchEvent(ev); if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { mViewDragHelper.processTouchEvent(ev); } return isCanTouch&&shouldIntercept; } //将touch事件交给ViewDragHelper处理 @Override public boolean onTouchEvent(MotionEvent event) { mViewDragHelper.processTouchEvent(event); return true; } //垂直方向上才滚动 private class YScrollDetector extends GestureDetector.SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return Math.abs(distanceY) > Math.abs(distanceX); } } }
使用ViewDragLayout
bottom_fragment_view中使用了ScrollView,但是原生是不行的,所以这里我又将ScrollView重写了一下
这里主要是处理dispatchTouchEvent(MotionEvent ev)方法,判断将touch事件交给自己处理还是交给父View处理
public class CustomScrollView extends ScrollView { //滚动临界值 private int mTouchSlop; //获取初始X坐标 private float mRawX; //获取初始Y坐标 private float mRawY; //是否向上滑动 private boolean mCanScrollUp; //是否向下滑动 private boolean mCanScrollDown; public CustomScrollView(Context context) { super(context); init(); } public CustomScrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public CustomScrollView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: mRawX = ev.getRawX(); mRawY = ev.getRawY(); mCanScrollUp = canScrollingUp(); mCanScrollDown = canScrollingDown(); //表示子View要自己消费这次事件,告诉父View不拦截这次的事件。 getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: float xDis = Math.abs(mRawX - ev.getRawX()); float yDis = Math.abs(mRawY - ev.getRawY()); if (yDis > xDis && yDis > mTouchSlop) { if (mRawY < ev.getRawY() && mCanScrollUp) { //表示子View不消费这次事件,告诉父View拦截这次的事件。 getParent().requestDisallowInterceptTouchEvent(false); return false; } if (mRawY > ev.getRawY() && mCanScrollDown) { //表示子View不消费这次事件,告诉父View拦截这次的事件。 getParent().requestDisallowInterceptTouchEvent(false); return false; } } break; } return super.dispatchTouchEvent(ev); } /** * 手指向下滑动(内容向上滑动) * @return */ private boolean canScrollingUp() { if (ViewCompat.canScrollVertically(this, -1)) { return false; } else { return true; } } /** * 手指向上滑动(内容向下滑动) * @return */ private boolean canScrollingDown() { if (ViewCompat.canScrollVertically(this, 1)) { return false; } else { return true; } } }
好了,具体拖拽代码就是这些了,界面我用的两个Fragment,相信大家也看出来了。里面大家换成自己的业务UI就可以了。
以上所述是小编给大家介绍的Android ViewDragHelper实现京东、淘宝拖拽详情功能的实现,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!