在第二十五篇博客中,通过重写BottomSheetDialog 给他添加了一个布局,同时也给顶部添加了一个TopMargin ,但是在使用过程中产品对于现阶段的BottomSheetDialog 的表现形式并不是非常满意,这里我先把已经添加底部固定区域的的BottomSheetDialog 的图片上一下,
GIF 2021-6-9 17-00-38.gif
本期,我们继续对这个dialog做一下修改,
先来说一下我们的问题都有哪些,
- 由于给BottomSheetDialog 添加了一个顶部的margin,导致在BottomSheetDialog 完全展开时,点击顶部透明区域时,dialog并不会收缩
2.产品在看了原生BottomSheetDialog 的展示形式时,感觉类似抖音之类的展示形式比较好,也就是整个BottomSheetDialog 除了展开就是隐藏,不想要中间的那个折叠状态,我们上下最终的效果图
由于修改这个代码比较多,所以还是分享一个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,就可以使用了
网友评论