如何在不使用对话框的情况下显示 Bottomsheet 时使背景变暗?

How can I dim the background when Bottomsheet is displayed, without using Dialog?

我知道 BottomSheetDialog 已经这样做了,但我需要使用常规 BottomSheet 和从 BottomSheetBehavior.from() 生成的行为。 BottomSheet 不会使背景变暗,并且触摸外部也不会关闭它。有没有办法在显示 BottomSheet 时调暗背景?并可能在触摸外部时解雇。基本上行为就像 BottomSheetDialog 但我必须直接使用 BottomSheet BottomSheetBehavior

如果你想要我在

中使用的概念,你可以使用我的概念

我的做法

  1. 截图
  2. 以编程方式为 dim/blur 屏幕截图制作动画
  3. 使用对话框获取醋栗window 没有任何内容
  4. 附上效果图
  5. 显示我想显示的实景

这里我有一个 class 用于将背景图像作为位图

public class AppUtils {

    public static Bitmap takeScreenShot(Activity activity) {
        View view = activity.getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();


        Bitmap b1 = view.getDrawingCache();
        Rect frame = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
        int statusBarHeight = frame.top;

        Display display = activity.getWindowManager().getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        int width = size.x;
        int height = size.y;


        Bitmap b = Bitmap.createBitmap(b1, 0, statusBarHeight, width, height - statusBarHeight);
        view.destroyDrawingCache();
        return b;
    }
}

恭喜你现在有了一张与背景相同的 darken/dim 图片

然后你的要求是像我一样变暗而不是模糊所以你可以将这个位图传递给下面的方法,

public static Bitmap changeBitmapContrastBrightness(Bitmap bmp, float contrast, float brightness) {
        ColorMatrix cm = new ColorMatrix(new float[]
                {
                        contrast, 0, 0, 0, brightness,
                        0, contrast, 0, 0, brightness,
                        0, 0, contrast, 0, brightness,
                        0, 0, 0, 1, 0
                });

        Bitmap ret = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());

        Canvas canvas = new Canvas(ret);

        Paint paint = new Paint();
        paint.setColorFilter(new ColorMatrixColorFilter(cm));
        canvas.drawBitmap(bmp, 0, 0, paint);

        return ret;
    }

现在使用假对话/对话 withOut a background/content 只得到 window(检查我的问题实现你可以理解)

Window window = fakeDialogUseToGetWindowForBlurEffect.getWindow();
window.setBackgroundDrawable(draw);  // draw is bitmap that you created 

在此之后你可以展示你的真实 view.In 我的案例我会显示一个警报你可以显示你的任何视图并记住 remove/gone 当你的真实视图从屏幕上消失时发出警报!

快速输出 :(背景可以随意定制,不仅暗淡)

使用此样式并应用于您的对话框。

PS: this style also works perfectly in Android 6.0, 6.1 and 7.0.

<style name="MaterialDialogSheet" parent="@android:style/Theme.Dialog">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:backgroundDimEnabled">true</item>
        <item name="android:windowIsFloating">false</item>
        <item name="android:windowAnimationStyle">@style/MaterialDialogSheetAnimation</item>
    </style>
<style name="MaterialDialogSheetAnimation">
        <item name="android:windowEnterAnimation">@anim/popup_show</item>
        <item name="android:windowExitAnimation">@anim/popup_hide</item>
    </style>

并用作:

final Dialog mBottomSheetDialog = new Dialog(mActivity, R.style.MaterialDialogSheet);

谢谢。

您可以创建一个自定义片段,将布局(bottomSheet 的一种)附加到底部并制作背景 transparent_black 并在触摸该 BG 时删除该片段。例如:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff2020"
    android:orientation="vertical"
    tools:context="com.example.jiffysoftwaresolutions.copypastesampleapp.MainActivity">

    <Button
        android:id="@+id/show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Show" />

    <FrameLayout
        android:id="@+id/bottom_sheet_fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></FrameLayout>

</RelativeLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private BottomSheetFragment bottomSheetFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.show).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                if (bottomSheetFragment == null) {
                    bottomSheetFragment = new BottomSheetFragment();
                }
                getSupportFragmentManager().beginTransaction().add(R.id.bottom_sheet_fragment_container, bottomSheetFragment).addToBackStack(null).commit();

            }
        });

    }


    public void removeBottomSheet() {
        try {
            getSupportFragmentManager().beginTransaction().remove(bottomSheetFragment).addToBackStack(null).commit();
        } catch (Exception e) {
        }
    }

}

BottomSheetFragment.java

public class BottomSheetFragment extends Fragment {


    private View rootView;
    private LayoutInflater layoutInflater;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    }


    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.bottom_sheet_layout, container, false);
        rootView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // remove sheet on BG touch
                ((MainActivity) getActivity()).removeBottomSheet();
            }
        });
        return rootView;
    }

}

