1.先看效果图:
仿汽车之家折叠列表.gif2.效果分析
2.1.后面的布局不能拖动
2.2.前面的只能垂直拖动
//1.拖动我们的子view
private ViewDragHelper.Callback mViewDragHelperCallback = new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(@NonNull View child, int pointerId) {
//在这里可以指定子 view 是否可以拖动
//只能是前面的布局才能滑动
return mDragListView == child;
}
@Override
public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
//垂直拖动的滑动距离
Log.d("TAG","top="+top);
if(top < 0){
top = 0;
}
if(top > mInnerViewheight){
top = mInnerViewheight;
}
return top;
}
};
2.3.垂直拖动的范围只能是后面菜单的高度
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if(changed){
mInnerViewheight = getChildAt(0).getMeasuredHeight();
}
}
2.4.松开的时候只能打开或是关闭
/**
* 拖动松开的时候
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
if(releasedChild == mDragContentView) {
if (mDragContentView.getTop() > mInnerViewHeight / 2) {
//滚动到菜单的高度
mViewDragHelper.settleCapturedViewAt(0, mInnerViewHeight);
} else {
//恢复
mViewDragHelper.settleCapturedViewAt(0, 0);
}
invalidate();
}
}
//相应滚动
@Override
public void computeScroll() {
super.computeScroll();
if(mViewDragHelper.continueSettling(true)){
invalidate();
}
}
3.内容页是滑动控件
比如是 ListView 解决滑动冲突
判断是否是ListView
/*
判断是否还能不能向上滚动 源码 来自于 SwipeRefreshLayout
*/
private boolean canChildScrollUp() {
if (mDragContentView instanceof ListView) {
return canScrollList((ListView) mDragContentView, -1);
}
return false;
}
private boolean canScrollList(ListView listView, int direction) {
if (Build.VERSION.SDK_INT >= 19) {
// Call the framework version directly
return listView.canScrollList(direction);
} else {
// provide backport on earlier versions
final int childCount = listView.getChildCount();
if (childCount == 0) {
return false;
}
final int firstPosition = listView.getFirstVisiblePosition();
if (direction > 0) {
final int lastBottom = listView.getChildAt(childCount - 1).getBottom();
final int lastPosition = firstPosition + childCount;
return lastPosition < listView.getCount()
|| (lastBottom > listView.getHeight() - listView.getListPaddingBottom());
} else {
final int firstTop = listView.getChildAt(0).getTop();
return firstPosition > 0 || firstTop < listView.getListPaddingTop();
}
}
}
如果是ListView,在判断是否滑动到顶部的时候 需要做拦截事件
case MotionEvent.ACTION_MOVE:
float moveY = ev.getY();
Log.d("TAG","moveY="+moveY+" mDownY="+mDownY);
if(moveY - mDownY > 0 && !canChildScrollUp()){
//向下滑动 && 滚动到顶部 拦截 不让ListView做处理
return true;
}
break;
其他滑动冲突说明:
//现象:如果contentView是ListView listView可以滑动 但是 拖动效果没有了 所以要解决事件冲突
//because ACTION_DOWN was not received for this pointer before ACTION_MOVE
//出现以上的原因:
//流程分析:VerticalDragListLayout.onInterceptTouchEvent.DOWN -> ListView.OnTouch()->
//VerticalDragListLayout.onInterceptTouchEvent.MOVE - > VerticalDragListLayout.OnTouchEvent.MOVE
//看到上面的流程可以看到 ListView的DOWN事件被拦截了 因为重写了 onTouchEvent return true了
完整的事件拦截代码:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//如果 菜单 是打开的 则全部拦截
if(mInnerViewIsOpen){
return true;
}
//向下滑动的时候 不要给ListView做处理
//父类拦截子类
//requestDisallowInterceptTouchEvent 请求父类不要拦截子类的事件 也就是不需要修改mGroupFlags的值
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mDownY = ev.getY();
//所以我们要在这里 给DragHelper一个完整的事件
mViewDragHelper.processTouchEvent(ev);
break;
case MotionEvent.ACTION_MOVE:
float moveY = ev.getY();
Log.d("TAG","moveY="+moveY+" mDownY="+mDownY);
if(moveY - mDownY > 0 && !canChildScrollUp()){
//向下滑动 && 滚动到顶部 拦截 不让ListView做处理
return true;
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onInterceptTouchEvent(ev);
}
到此已经完毕
4.源码地址
https://github.com/panshimu/VerticalDragListLayout
有问题随时留言
QQ 362976241
谢谢!
网友评论