Android 进阶学习(二十九) BottomSheetDialog 改造(二) 去除中间折叠状态

在第二十五篇博客中,通过重写BottomSheetDialog 给他添加了一个布局,同时也给顶部添加了一个TopMargin ,但是在使用过程中产品对于现阶段的BottomSheetDialog 的表现形式并不是非常满意,这里我先把已经添加底部固定区域的的BottomSheetDialog 的图片上一下,


GIF 2021-6-9 17-00-38.gif

本期,我们继续对这个dialog做一下修改,
先来说一下我们的问题都有哪些,

  1. 由于给BottomSheetDialog 添加了一个顶部的margin,导致在BottomSheetDialog 完全展开时,点击顶部透明区域时,dialog并不会收缩
    2.产品在看了原生BottomSheetDialog 的展示形式时,感觉类似抖音之类的展示形式比较好,也就是整个BottomSheetDialog 除了展开就是隐藏,不想要中间的那个折叠状态,我们上下最终的效果图
GIF 2021-7-26 19-06-54.gif

由于修改这个代码比较多,所以还是分享一个github 的地址吧,这样方便大家使用
https://github.com/tsm1991/TsmBottomSheetDialog

1.点击外部不能消失的问题

先来修改第一个问题,想要解决外部不能点击的问题很简单,我们只需要让最外层的View 可以点击就好了,



/////  这里增加可以点击,同时在dialog中添加点击事件即可
  

      

      
  
  
  

2.第二个问题就比较严重了,由于 BottomSheetDialog 很多信息我们在外界都获取不到,想要实现上述所说的问题就必须要重写BottomSheetBehavior , 并修改他的代码,我们这里就先看看我们需要改什么吧,

public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) {
    
   ... 省略部分代码

   if (state == STATE_EXPANDED) {
    ViewCompat.offsetTopAndBottom(child, getExpandedOffset());
  } else if (state == STATE_HALF_EXPANDED) {
    ViewCompat.offsetTopAndBottom(child, halfExpandedOffset);
  } else if (hideable && state == STATE_HIDDEN) {
    ViewCompat.offsetTopAndBottom(child, parentHeight);
  } else if (state == STATE_COLLAPSED) {
    ViewCompat.offsetTopAndBottom(child, collapsedOffset);
  } else if (state == STATE_DRAGGING || state == STATE_SETTLING) {
    ViewCompat.offsetTopAndBottom(child, savedTop - child.getTop());
  }
  ...省略部分代码
  }

onLayoutChild 这个方法是在布局完成后调用,可以看到在布局完成后他是根据不同的状态来确定展示形式,而我们的需求比较粗暴,无非就是全部展开或者消失,这里就直接变成

public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) {
    
   ... 省略部分代码

ViewCompat.offsetTopAndBottom(child, getExpandedOffset());
  ...省略部分代码
  }

全部都是完全展开的形式,

这里处理完就剩下一个拖动事件了,由于他是使用了NestScroll 等一系列的事件分发的方式,我们只需要关注最后一帧的滑动事件就好了,所以这里需要看一下onStopNestedScroll 这个方法

public void onStopNestedScroll(
    @NonNull CoordinatorLayout coordinatorLayout,
    @NonNull V child,
    @NonNull View target,
    int type) {
  if (child.getTop() == getExpandedOffset()) {///如果是展开,那么状态就是3
    setStateInternal(STATE_EXPANDED);
    return;
  }
  if (nestedScrollingChildRef == null
      || target != nestedScrollingChildRef.get()
      || !nestedScrolled) 
    return;
  }
  int top;
  int targetState;
  if (lastNestedScrollDy > 0) {
    if (fitToContents) {
      top = fitToContentsOffset;
      targetState = STATE_EXPANDED;
    } else {
      int currentTop = child.getTop();
      if (currentTop > halfExpandedOffset) {
        top = halfExpandedOffset;
        targetState = STATE_HALF_EXPANDED;
      } else {
        top = expandedOffset;
        targetState = STATE_EXPANDED;
      }
    }
  } else if (hideable && shouldHide(child, getYVelocity())) {
    top = parentHeight;
    targetState = STATE_HIDDEN;
  } else if (lastNestedScrollDy == 0) {
    int currentTop = child.getTop();
    if (fitToContents) {
      if (Math.abs(currentTop - fitToContentsOffset) < Math.abs(currentTop - collapsedOffset)) {
        top = fitToContentsOffset;
        targetState = STATE_EXPANDED;
      } else {
        top = collapsedOffset;
        targetState = STATE_COLLAPSED;
      }
    } else {
      if (currentTop < halfExpandedOffset) {
        if (currentTop < Math.abs(currentTop - collapsedOffset)) {
          top = expandedOffset;
          targetState = STATE_EXPANDED;
        } else {
          top = halfExpandedOffset;
          targetState = STATE_HALF_EXPANDED;
        }
      } else {
        if (Math.abs(currentTop - halfExpandedOffset) < Math.abs(currentTop - collapsedOffset)) {
          top = halfExpandedOffset;
          targetState = STATE_HALF_EXPANDED;
        } else {
          top = collapsedOffset;
          targetState = STATE_COLLAPSED;
        }
      }
    }
  } else {
    if (fitToContents) {
      top = collapsedOffset;
      targetState = STATE_COLLAPSED;
    } else {
      // Settle to nearest height.
      int currentTop = child.getTop();
      if (Math.abs(currentTop - halfExpandedOffset) < Math.abs(currentTop - collapsedOffset)) {
        top = halfExpandedOffset;
        targetState = STATE_HALF_EXPANDED;
      } else {
        top = collapsedOffset;
        targetState = STATE_COLLAPSED;
      }
    }
  }
  startSettlingAnimation(child, targetState, top, false);
  nestedScrolled = false;
}

