Как показать снэк-бар в верхней части экрана

Как сказано в документации Android

Закусочные обеспечивают легкую обратную связь об операции. Они показывают краткое сообщение внизу экрана на мобильном телефоне и внизу слева на больших устройствах.

Есть ли альтернатива, с помощью которой мы можем показать snackbars вверху экрана вместо нижнего?

Прямо сейчас я делаю что-то вроде этого, которое показывает snackbar внизу экрана.

Snackbar.make(findViewById(android.R.id.content), "Hello this is a snackbar!!!", 
Snackbar.LENGTH_LONG).setAction("Undo", mOnClickListener)
.setActionTextColor(Color.RED)
.show();

12 ответов

Можно заставить снэк-бар появляться в верхней части экрана, используя это:

Snackbar snack = Snackbar.make(parentLayout, str, Snackbar.LENGTH_LONG);
View view = snack.getView();
FrameLayout.LayoutParams params =(FrameLayout.LayoutParams)view.getLayoutParams();
params.gravity = Gravity.TOP;
view.setLayoutParams(params);
snack.show();

Из ОП:

Мне пришлось изменить первую строку:

Snackbar snack = Snackbar.make(findViewById(android.R.id.content), "Had a snack at Snackbar", Snackbar.LENGTH_LONG);
CoordinatorLayout coordinatorLayout=(CoordinatorLayout)findViewById(R.id.coordinatorLayout);
Snackbar snackbar = Snackbar.make(coordinatorLayout, "Text", Snackbar.LENGTH_LONG);
View view = snackbar.getView();
CoordinatorLayout.LayoutParams params=(CoordinatorLayout.LayoutParams)view.getLayoutParams();
params.gravity = Gravity.TOP;
view.setLayoutParams(params);
snackbar.show();

Котлин

    val snackBarView = Snackbar.make(view, "SnackBar Message" , Snackbar.LENGTH_LONG)
    val view = snackBarView.view
    val params = view.layoutParams as FrameLayout.LayoutParams
    params.gravity = Gravity.TOP
    view.layoutParams = params
    view.background = ContextCompat.getDrawable(context,R.drawable.custom_drawable) // for custom background
    snackBarView.animationMode = BaseTransientBottomBar.ANIMATION_MODE_FADE
    snackBarView.show()

Строка ниже решит проблему с анимацией.

snackBarView.animationMode = BaseTransientBottomBar.ANIMATION_MODE_FADE

Альтернативное решение-snackBarView.anchorView = mention viewId above whom you want to show SnackBar

Вы можете сделать следующее, чтобы разместить SnackBar в любом месте макета (у этого метода нет проблем с анимацией)

1) Согласно:

https://developer.android.com/reference/android/support/design/widget/Snackbar.html

Создание закусочной (просмотр просмотра, текст CharSequence, длительность int)

Создайте Snackbar для отображения сообщения Snackbar попытается найти родительский вид, чтобы удерживать представление Snackbar от значения, данного для view. Snackbar будет проходить вверх по дереву представлений, пытаясь найти подходящего родителя, который определяется как CoordinatorLayout или представление содержимого декора окна, в зависимости от того, что наступит раньше.

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

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:orientation="vertical"
android:id="@+id/rl"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Coordinator Layout used to position the SnackBar -->

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/cl"
    android:layout_alignParentTop="true"
    android:background="@android:color/transparent">
</android.support.design.widget.CoordinatorLayout>

<!-- add your layout here -->

</RelativeLayout>

2) Макет координатора, используемый для отображения SnackBar, должен быть поверх всех других видов (самая высокая отметка). Для этого можно позвонитьbringToFront() на макете координатора или поднять макет координатора (добавить android:elevation="10dp" например)

3) На этом этапе закуска появится в желаемом месте, но закуска будет отображаться с анимацией снизу вверх (поведение по умолчанию). Чтобы добиться анимации сверху вниз, вы можете сделать следующее:

  • Макет "Повернуть координатор", используемый в методе Snackbar.make, на 180 градусов

4) После шага 3 SnackBar будет отображаться с анимацией сверху вниз, но текст сообщения и действия поворачивается, а сила тяжести меняется на противоположную, поэтому в качестве последнего шага я сделал следующее:

  • Получил SnackBar View, обнаружил, что LinearLayout содержит TextView, отвечающий за отображение сообщения, и TextView, отвечающий за действие, и повернул родительский LinearLayout на 180 градусов

5) Пример:

