Как затемнить фон при отображении нижнего листа без использования диалогового окна?

Я знаю, что BottomSheetDialog уже делает это, но мне нужно использовать обычный BottomSheet и поведение, сгенерированное из BottomSheetBehavior.from(). Этот BottomSheet не затемняет фон, а также касание снаружи не закрывает его. Есть ли способ затемнить фон, когда отображается BottomSheet? и, возможно, уволить при прикосновении снаружи. В основном поведение такое же, как BottomSheetDialog, но я должен использовать BottomSheet BottomSheetBehavior напрямую.


person user1865027    schedule 01.12.2016    source источник
comment
Почему вы должны использовать BottomSheetBehavior?   -  person ianhanniballake    schedule 01.12.2016
comment
Была такая же проблема. Для меня причина использования BottomSheet и BottomSheetBehavior вместо BottomSheetDialog(Fragment) заключается в том, что BottomSheet хорошо играет при отображении клавиатуры. Использование BottomSheetDialogFragment вызывает некоторую дерганую анимацию. При отображении клавиатуры DialogFragment просто привязывается к новой позиции. BottomSheet плавно анимируется вверх/вниз.   -  person tir38    schedule 05.01.2017
comment
Это имеет затемнение фона без фрагмента hidroh.com/2016/06/17 /нижний лист-все   -  person Samuel    schedule 29.09.2017


Ответы (6)


Вы можете использовать этот код 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);
    }
    
    }
    
person Rahul Pareta    schedule 12.01.2017
comment
Мне пришлось использовать setPeekHeight(int peekHeight) так же, как этот ответ показывает, чтобы этот пример работал - person Sam; 11.09.2017
comment
Это хорошо работает! Но не плохо ли для производительности постоянно использовать findview? Особенно в onSlide. Почему бы просто не получить ссылку на представление в oncreate? - person behelit; 28.11.2018
comment
Это более правильное решение. - person Relm; 18.12.2018
comment
как можно затемнить цвет строки состояния? - person Pranav; 07.05.2019

Вы можете создать пользовательский фрагмент с макетом (вид нижнего листа), прикрепленным к нижней части, и сделать фон transparent_black, а при касании этого фона удалить этот фрагмент. Бывший :

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.. вы можете перейти по этой ссылке: Фрагменты Android и анимация

person NehaK    schedule 12.01.2017

Вы можете использовать мою концепцию, если хотите, чтобы я использовал ее в AlertDialog с размытием фона в центре позиции

Мой подход

  1. Сделать снимок экрана
  2. Программная анимация затемненного/размытого снимка экрана
  3. Получить текущее окно с помощью диалогового окна, в котором нет содержимого
  4. Прикрепить скриншот с эффектом
  5. Показать реальный вид, который я хотел отобразить

Здесь у меня есть класс для получения изображения фона в виде растрового изображения.

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

Поздравляем, теперь у вас есть затемненное/тусклое изображение, такое же, как ваш фон.

Тогда ваше требование состоит в том, чтобы затемнить, а не размыть, как у меня, чтобы вы могли передать это растровое изображение методу ниже,

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

Теперь используйте поддельный диалог/диалог без фона/контента только для получения окна (проверьте реализацию моих вопросов, вы можете это понять)

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

после этого вы можете показать свое реальное представление. В моем случае я показываю предупреждение, которое вы можете отобразить, независимо от вашего представления, и не забудьте удалить / исчезнуть это предупреждение, когда ваше реальное представление исчезнет с экрана!

Быстрый вывод: (фон можно настроить по своему усмотрению, а не только тусклый)

введите здесь описание изображения

person Charuක    schedule 10.01.2017

Используйте этот стиль и примените его к своему диалогу.

PS: этот стиль также отлично работает в Android 6.0, 6.1 и 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);

Спасибо.

person Harsh Dalwadi    schedule 11.01.2017
comment
@ tir38 не судите никого, не зная проблемы. они упомянули без использования диалога фрагментов. вам лучше ответить на этот вопрос, не отрицая ответ другого. - person Harsh Dalwadi; 12.01.2017
comment
Спасибо, этот ответ помог мне...! - person Rajnish Sharma; 30.03.2021
comment
Не могли бы вы также поделиться XML и стилем анимации? - person MeLine; 29.05.2021

Использование интерфейса - onSlide, который в качестве параметра slideOffSet типа float может использоваться для затемнения фона. Новое смещение этого нижнего листа в диапазоне [-1,1]. Смещение увеличивается по мере того, как этот нижний лист движется вверх. От 0 до 1 лист находится между свернутым и развернутым состояниями, а от -1 до 0 — между скрытым и свернутым состояниями.

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

где mainActivityLayoutView — это идентификатор NestedScrollView, содержащий основное содержимое действия.

person Archit    schedule 24.04.2020
comment
не выглядит тусклым - person ShadeToD; 29.12.2020

Если представление, реализующее BottomSheetBehavior, находится внутри отдельного действия, вы можете использовать следующее решение

Чтобы сделать фон вашей активности прозрачным + затененным, добавьте следующий стиль к вашей деятельности

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

И, наконец, чтобы скрыть нижний лист при щелчке за пределами содержимого листа, расширьте 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>
person TreyWurm    schedule 22.07.2021