Android-BottomSheetDialog改造过程详解

背景

关于Google在Metrail Design 风格中新推出的dialog,BottomSheetDialog 在现在不少App中都能看到,我就在抖音和网易新闻上看到过,不管是样式还是新颖的交互来说都是不错的,国内设计师习惯将弹窗的最底部增加一个功能性的按键

11861448-f60d8c29454093be.png

现状

类似于上面图片这种,弹窗的高度还要根据数据条目的个数去适配,如果使用普通的dialog 即没有好的交互体验,弹出的高度也需要我们自己计算,这无疑是增加了开发难度;

找方案

使用BottomSheetDialog ,底部去支付的按钮始终是在数据的下方,如果只有两三条数据还好,如果数据过多则需要将列表滑动到最下方才能看到功能键,这无疑是一个非常糟糕的交互,今天我们就通过查看BottomSheetDialog 来修改他,让他可以存放一个始终放下最下方的区域,先来看一下他的源码

public class BottomSheetDialog extends AppCompatDialog {
    ....省略部分代码
    @Override
    public void setContentView(@LayoutRes int layoutResId) {
        super.setContentView(wrapInBottomSheet(layoutResId, null, null));
    }

    /**
     *  我们知道要实现BottomSheetDialog 离不开BottomSheetBehavior 的支持,
     *这里将 contentView 添加到 一个拥有 BottomSheetBehavior 的布局当中
     **/
    private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
        final CoordinatorLayout coordinator = (CoordinatorLayout) View.inflate(getContext(),
                R.layout.design_bottom_sheet_dialog, null);//事先加载一个拥有BottomSheetBehavior 的布局
        if (layoutResId != 0 && view == null) {
            view = getLayoutInflater().inflate(layoutResId, coordinator, false);
        }
        FrameLayout bottomSheet = (FrameLayout) coordinator.findViewById(R.id.design_bottom_sheet);
        mBehavior = BottomSheetBehavior.from(bottomSheet);//获取到布局BottomSheetBehavior
        mBehavior.setBottomSheetCallback(mBottomSheetCallback);
        mBehavior.setHideable(mCancelable);
        if (params == null) {
            bottomSheet.addView(view);//将contentView 添加入容器中
        } else {
            bottomSheet.addView(view, params);//将contentView 添加入容器中
        }
        // We treat the CoordinatorLayout as outside the dialog though it is technically inside
        coordinator.findViewById(R.id.touch_outside).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mCancelable && isShowing() && shouldWindowCloseOnTouchOutside()) {
                    cancel();
                }
            }
        });
        return coordinator;
    }
    ....省略部分代码
}

复制代码

其实从上面这段代码根本看不出来什么,只是知道contentView被放入的一个拥有BottomSheetBehavior 的容器总,想要在原有的基础上增加一个可拓展的底部功能区域,就需要修改原始的layout布局,

design_bottom_sheet_dialog.xml 布局文件


<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">
  <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="@string/bottom_sheet_behavior"/>
  </androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>

复制代码

CoordinatorLayout  包裹了我们的contentView的容器,也就是design_bottom_sheet 这个FrameLayout,想要给底部增加一个按钮,只需要让CoordinatorLayout  的marginBottom 与 底部的区域高度相等即可,
 

修改后的文件如下:

<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">
    <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="@string/bottom_sheet_behavior"/>
            
    </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"/>
</FrameLayout>
复制代码

修改后的BottomSheetDialog 如下

open class BaseBottomSheetDialog : AppCompatDialog {
    private var mBehavior: TsmBottomSheetBehavior<FrameLayout>? = null
    private var mCancelable = true
    private var mCanceledOnTouchOutside = true
    private var mCanceledOnTouchOutsideSet = false
    protected var mContext: Activity? = null
    
    constructor(context: Activity) : this(context, 0) {
        this.mContext = context
    }
    
    constructor(context: Context, @StyleRes theme: Int) : super(
        context,
        getThemeResId(context, theme)
    ) {
        // We hide the title bar for any style configuration. Otherwise, there will be a gap
        // above the bottom sheet when it is expanded.
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
    }

