美文网首页Android 进阶之旅
Android 进阶学习(二十九) BottomSheetD

Android 进阶学习(二十九) BottomSheetD

作者: Tsm_2020 | 来源:发表于2021-07-26 15:25 被阅读0次

在第二十五篇博客中,通过重写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 可以点击就好了,

<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2015 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~      http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<FrameLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:fitsSystemWindows="true"
  android:clickable="true">/////  这里增加可以点击,同时在dialog中添加点击事件即可
  <androidx.coordinatorlayout.widget.CoordinatorLayout
      android:id="@+id/coordinator"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:fitsSystemWindows="true">

      <View
          android:id="@+id/touch_outside"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:importantForAccessibility="no"
          android:soundEffectsEnabled="false"
          tools:ignore="UnusedAttribute"/>

      <FrameLayout
          android:id="@+id/design_bottom_sheet"
          style="?attr/bottomSheetStyle"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_gravity="center_horizontal|top"
          app:layout_behavior="com.tsm.tsmmodelapp.behavior.TsmBottomSheetBehavior"/>
  </androidx.coordinatorlayout.widget.CoordinatorLayout>
  <FrameLayout
      android:id="@+id/bottom_design_bottom_sheet"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="bottom"
      android:clickable="true" ///这里的作用是屏蔽底部View 的点击事件,不让他传递到下一层
      android:background="#ffffff"
      >
  </FrameLayout>
</FrameLayout>

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 进阶学习(二十九) BottomSheetD

    本文链接:https://www.haomeiwen.com/subject/kwnwmltx.html