The problem

When implementing BottomSheetDialog to embed the Webview, dragging down will make the entire dialog disappear instead of sliding the Webview content, as follows:

To solve the problem

1. Come up with an idea

The normal webView sliding/dragging effect should appear normally, and the BottomSheetDialog control’s own dragging effect will also disappear its entire View, so there will be partial conflicts between the two when dragging down at the same time. (If there is no conflict, then It must have been designed and handled by Google.) Therefore, the key to solving the problem can be assumed to be to resolve the “conflict” between downward actions when they occur.

2. Source code analysis

Look at BottomSheetDialog’s source code and find that there is not much content:

 private View wrapInBottomSheet(int layoutResId, View view, ViewGroup.LayoutParams params) {
        final FrameLayout container = (FrameLayout) View.inflate(getContext(),
                R.layout.design_bottom_sheet_dialog, null);
        final CoordinatorLayout coordinator =
                (CoordinatorLayout) container.findViewById(R.id.coordinator);
        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);
        mBehavior.setBottomSheetCallback(mBottomSheetCallback);
        mBehavior.setHideable(mCancelable);
        if (params == null) {
            bottomSheet.addView(view);
        } else{ bottomSheet.addView(view, params); } / /... omitreturn container;
    }
Copy the code

The incoming View will eventually be added to a FrameLayout bottomSheet, and then instantiate a bottomSheet behavior through the bottomSheet, We then set an mBottomSheetCallback to the mBehavior and let’s see what it looks like:

    private BottomSheetBehavior.BottomSheetCallback mBottomSheetCallback
            = new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View bottomSheet,
                @BottomSheetBehavior.State int newState) {
            if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                cancel();
            }
        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        }
    };
Copy the code

It can be seen that it involves monitoring the state of BottomSheet, and the logical judgment can be processed according to the state of BottomSheet behavior. Let’s look at the states in BottomSheetBehavior:

/**
     * The bottom sheet is dragging.
     */
    public static final int STATE_DRAGGING = 1;

    /**
     * The bottom sheet is settling.
     */
    public static final int STATE_SETTLING = 2;

    /**
     * The bottom sheet is expanded.
     */
    public static final int STATE_EXPANDED = 3;

    /**
     * The bottom sheet is collapsed.
     */
    public static final int STATE_COLLAPSED = 4;

    /**
     * The bottom sheet is hidden.
     */
    public static final int STATE_HIDDEN = 5;
Copy the code

Having found the STATE_DRAGGING state, assuming we can listen for the corresponding action in the onStateChanged listening method of BottomSheetCallback, we can intercept and process the initial problem. Therefore, log onStateChanged, print the status code corresponding to the newState of each action, and finally find that sure enough, when dragging down the whole view, its newState is STATE_DRAGGING. Therefore, simply changing the state here to something other than that state may achieve the desired effect. Select STATE_EXPANDED by name to do the processing accordingly.

But how? The BottomSheetCallback is set through the BottomSheetBehavior, whereas the BottomSheetBehavior has

 public static <V extends View> BottomSheetBehavior<V> from(V view) 
Copy the code

Static method, so it’s pretty clear here.

The solution

1. Rewrite the judgment logic in BottomSheetCallback

private BottomSheetBehavior.BottomSheetCallback mBottomSheetCallback
            = new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View bottomSheet,
                                   @BottomSheetBehavior.State int newState) {
            if(newState because = = BottomSheetBehavior STATE_DRAGGING) {/ / to drag down behavior, Is forced to set status to expand bottomSheetBehavior. SetState (bottomSheetBehavior. STATE_EXPANDED); } LogUtil.e(TAG,"OnStateChanged - >" + newState);
        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
            LogUtil.e(TAG, "OnSlide - >"+ slideOffset); }};Copy the code

2. Instantiate BottomSheetBehavior and set the callback to BottomSheetDialog

mBottomSheetDialog.setmBottomSheetCallback((FrameLayout)(view.getParent())); // Since the view is added, the callback listener is set to its parent ViewCopy the code
 public void setmBottomSheetCallback(View sheetView) {
        if (bottomSheetBehavior == null) {
            bottomSheetBehavior = BottomSheetBehavior.from(sheetView);
        }
        bottomSheetBehavior.setBottomSheetCallback(mBottomSheetCallback);
    }

Copy the code

3. Program effect

conclusion

The above is just a simple solution combined with source code analysis and conjecture, and there are many problems left unexplored. The original “conflict” was already resolved by Google, but with some extra processing to make it work according to its rules. Other such as adding RecycleView should also be a similar solution, step by step analysis of the source code and guess to try, always solve.