其实这里很多属性我也不是很了解,但是我能看一下大概,具体修改后的代码如下,

  public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int type) {
      if (child.getTop() == this.getExpandedOffset()) {
          this.setStateInternal(3);
      } else if (this.nestedScrollingChildRef != null && target == this.nestedScrollingChildRef.get() && this.nestedScrolled) {
          int top;
          byte targetState;
          if (this.lastNestedScrollDy > 0) {
              top = this.getExpandedOffset();
              targetState = 3;
          } else if (this.hideable && this.shouldHide(child, this.getYVelocity())) {
              top = this.parentHeight;
              targetState = 5;
          } else {
              int currentTop;
              if (this.lastNestedScrollDy == 0) {//最后一帧没有滑动
                  currentTop = child.getTop();
                  if (currentTop < this.halfExpandedOffset){ //小于折叠高度  那么就收缩
                      targetState = STATE_HIDDEN;
                      top = this.parentHeight;
                  }else{//其他展开
                      top = this.getExpandedOffset();
                      targetState = 3;
                  }
              }else {
                  currentTop = child.getTop();
                  ///大于折叠高度,并小于最大高度展开
                  if (Math.abs(currentTop - this.halfExpandedOffset) < Math.abs(currentTop - this.collapsedOffset)) {
                      top = this.getExpandedOffset();
                      targetState = 3;
                  } else {
                      top = this.parentHeight;///收起
                      targetState = 5;
                  }
              }
          }
          this.startSettlingAnimation(child, targetState, top, false);
          this.nestedScrolled = false;
      }
  }

去除了很多中间的状态的代码,反而看起来更简单了, 这里是嵌套滑动的整改,如果没有嵌套滑动,则调用的是onViewReleased 这个方法,所以对这个方法我们也要修改



      public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
          int top;
          byte targetState;
          int currentTop;
          if (yvel < 0.0F) {
              if (TsmBottomSheetBehavior.this.fitToContents) {
                  top = TsmBottomSheetBehavior.this.fitToContentsOffset;
                  targetState = STATE_EXPANDED;
              } else {
                  currentTop = releasedChild.getTop();
                  if (currentTop > TsmBottomSheetBehavior.this.halfExpandedOffset) {
                      top = TsmBottomSheetBehavior.this.halfExpandedOffset;
                      targetState = STATE_EXPANDED;
                  } else {
                      top = TsmBottomSheetBehavior.this.expandedOffset;
                      targetState = STATE_EXPANDED;
                  }
              }
          } else if (TsmBottomSheetBehavior.this.hideable && TsmBottomSheetBehavior.this.shouldHide(releasedChild, yvel) && (releasedChild.getTop() > TsmBottomSheetBehavior.this.collapsedOffset || Math.abs(xvel) < Math.abs(yvel))) {
              top = TsmBottomSheetBehavior.this.parentHeight;
              targetState = STATE_HIDDEN;
          } else if (yvel != 0.0F && Math.abs(xvel) <= Math.abs(yvel)) {
              if (TsmBottomSheetBehavior.this.fitToContents) {
                  top = TsmBottomSheetBehavior.this.parentHeight;
                  targetState = STATE_HIDDEN;
              } else {
                  currentTop = releasedChild.getTop();
                  if (Math.abs(currentTop - TsmBottomSheetBehavior.this.halfExpandedOffset) < Math.abs(currentTop - TsmBottomSheetBehavior.this.collapsedOffset)) {
                      top = TsmBottomSheetBehavior.this.expandedOffset;
                      targetState = STATE_EXPANDED;
                  } else {
                      top = TsmBottomSheetBehavior.this.parentHeight;
                      targetState = STATE_HIDDEN;
                  }
              }
          } else {
              currentTop = releasedChild.getTop();
              if (TsmBottomSheetBehavior.this.fitToContents) {
                  if (Math.abs(currentTop - TsmBottomSheetBehavior.this.fitToContentsOffset) < Math.abs(currentTop - TsmBottomSheetBehavior.this.collapsedOffset)) {
                      top = TsmBottomSheetBehavior.this.fitToContentsOffset;
                      targetState = STATE_EXPANDED;
                  } else {
                      top = TsmBottomSheetBehavior.this.parentHeight;
                      targetState = STATE_HIDDEN;
                  }
              } else if (currentTop < TsmBottomSheetBehavior.this.halfExpandedOffset) {
                  if (currentTop < Math.abs(currentTop - TsmBottomSheetBehavior.this.collapsedOffset)) {
                      top = TsmBottomSheetBehavior.this.expandedOffset;
                      targetState = STATE_EXPANDED;
                  } else {
                      top = TsmBottomSheetBehavior.this.parentHeight;
                      targetState = STATE_HIDDEN;
                  }
              } else if (Math.abs(currentTop - TsmBottomSheetBehavior.this.halfExpandedOffset) < Math.abs(currentTop - TsmBottomSheetBehavior.this.collapsedOffset)) {
                  top = TsmBottomSheetBehavior.this.parentHeight;
                  targetState = STATE_HIDDEN;
              } else {
                  top = TsmBottomSheetBehavior.this.parentHeight;
                  targetState = STATE_HIDDEN;
              }
          }
          TsmBottomSheetBehavior.this.startSettlingAnimation(releasedChild, targetState, top, true);
      }

}

最后结合第二十五篇的BottomSheetDialog,就可以使用了

你可能感兴趣的:(Android 进阶学习(二十九) BottomSheetDialog 改造(二) 去除中间折叠状态)