49 votes

Comment puis-je atténuer l'arrière-plan lorsque Bottomsheet est affiché, sans utiliser Dialog ?

Je sais BottomSheetDialog le fait déjà, mais j'ai besoin d'utiliser l'habituelle BottomSheet et le comportement généré par BottomSheetBehavior.from() . Ce site BottomSheet n'atténue pas l'arrière-plan et la touche extérieure ne la ferme pas. Existe-t-il un moyen d'atténuer l'arrière-plan lorsque BottomSheet est affichée ? et peut-être la désactiver en touchant l'extérieur. En gros, le comportement est le suivant BottomSheetDialog mais je dois utiliser BottomSheet BottomSheetBehavior directement.

0 votes

Pourquoi devez-vous utiliser BottomSheetBehavior ?

3 votes

J'ai eu le même problème. Pour moi, la raison d'utiliser BottomSheet y BottomSheetBehavior au lieu de BottomSheetDialog(Fragment) est que le BottomSheet joue bien quand on montre le clavier. Utilisation de BottomSheetDialogFragment provoque des animations bizarres. Lorsque vous affichez le clavier, le DialogFragment s'adapte simplement à sa nouvelle position. BottomSheet s'anime doucement de haut en bas.

1 votes

Il y a une gradation de fond sans fragment hidroh.com/2016/06/17/bottom-sheet-everything (en anglais)

49voto

Rahul Pareta Points 332

Vous pouvez utiliser ce code 1. Activité principale.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);
    }
    
    }

3 votes

J'ai dû utiliser setPeekHeight(int peekHeight) tout comme cette réponse montre pour que cet exemple fonctionne

1 votes

Cela fonctionne bien ! Mais n'est-ce pas mauvais pour les performances d'utiliser findview de manière répétée ? Surtout dans onSlide. Pourquoi ne pas simplement obtenir la référence à la vue dans oncreate ?

0 votes

C'est une solution plus appropriée.

2voto

NehaK Points 1354

Vous pouvez créer un fragment personnalisé qui a une mise en page (une sorte de bottomSheet) attachée au fond et rendre l'arrière-plan transparent_black & quand on touche à ce BG, on enlève ce fragment. Ex :

activité_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;
    }

}

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

Pour ajouter ce fragment avec bottom_top/animation vous pouvez suivre ce lien : Fragments d'Android et animation

1voto

Archit Points 9

En utilisant l'interface - onSlide qui a comme paramètre slideOffSet de type float, peut être utilisé pour atténuer l'arrière-plan. Le nouveau décalage de cette feuille de fond se situe dans la plage [-1,1]. Le décalage augmente au fur et à mesure que la feuille inférieure se déplace vers le haut. De 0 à 1, la feuille se trouve entre les états replié et déployé et de -1 à 0, elle se trouve entre les états caché et replié.

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

où mainActivityLayoutView est l'identifiant de NestedScrollView , qui contient le contenu principal de l'activité.

0voto

TreyWurm Points 280

Dans le cas où la vue qui implémente le BottomSheetBehavior est à l'intérieur d'une activité distincte, vous pouvez utiliser la solution suivante

Pour rendre l'arrière-plan de votre activité transparent et atténué, ajoutez le style suivant à votre activité

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

Dans votre AndroidManifest.xml, définissez le thème de l'activité.

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

Configuration de la couleur de la barre d'état pour qu'elle soit totalement transparente (fonctionne uniquement pour 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
    }
}

Enfin, pour masquer la feuille du bas lorsque l'on clique en dehors du contenu de la feuille, étendez l'attribut 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)
    }
}

Et définissez-le pour le comportement de votre vue

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

0voto

Charuක Points 9861

Vous pouvez utiliser mon concept si vous voulez, que j'ai utilisé dans AlertDialog avec fond flou au centre de la position

Mon approche

  1. Faites une capture d'écran
  2. Animation programmée d'une capture d'écran de type "dim/blur".
  3. Obtenir la fenêtre actuelle en utilisant un dialogue qui n'a pas de contenu.
  4. Joindre une capture d'écran avec effet
  5. Afficher la vue réelle que je voulais afficher

Ici, j'ai une classe pour prendre une image de l'arrière-plan comme un bitmap.

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

Félicitations, vous avez maintenant une image d'obscurcissement/de réduction identique à votre arrière-plan.

Alors votre exigence est de ne pas faire de flou comme moi, vous pouvez donc passer ce bitmap à la méthode ci-dessous,

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

Maintenant, utilisez un faux dialogue / un dialogue sans arrière-plan/contenu uniquement pour obtenir la fenêtre (vérifiez la mise en œuvre de mes questions, vous pouvez comprendre cela).

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

Dans mon cas, j'affiche une alerte, vous pouvez afficher ce que vous voulez et n'oubliez pas de supprimer cette alerte lorsque votre vue réelle disparaît de l'écran !

Sortie rapide (l'arrière-plan peut être personnalisé comme vous le souhaitez, et pas seulement en fonction de la luminosité).

enter image description here

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X