bottom_sheet_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#6d000000"
    android:gravity="bottom">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fff"
        android:orientation="vertical"
        android:padding="5dp">

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button1"
            android:textColor="#000" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button2"
            android:textColor="#000" />

    </LinearLayout>

</RelativeLayout>

要使用 bottom_top/animation 添加该片段,您可以按照此 link :Android Fragments and animation

您可以使用此代码 1. MainActivity.xml

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<ScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingTop="24dp">

        <Button
            android:id="@+id/button_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Button 1"
            android:padding="16dp"
            android:layout_margin="8dp"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_green_dark"/>

    </LinearLayout>

</ScrollView>

<View
    android:visibility="gone"
    android:id="@+id/bg"
    android:background="#99000000"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

<android.support.v4.widget.NestedScrollView
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="350dp"
    android:clipToPadding="true"
    android:background="@android:color/holo_orange_light"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
    >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="aefwea"
        android:padding="16dp"
        android:textSize="16sp"/>

</android.support.v4.widget.NestedScrollView>

 </android.support.design.widget.CoordinatorLayout>
  1. MAinActivity.java

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    private static final String TAG = "MainActivity";
    private BottomSheetBehavior mBottomSheetBehavior;
    View bottomSheet;
    View mViewBg;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bottomSheet = findViewById(R.id.bottom_sheet);
        mViewBg = findViewById(R.id.mViewBg);
        Button button1 = (Button) findViewById(R.id.button_1);
        button1.setOnClickListener(this);
        mViewBg.setOnClickListener(this);
        mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
        mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                if (newState == BottomSheetBehavior.STATE_COLLAPSED)
                    mViewBg.setVisibility(View.GONE);
            }
    
            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                Log.d(TAG, "onSlide: slideOffset" + slideOffset + "");
               mViewBg.setVisibility(View.VISIBLE);
                mViewBg.setAlpha(slideOffset);
            }
        });
    
    }
    
    
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button_1: {
                mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                break;
            }
            case R.id.bg: {
                mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                break;
            }
        }
    }
    
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
                Rect outRect = new Rect();
                bottomSheet.getGlobalVisibleRect(outRect);
                if (!outRect.contains((int) event.getRawX(), (int) event.getRawY())) {
                    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                    return true;
                }
    
            }
        }
        return super.dispatchTouchEvent(event);
    }
    
    }
    

使用接口 - onSlide 作为 float 类型的参数 slideOffSet,可用于使背景变暗。此底部 sheet 在 [-1,1] 范围内的新偏移量。随着底部 sheet 向上移动,偏移量增加。从 0 到 1,sheet 在折叠和展开状态之间,从 -1 到 0,它在隐藏和折叠状态之间。

if ( slideOffSet>=0 && slideOffSet<=1 ) {
    mainActivityLayoutView.setAlpha( 1f - slideOffSet ); 
}

其中 mainActivityLayoutView 是 NestedScrollView 的 ID,包含 activity.

的主要内容

如果实现 BottomSheetBehavior 的视图位于不同的 activity 中,您可以使用以下解决方案

要使 activity 的背景透明 + 变暗,请将以下样式添加到 activity

<style name="Your.Transparent.Style">
    <!-- Found no solution for setting the status bar color to fully transparent 
         from within the style. Had to resort to programmatically setting -->
    <item name="android:windowBackground">@color/transparent</item>
    <item name="android:backgroundDimEnabled">true</item>
</style>

在您的 AndroidManifest.xml 中设置 activity 主题

<activity
   android:name="your.activity"
   android:theme="@style/Your.Transparent.Style" />

将状态栏颜色设置为完全透明(仅适用于 API >= 21)

// Inside your activity's onCreate 
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setStatusBarTransparent(window)

fun setStatusBarTransparent(window: Window) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        window.statusBarColor = Color.TRANSPARENT
    }
}

最后,为了在点击 sheet 内容之外时隐藏底部 sheet,扩展 BottomSheetBehavior

class AutoCloseBottomSheetBehavior<V : View>(
    context: Context,
    attrs: AttributeSet
) : BottomSheetBehavior<V>(context, attrs) {
    
    @SuppressLint("ClickableViewAccessibility")
    override fun onLayoutChild(parent: CoordinatorLayout, child: V, layoutDirection: Int): Boolean {
        parent.setOnTouchListener { _, event ->
            if (event.action == MotionEvent.ACTION_DOWN) {
                val outRect = Rect()
                child.getGlobalVisibleRect(outRect)
                if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
                    state = STATE_HIDDEN
                    return@setOnTouchListener true
                }
            }

            return@setOnTouchListener false

        }
        return super.onLayoutChild(parent, child, layoutDirection)
    }
}

并根据您的视图行为进行设置

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <Your.View.Acting.As.BottomSheet
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior=".your.path.AutoCloseBottomSheetBehavior" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>