public class MainActivity extends AppCompatActivity {

private final String TAG = MainActivity.class.getSimpleName();
private RelativeLayout rl;
private CoordinatorLayout cl;
private CoordinatorLayout cl1;
private CoordinatorLayout cl2;
private CoordinatorLayout cl3;
private CoordinatorLayout cl4;
private Snackbar snackbar_updated;
private Snackbar snackbar_updated1;
private Snackbar snackbar_updated2;
private Snackbar snackbar_updated3;
private Snackbar snackbar_updated4;
private Snackbar snackbar_ordinary;


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

    rl = (RelativeLayout) findViewById(R.id.rl);
    cl = (CoordinatorLayout) findViewById(R.id.cl);
    cl1 = (CoordinatorLayout) findViewById(R.id.cl1);
    cl2 = (CoordinatorLayout) findViewById(R.id.cl2);
    cl3 = (CoordinatorLayout) findViewById(R.id.cl3);
    cl4 = (CoordinatorLayout) findViewById(R.id.cl4);
    cl.bringToFront();
    cl1.bringToFront();
    cl2.bringToFront();
    cl3.bringToFront();
    cl4.bringToFront();

    snackbar_updated = Snackbar.make(cl, "Message", Snackbar.LENGTH_INDEFINITE);
    snackbar_updated.setAction("Ok", new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });
    /** Snackbar message and action TextViews are placed inside a LinearLayout
     */
    final Snackbar.SnackbarLayout snackBarLayout = (Snackbar.SnackbarLayout) snackbar_updated.getView();
    for (int i = 0; i < snackBarLayout.getChildCount(); i++) {
        View parent = snackBarLayout.getChildAt(i);
        if (parent instanceof LinearLayout) {
            ((LinearLayout) parent).setRotation(180);
            break;
        }
    }

    snackbar_updated1 = Snackbar.make(cl1, "Message", Snackbar.LENGTH_INDEFINITE);
    snackbar_updated1.setAction("Ok", new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });
    /** Snackbar message and action TextViews are placed inside a LinearLayout
     */
    final Snackbar.SnackbarLayout snackBarLayout1 = (Snackbar.SnackbarLayout) snackbar_updated1.getView();
    for (int i = 0; i < snackBarLayout1.getChildCount(); i++) {
        View parent = snackBarLayout1.getChildAt(i);
        if (parent instanceof LinearLayout) {
            ((LinearLayout) parent).setRotation(180);
            break;
        }
    }

    snackbar_updated2 = Snackbar.make(cl2, "Message", Snackbar.LENGTH_INDEFINITE);
    snackbar_updated2.setAction("Ok", new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });

    snackbar_updated3 = Snackbar.make(cl3, "Message", Snackbar.LENGTH_INDEFINITE);
    snackbar_updated3.setAction("Ok", new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });
    /** Snackbar message and action TextViews are placed inside a LinearLayout
     */
    Snackbar.SnackbarLayout snackBarLayout3 = (Snackbar.SnackbarLayout) snackbar_updated3.getView();
    for (int i = 0; i < snackBarLayout3.getChildCount(); i++) {
        View parent = snackBarLayout3.getChildAt(i);
        if (parent instanceof LinearLayout) {
            ((LinearLayout) parent).setRotation(180);
            break;
        }
    }

    snackbar_updated4 = Snackbar.make(cl4, "Message", Snackbar.LENGTH_INDEFINITE);
    snackbar_updated4.setAction("Ok", new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });

    snackbar_ordinary = Snackbar.make(rl, "Message", Snackbar.LENGTH_INDEFINITE);
    snackbar_ordinary.setAction("Ok", new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        }
    });

    rl.post(new Runnable() {
        @Override
        public void run() {
            snackbar_updated.show();
            rl.postDelayed(new Runnable() {
                @Override
                public void run() {
                    snackbar_updated1.show();
                    rl.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            snackbar_updated2.show();
                            rl.postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    snackbar_updated3.show();
                                    rl.postDelayed(new Runnable() {
                                        @Override
                                        public void run() {
                                            snackbar_updated4.show();
                                            rl.postDelayed(new Runnable() {
                                                @Override
                                                public void run() {
                                                    snackbar_ordinary.show();
                                                }
                                            }, 2000);
                                        }
                                    }, 2000);
                                }
                            }, 2000);
                        }
                    }, 2000);
                }
            }, 2000);
        }
    });

}

}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:orientation="vertical"
android:id="@+id/rl"
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">

<!-- Coordinator Layout used to position the SnackBar -->

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/cl"
    android:rotation="180"
    android:layout_alignParentTop="true"
    android:background="@android:color/transparent">
</android.support.design.widget.CoordinatorLayout>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_alignParentTop="true"
    android:orientation="vertical">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fitsSystemWindows="true">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:itemIconTint="#333"
        app:itemTextColor="#333"
        app:layout_collapseMode="pin"
        app:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Dark"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