    protected constructor(
        context: Context, cancelable: Boolean,
        cancelListener: DialogInterface.OnCancelListener?
    ) : super(context, cancelable, cancelListener) {
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE)
        mCancelable = cancelable
    }

    override fun setContentView(@LayoutRes layoutResId: Int) {
        super.setContentView(wrapInBottomSheet(layoutResId, 0, null, null))
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        window!!.setLayout(
            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
        )
    }

    override fun setContentView(view: View) {
        super.setContentView(wrapInBottomSheet(0, 0, view, null))
    }

    override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {
        super.setContentView(wrapInBottomSheet(0, 0, view, params))
    }

    fun setContentView(view: View?, @LayoutRes bottom: Int) {
        super.setContentView(wrapInBottomSheet(0, bottom, view, null))
    }

    override fun setCancelable(cancelable: Boolean) {
        super.setCancelable(cancelable)
        if (mCancelable != cancelable) {
            mCancelable = cancelable
            if (mBehavior != null) {
                mBehavior!!.isHideable = cancelable
            }
        }
    }

    override fun setCanceledOnTouchOutside(cancel: Boolean) {
        super.setCanceledOnTouchOutside(cancel)
        if (cancel && !mCancelable) {
            mCancelable = true
        }
        mCanceledOnTouchOutside = cancel
        mCanceledOnTouchOutsideSet = true
    }

    private fun wrapInBottomSheet(
        layoutResId: Int,
        bottomLayoutId: Int,
        view: View?,
        params: ViewGroup.LayoutParams?
    ): View {
        var view = view
        val parent = View.inflate(context, R.layout.zr_bottom_sheet_dialog_with_bottom, null)
        val coordinator = parent.findViewById<View>(R.id.coordinator) as CoordinatorLayout
        if (layoutResId != 0 && view == null) {
            view = layoutInflater.inflate(layoutResId, coordinator, false)
        }
        if (bottomLayoutId != 0) {
            val bottomView = layoutInflater.inflate(bottomLayoutId, coordinator, false)
            val fl = parent.findViewById<FrameLayout>(R.id.bottom_design_bottom_sheet)
            fl.addView(bottomView)
            coordinator.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    coordinator.viewTreeObserver.removeOnGlobalLayoutListener(this)
                    val p2 = coordinator.layoutParams as FrameLayout.LayoutParams
                    p2.setMargins(0, dp2px(context, 60f), 0, bottomView.height)
                    coordinator.layoutParams = p2
                    val p1 = fl.layoutParams
                    p1.height = bottomView.height
                    fl.layoutParams = p1
                }
            })
//            bottomView.isClickable=true
        }
        val bottomSheet = coordinator.findViewById<View>(R.id.design_bottom_sheet) as FrameLayout
        bottomSheet.setOnClickListener { }
        mBehavior = TsmBottomSheetBehavior.from(bottomSheet)
        mBehavior?.setBottomSheetCallback(mBottomSheetCallback)
        mBehavior?.setHideable(mCancelable)
        if (params == null) {
            bottomSheet.addView(view)
        } else {
            bottomSheet.addView(view, params)
        }
        // We treat the CoordinatorLayout as outside the dialog though it is technically inside
        coordinator.findViewById<View>(R.id.touch_outside).setOnClickListener {
            if (mCancelable && isShowing && shouldWindowCloseOnTouchOutside()) {
                cancel()
            }
        }

        parent?.findViewById<View>(R.id.container)?.setOnClickListener {
            if (mCancelable && isShowing && shouldWindowCloseOnTouchOutside()) {
                cancel()
            }
        }

        return parent
    }

    private fun shouldWindowCloseOnTouchOutside(): Boolean {
        if (!mCanceledOnTouchOutsideSet) {
            if (Build.VERSION.SDK_INT < 11) {
                mCanceledOnTouchOutside = true
            } else {
                val a =
                    context.obtainStyledAttributes(intArrayOf(android.R.attr.windowCloseOnTouchOutside))
                mCanceledOnTouchOutside = a.getBoolean(0, true)
                a.recycle()
            }
            mCanceledOnTouchOutsideSet = true
        }
        return mCanceledOnTouchOutside
    }

    private val mBottomSheetCallback: TsmBottomSheetBehavior.TsmBottomSheetCallback = object : TsmBottomSheetBehavior.TsmBottomSheetCallback() {
        override fun onStateChanged(
            bottomSheet: View,
            @BottomSheetBehavior.State newState: Int
        ) {
//            if (newState == BottomSheetBehavior.STATE_HIDDEN) {
//                dismiss();
//            }
        }

        override fun onSlide(bottomSheet: View, slideOffset: Float) {}
    }

    companion object {
        private fun getThemeResId(context: Context, themeId: Int): Int {
            var themeId = themeId
            if (themeId == 0) {
                // If the provided theme is 0, then retrieve the dialogTheme from our theme
                val outValue = TypedValue()
                themeId = if (context.theme.resolveAttribute(
                        R.attr.bottomSheetDialogTheme, outValue, true
                    )
                ) {
                    outValue.resourceId
                } else {
                    // bottomSheetDialogTheme is not provided; we default to our light theme
                    R.style.Theme_Design_Light_BottomSheetDialog
                }
            }
            return themeId
        }
    }


    open fun dp2px(context: Context, dp: Float): Int {
        val scale: Float = context.getResources().getDisplayMetrics().density
        return (dp * scale + 0.5f).toInt()
    }

}