</android.support.design.widget.AppBarLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_below="@id/appbar"
        android:layout_gravity="bottom">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:id="@+id/tv_top"
            android:text="Layout Top"
            android:gravity="center"
            android:textSize="15sp"
            android:textColor="@android:color/white"
            android:layout_alignParentTop="true"
            android:background="@color/colorAccent">
        </TextView>

        <!-- Coordinator Layout used to position the SnackBar -->

        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cl1"
            android:rotation="180"
            android:layout_below="@id/tv_top"
            android:background="@android:color/transparent">
        </android.support.design.widget.CoordinatorLayout>


        <!-- Coordinator Layout used to position the SnackBar -->

        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cl2"
            android:paddingBottom="75dp"
            android:layout_centerInParent="true"
            android:background="@android:color/transparent">
        </android.support.design.widget.CoordinatorLayout>

        <!-- Coordinator Layout used to position the SnackBar -->

        <TextView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:id="@+id/tv_center"
            android:text="Center"
            android:gravity="center"
            android:textSize="15sp"
            android:layout_centerInParent="true"
            android:textColor="@android:color/white"
            android:background="@color/colorAccent">
        </TextView>

        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cl3"
            android:rotation="180"
            android:paddingBottom="75dp"
            android:layout_centerInParent="true"
            android:background="@android:color/transparent">
        </android.support.design.widget.CoordinatorLayout>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:id="@+id/tv_bottom"
            android:text="Layout Bottom"
            android:gravity="center"
            android:textSize="15sp"
            android:textColor="@android:color/white"
            android:layout_alignParentBottom="true"
            android:background="@color/colorAccent">
        </TextView>

        <!-- Coordinator Layout used to position the SnackBar -->

        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cl4"
            android:layout_above="@id/tv_bottom"
            android:background="@android:color/transparent">
        </android.support.design.widget.CoordinatorLayout>


    </RelativeLayout>

</RelativeLayout>

</RelativeLayout>

6) Результат:

Комбинированное решение из вышеперечисленных:

final ViewGroup.LayoutParams params = snackbar.getView().getLayoutParams();
if (params instanceof CoordinatorLayout.LayoutParams) {
    ((CoordinatorLayout.LayoutParams) params).gravity = Gravity.TOP;
} else {
    ((FrameLayout.LayoutParams) params).gravity = Gravity.TOP;
}
snackbar.getView().setLayoutParams(params);

По-прежнему страдает от неправильной анимации.

Это заставляет Snackbar отображаться сверху без странного слайда при переходе.

Лучшее простое решение в Котлине:

    val snackbar = Snackbar.make(view, string, Snackbar.LENGTH_LONG)
    val layoutParams = LayoutParams(snackbar.view.layoutParams)

    layoutParams.gravity = Gravity.TOP
    snackbar.view.setPadding(0, 10, 0, 0)
    snackbar.view.layoutParams = layoutParams
    snackbar.animationMode = BaseTransientBottomBar.ANIMATION_MODE_FADE
    snackbar.show()

Идиоматическая версия Котлина

      snackbar.view.layoutParams = (snackbar.view.layoutParams as FrameLayout.LayoutParams).apply {
  gravity = Gravity.TOP
}

Расширение Котлина

      /** Kotlin extension that adds this snackbar at the top of the screen. */
fun Snackbar.gravityTop() {
  this.view.layoutParams = (this.view.layoutParams as FrameLayout.LayoutParams).apply {
    gravity = Gravity.TOP
  }
}

Тогда просто позвоните:

      snackbar.gravityTop()

Перед отображением Snackbar добавьте следующий код

View snackbarView = snackbar.getView();

FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) snackbarView.getLayoutParams();
params.gravity = Gravity.TOP;
snackbarView.setLayoutParams(params);
snackbarView.startAnimation(AnimationUtils.loadAnimation(host, R.anim.slide_in_snack_bar));

И когда уволить ()

View snackbarView = snackbar.getView();
  
snackbarView.startAnimation(AnimationUtils.loadAnimation(_snackbar.getContext(), R.anim.slide_out_snack_bar));
snackbar.dismiss()

slide_in_snack_bar.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromYDelta="-100%p"
android:toYDelta="0%p" />

slide_out_snack_bar.xml

<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_longAnimTime"
android:fromYDelta="0%p"
android:toYDelta="-100%p" />

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

      <LinearLayout>
......<Button>

<LinearLayout>
<TextView>
</LinearLayout>
</LinearLayout>

в коде:

      Snackbar snackbar = Snackbar.make(view,message, snackbar.LENGTH_SHORT).setAnchorView(R.id.reg_button);

R.id.reg_button — это кнопка в макете, она может быть где угодно, закусочная будет отображаться прямо там Нет необходимости использовать какой-либо фрейм и т. д. Приложение будет работать, как и ожидалось, не будет падать

если вы используете Constraintlayoutas в качестве корневого родителя с помощью Руководств и CoordinatorLayout вы можете попробовать это:

изменение значения: layout_constraintGuide_end = "Значение вашего нижнего поля" внутри Руководства, как показано ниже в примере.

      <androidx.coordinatorlayout.widget.CoordinatorLayout
    android:id="@+id/coordinator"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/guideline3" />

<androidx.constraintlayout.widget.Guideline
    android:id="@+id/guideline3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintGuide_end="150dp" />

в активности вы можете передать CoordinatorLayout как представление для закусочной

      mSnackBar = Snackbar.make(
            coordinator,
            getString(R.string.no_internet_connection),
            Snackbar.LENGTH_INDEFINITE
        )
        mSnackBar.show()

2 года спустя, вот мое решение..

Установка верхнего и нижнего полей изменяет результат эффекта... Я попытался добавить как можно больше параметров настройки. Переопределение анимации - это еще один вариант, который здесь не написан.

Спасибо всем за ответы на несколько вопросов...

{
    // usage for setSnackBar
    int view = R.id.toolbar;
    String snackMessage = "";
    boolean useAction = false;
    Runnable ifUseActionRunnable = null;
    String actionText = "";
    int leftMargin = 0;
    int topMargin = 0;
    int rightMargin = 0;
    int bottomMargin = 0;
    int backgroundColour = Color.BLACK;
    int textColor = Color.WHITE;
    int duration = Snackbar.LENGTH_LONG;
    setSnackBar(view, snackMessage, useAction, ifUseActionRunnable, actionText, leftMargin, topMargin, rightMargin, bottomMargin, backgroundColour, textColor, duration);

}

Snackbar snb;
public void setSnackBar(int targetView, String snackMessage, boolean useAction, final Runnable ifUseActionRunnable, String actionText , int leftMargin, int topMargin, int rightMargin, int bottomMargin, int backgroundColour, int textColor, int duration)
{
    snb = Snackbar.make(findViewById(targetView), snackMessage, duration);
    View view = snb.getView();
    view.setBackgroundColor(backgroundColour);
    snb.setActionTextColor(textColor);
    FrameLayout.LayoutParams params =(FrameLayout.LayoutParams)view.getLayoutParams();
    params.gravity =  Gravity.CENTER_HORIZONTAL | Gravity.TOP;
    params.setMargins(leftMargin, topMargin, rightMargin, bottomMargin);
    view.setLayoutParams(params);
    if (useAction)
    {
        snb.setAction(actionText, new View.OnClickListener(){
                @Override
                public void onClick(View p1)
                {
                    ifUseActionRunnable.run();

                }
            });
    }
    if (snb.isShown())
    {
        snb.dismiss();
        snb.show();
    }
    else
    {
        snb.show();
    }
}

Чтобы показать Snackbar вверху экрана (kotlin)

// Custom snackbar : banner model
            val customSnackBar   = Snackbar.make(snackbar_id, "", Snackbar.LENGTH_INDEFINITE)

            val view = customSnackBar.view
            val params = view.layoutParams as CoordinatorLayout.LayoutParams
            params.gravity = Gravity.TOP
            view.layoutParams = params

            val layout           = customSnackBar.view as Snackbar.SnackbarLayout
            val customSnackView  = layoutInflater.inflate(R.layout.snackbar_banner, null)

            val actionButton1   = customSnackView.findViewById(R.id.action_button_1) as MaterialButton
            actionButton1.setOnClickListener {
                customSnackBar.dismiss()
            }
            val actionButton   = customSnackView.findViewById(R.id.action_button_2) as MaterialButton
            actionButton.setOnClickListener {
                customSnackBar.dismiss()
            }

            // We can also customize the above controls
            layout.setPadding(0, 0, 0, 0)
            layout.addView(customSnackView, 0)


            customSnackBar.show()

XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="@android:color/white"
   >

    <TextView
        android:padding="16dp"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/snackbar_banner_message"
        android:textColor="@android:color/black"
        android:textSize="16sp" />

    <LinearLayout
        android:gravity="end"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">

        <com.google.android.material.button.MaterialButton
            android:id="@+id/action_button_1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginEnd="8dp"
            android:layout_weight="1"
            android:background="@android:color/black"
            android:text="Close"
            style="@style/Widget.MaterialComponents.Button.TextButton"
           />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/action_button_2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@android:color/black"
            android:text="Fix it"
            android:textStyle="bold"
            style="@style/Widget.MaterialComponents.Button.TextButton"
           />

    </LinearLayout>

</LinearLayout>

Другие вопросы по тегам