复制代码

此时写完之后的效果是

底部的高度是通过布局计算的,不需要指定高度,但是需要单独传递一个底部固定部分的id,然后动态添加进去

11861448-9f806a9e8db92321.gif

此时修改过后的BottomSheetDialog 还有一些粗陋,再次封装一下即可使用

abstract class TsmBaseBottomSheetDialog : BaseBottomSheetDialog {
    constructor(context: Activity) :super(context, R.style.bottom_sheet_dilog){
        initDialog()
    }
    open fun initDialog() {
        //需要设置这个才能设置状态栏和导航栏颜色
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            window?.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
            //设置状态栏颜色
            window?.setStatusBarColor(Color.TRANSPARENT)
        }
        //回复默认导航栏颜色
        val view = LayoutInflater.from(context).inflate(layoutId, null)
        setContentView(view, bottomLayoutId)
        behaver = TsmBottomSheetBehavior.from(view.parent as View)
        behaver?.setBottomSheetCallback(object : TsmBottomSheetBehavior.TsmBottomSheetCallback() {
            override fun onStateChanged(bottomSheet: View, newState: Int) {
                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                    dismiss()
                }
            }
            override fun onSlide(bottomSheet: View, slideOffset: Float) {}
        })

    }

    /**

     * 可以控制菜单状态

     */

    protected var behaver: TsmBottomSheetBehavior<View>?=null
    override fun show() {
        initViews()
        super.show()
    }
    protected abstract val layoutId: Int
    protected open val bottomLayoutId: Int
        protected get() = 0
    protected abstract fun initViews()
}
复制代码

再次封装后使用则会方便很多
封装后使用如下

class TsmBottomSheetDialog(context: Activity) : TsmBaseBottomSheetDialog(context) {
    override val layoutId: Int
        protected get() = R.layout.dialog_tsm_bottom_sheet
    override fun initViews() {
        val recycler_view = findViewById<RecyclerView>(R.id.recycler_view)
        recycler_view!!.adapter = object :BaseQuickAdapter<String,BaseViewHolder>(R.layout.item_simple_test,getList(23)){
            override fun convert(holder: BaseViewHolder, item: String) {
                holder?.setText(R.id.tv_item, item)
            }
        }
    }

    private fun getList(count: Int): MutableList<String>? {
        var list: MutableList<String> = MutableList(count,init = {
            it.toString()
        })
        return list
    }
    override val bottomLayoutId: Int
        protected get() = R.layout.botttom_sheet_bottom_view
}
复制代码

但是在使用过程中产品对于现阶段的BottomSheetDialog 的表现形式并不是非常满意

问题

先来说一下我们的问题都有哪些:

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

11861448-8393400af24ebeb4.gif

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);
        }

}
复制代码

到了这里就结束了

由于修改这个代码比较多,所以还是分享一个github 的地址吧,这样方便大家使用github去看看>>

本文作者:大前端研发中心-田守明

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享