将 snackbar 移到底部栏上方
Move snackbar above the bottom bar
我在使用新底栏时遇到了一些问题。
我不能强制将小吃栏移动到底部栏上方(设计指南告诉我应该这样 https://www.google.com/design/spec/components/bottom-navigation.html#bottom-navigation-specs)。
这是我的activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/app_bar_main_activity"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main_activity"
app:menu="@menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>
这是我的app_bar_main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="test.tab_activity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/appbar_padding_top"
android:theme="@style/MyAppTheme.NoActionBar.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/main_activity_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/MyAppTheme.NoActionBar.PopupOverlay">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<LinearLayout 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"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_add_white_24dp" />
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
style="@style/AppTabLayout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/colorPrimary"
/>
</LinearLayout>
main_activity.java 中的小吃店看起来像这样
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(findViewById(R.id.main_content), "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
替换你的xml ->
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="test.tab_activity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/main_activity_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/container"
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.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/placeSnackBar">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_menu_gallery" />
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/colorPrimary" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
并且 Snackbar 代码将为
Snackbar.make(findViewById(R.id.placeSnackBar), "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
您可以通过更改快餐栏的边距以编程方式执行此操作,而不会用额外的 CoordinatorLayouts 弄乱您的 xml。
Java 示例:
Snackbar snack = Snackbar.make(findViewById(R.id.coordinatorLayout),
"Your message", Snackbar.LENGTH_LONG);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)
snack.getView().getLayoutParams();
params.setMargins(leftMargin, topMargin, rightMargin, bottomBar.height);
snack.getView().setLayoutParams(params);
snack.show();
Kotlin 单行:
Snackbar.make(coordinatorLayout, "Your message", Snackbar.LENGTH_LONG).apply {view.layoutParams = (view.layoutParams as CoordinatorLayout.LayoutParams).apply {setMargins(leftMargin, topMargin, rightMargin, bottomBar.height)}}.show()
我在目标 OS kitkat、lollipop 和 Marshmallow 上使用设计支持库版本 25.3.1 中的 BottomNavigationView 和 Snackbar。在棒棒糖及以上版本中,Snackbar 隐藏在 BottomNavigationView 后面,但在 Kitkat 中,BottomNavigationView 隐藏在 Snackbar 后面。
我试图用不同的方法来展示 Snackbar。当显示 Snackbar 时,BottomNavigationView 使用 translationY 属性 和 Interpolator 在 Y 轴上平移(向下滚动)。 Snackbar 消失后,BottomNavigationView 再次出现,并具有相同的翻译 Y 属性.
隐藏 BottomNavigationView(向底部):
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) bottomNavigationView.getLayoutParams();
bottomNavigationView.animate().translationY(bottomNavigationView.getHeight() + layoutParams.bottomMargin).setInterpolator(new LinearInterpolator()).start();
重新在屏幕上显示 BottomNavigationView:
bottomNavigationView.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
假设您正在使用 CoordinatorLayout,您可以在调用 show() 之前修改 Snackbar 的布局参数。通过设置 anchorId 和 anchorGravity,snackBar 将显示在底部导航栏上方:
val layoutParams = snackbar.view.layoutParams as CoordinatorLayout.LayoutParams
layoutParams.anchorId = R.id.navigation //Id for your bottomNavBar or TabLayout
layoutParams.anchorGravity = Gravity.TOP
layoutParams.gravity = Gravity.TOP
snackbar.view.layoutParams = layoutParams
有一篇关于如何使用它的很棒的文章HERE。在那里你会知道如何制作上面的小吃店 BottomNavigationBar
基本上,下面的代码展示了 Toolbar
以及 BottomNavigationBar
和 FrameLayout
作为片段容器的最常见用法
重要!注意
- fab 按钮使用 anchor 正确放置,useCompactPadding 保留边距
BottomNavigationView
使用 layout_behaviour 来处理滚动和 SnackBar 位置
<android.support.design.widget.AppBarLayout
android:id="@+id/myAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants"
android:focusableInTouchMode="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:contentInsetStart="0dp"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:menu="@menu/bottom_navigation"
app:layout_behavior="murt.shoppinglistapp.ui.BottomNavigationBehavior"
android:background="?android:attr/windowBackground"
/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_add_shopping_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:useCompatPadding="true"
app:srcCompat="@drawable/ic_add_24"
app:layout_anchor="@id/navigation_bar"
app:layout_anchorGravity="top|right"
android:layout_gravity="top"
/>
行为的实现不要犹豫,使用它!它既简单又友好 ;)(滚动)
class BottomNavigationBehavior : CoordinatorLayout.Behavior<BottomNavigationView> {
constructor(): super()
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun layoutDependsOn(parent: CoordinatorLayout, child: BottomNavigationView,
dependency: View): Boolean {
if (dependency is Snackbar.SnackbarLayout) {
updateSnackbar(child, dependency)
}
return super.layoutDependsOn(parent, child, dependency)
}
private fun updateSnackbar(child: View, snackbarLayout: Snackbar.SnackbarLayout) {
if (snackbarLayout.layoutParams is CoordinatorLayout.LayoutParams) {
val params = snackbarLayout.layoutParams as CoordinatorLayout.LayoutParams
params.anchorId = child.id
params.anchorGravity = Gravity.TOP
params.gravity = Gravity.TOP
snackbarLayout.layoutParams = params
}
}
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: BottomNavigationView,
directTargetChild: View,
target: View,
nestedScrollAxes: Int
): Boolean {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
}
override fun onNestedPreScroll(
coordinatorLayout: CoordinatorLayout,
child: BottomNavigationView,
target: View,
dx: Int,
dy: Int,
consumed: IntArray
) {
if (dy < 0) {
showBottomNavigationView(child)
} else if (dy > 0) {
hideBottomNavigationView(child)
}
}
private fun hideBottomNavigationView(view: BottomNavigationView) {
view.animate().translationY(view.height.toFloat())
}
private fun showBottomNavigationView(view: BottomNavigationView) {
view.animate().translationY(0f)
}
}
要实现这一点,您必须注意提供给快餐栏的 ViewGroup 是 CoordinatorLayout,否则快餐栏不会显示在底部导航菜单上方。
如果父布局是 Coordinator 布局就可以简单地完成。
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)
snackbar.getView().getLayoutParams();
params.setAnchorId(R.id.navigation); //id of the bottom navigation view
params.gravity = Gravity.TOP;
params.anchorGravity = Gravity.TOP;
snackbar.getView().setLayoutParams(params);
有一个简单的方法,使用新的 Material 设计:
Snackbar snackbar= Snackbar.make(view, text, duration);
snackbar.setAnchorView(bottomBar);
因此 Snackbar
将显示在 BottomNavigationView
上方。
借助 material 组件库,您可以使用 setAnchorView
方法使 Snackbar
出现在特定视图上方。
在您的情况下,如果您使用 BottomAppBar
和 fab,则应在 setAanchorView
中定义 fab。
类似于:
FloatingActionButton fab = findViewById(R.id.fab);
Snackbar snackbar = Snackbar.make(view, "Snackbar over BottomAppBar", Snackbar.LENGTH_LONG);
snackbar.setAnchorView(fab);
结果:
使用 BottomNavigationView
你可以将它定义为 anchorView:
Snackbar snackbar = Snackbar.make(view,"Snackbar over BottomNav",Snackbar.LENGTH_INDEFINITE);
snackbar.setAnchorView(bottomNavigationView);
snackbar.show();
结果:
Snackbar outerSnackBar = Snackbar.make(findViewById(android.R.id.content), "Your text", Snackbar.LENGTH_INDEFINITE);
View view = outerSnackBar.getView();
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)view.getLayoutParams();
params.gravity = Gravity.BOTTOM;
params.setMargins(0, 0, 0, 150);
view.setLayoutParams(params);
对于无法使用 CoordinatorLayout
的任何人,即使使用 Anchor
解决方案,如 my case
中所示
了解您可以向 Snackbar
添加侦听器并且最多(如果不是全部)视图可以在运行时添加 layout_marginBottom
可能会有所帮助。
从我最初的测试来看,它似乎运行良好。
以下是我的代码,希望对大家有帮助
public class ItemActivity extends AppCompatActivity {
private ConstraintLayout mBottomViewConstraintLayout;
private int mPrevBottomPadding = 0;
private Snackbar mSnackbar;
private Snackbar.Callback mCallback = new Snackbar.Callback() {
@Override
public void onShown(Snackbar sb) {
super.onShown( sb );
addMarginToBottomView();
}
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
super.onDismissed( transientBottomBar, event );
initMarginToBottomView();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
//.....
mSnackbar = Snackbar.make(
findViewById( android.R.id.content ),
getString( R.string.snackbar_message_undo_item_removed ),
Snackbar.LENGTH_LONG)
.setAction( getString( R.string.snackbar_action_undo ),
new View.OnClickListener() {
@Override
public void onClick(View v) {
mItemDao.undoDelete( );
}
} )
.addCallback( mCallback )
.setDuration( Constants.SNACKBAR_DURATION_MS );
}
private void addMarginToBottomView() {
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) mBottomViewConstraintLayout.getLayoutParams();
mPrevBottomPadding = layoutParams.bottomMargin;
layoutParams.bottomMargin += mSnackbar.getView().getHeight();
mBottomViewConstraintLayout.setLayoutParams(layoutParams);
}
private void initMarginToBottomView() {
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) mBottomViewConstraintLayout.getLayoutParams();
layoutParams.bottomMargin = mPrevBottomPadding;
mBottomViewConstraintLayout.setLayoutParams(layoutParams);
}
private void showSnackbar() {
mSnackbar.show();
}
受 Gabriele Mariotti 的启发 这是我的 Kotlin 解决方案:
Snackbar.make(show_snack_btn, "Yeeeaaay!", Snackbar.LENGTH_LONG).also {
it.anchorView = fab
}.show()
我发现您可以放置一个 GuideLine(或视图,如果您不使用 ConstraintLayout),而不是以编程方式操作 CoordinatorLayout 参数,在其上方可以放置 Snackbar。我在这里使用 1 表示 100%,表示父 ConstraintLayout 的底部:
<androidx.constraintlayout.widget.Guideline
android:id="@+id/snackbar_anchor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="1" />
使用 Viewbinding + 此代码将快餐栏放置在指南上方:
Snackbar
.make(containerView, alert.description, Snackbar.LENGTH_LONG).
.setAnchorView(binding?.snackbarAnchor)
.show()
我在使用新底栏时遇到了一些问题。
我不能强制将小吃栏移动到底部栏上方(设计指南告诉我应该这样 https://www.google.com/design/spec/components/bottom-navigation.html#bottom-navigation-specs)。
这是我的activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/app_bar_main_activity"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main_activity"
app:menu="@menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>
这是我的app_bar_main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="test.tab_activity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/appbar_padding_top"
android:theme="@style/MyAppTheme.NoActionBar.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/main_activity_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/MyAppTheme.NoActionBar.PopupOverlay">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<LinearLayout 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"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_add_white_24dp" />
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
style="@style/AppTabLayout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/colorPrimary"
/>
</LinearLayout>
main_activity.java 中的小吃店看起来像这样
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(findViewById(R.id.main_content), "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
替换你的xml ->
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="test.tab_activity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/main_activity_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways">
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/container"
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.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:id="@+id/placeSnackBar">
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_menu_gallery" />
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/colorPrimary" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
并且 Snackbar 代码将为
Snackbar.make(findViewById(R.id.placeSnackBar), "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
您可以通过更改快餐栏的边距以编程方式执行此操作,而不会用额外的 CoordinatorLayouts 弄乱您的 xml。
Java 示例:
Snackbar snack = Snackbar.make(findViewById(R.id.coordinatorLayout),
"Your message", Snackbar.LENGTH_LONG);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)
snack.getView().getLayoutParams();
params.setMargins(leftMargin, topMargin, rightMargin, bottomBar.height);
snack.getView().setLayoutParams(params);
snack.show();
Kotlin 单行:
Snackbar.make(coordinatorLayout, "Your message", Snackbar.LENGTH_LONG).apply {view.layoutParams = (view.layoutParams as CoordinatorLayout.LayoutParams).apply {setMargins(leftMargin, topMargin, rightMargin, bottomBar.height)}}.show()
我在目标 OS kitkat、lollipop 和 Marshmallow 上使用设计支持库版本 25.3.1 中的 BottomNavigationView 和 Snackbar。在棒棒糖及以上版本中,Snackbar 隐藏在 BottomNavigationView 后面,但在 Kitkat 中,BottomNavigationView 隐藏在 Snackbar 后面。
我试图用不同的方法来展示 Snackbar。当显示 Snackbar 时,BottomNavigationView 使用 translationY 属性 和 Interpolator 在 Y 轴上平移(向下滚动)。 Snackbar 消失后,BottomNavigationView 再次出现,并具有相同的翻译 Y 属性.
隐藏 BottomNavigationView(向底部):
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) bottomNavigationView.getLayoutParams();
bottomNavigationView.animate().translationY(bottomNavigationView.getHeight() + layoutParams.bottomMargin).setInterpolator(new LinearInterpolator()).start();
重新在屏幕上显示 BottomNavigationView:
bottomNavigationView.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
假设您正在使用 CoordinatorLayout,您可以在调用 show() 之前修改 Snackbar 的布局参数。通过设置 anchorId 和 anchorGravity,snackBar 将显示在底部导航栏上方:
val layoutParams = snackbar.view.layoutParams as CoordinatorLayout.LayoutParams
layoutParams.anchorId = R.id.navigation //Id for your bottomNavBar or TabLayout
layoutParams.anchorGravity = Gravity.TOP
layoutParams.gravity = Gravity.TOP
snackbar.view.layoutParams = layoutParams
有一篇关于如何使用它的很棒的文章HERE。在那里你会知道如何制作上面的小吃店 BottomNavigationBar
基本上,下面的代码展示了 Toolbar
以及 BottomNavigationBar
和 FrameLayout
作为片段容器的最常见用法
重要!注意
- fab 按钮使用 anchor 正确放置,useCompactPadding 保留边距
BottomNavigationView
使用 layout_behaviour 来处理滚动和 SnackBar 位置<android.support.design.widget.AppBarLayout android:id="@+id/myAppBar" android:layout_width="match_parent" android:layout_height="wrap_content" android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:contentInsetStart="0dp" app:layout_scrollFlags="scroll|enterAlways" app:popupTheme="@style/AppTheme.PopupOverlay"/> </android.support.design.widget.AppBarLayout> <FrameLayout android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <android.support.design.widget.BottomNavigationView android:id="@+id/navigation_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" app:menu="@menu/bottom_navigation" app:layout_behavior="murt.shoppinglistapp.ui.BottomNavigationBehavior" android:background="?android:attr/windowBackground" /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab_add_shopping_list" android:layout_width="wrap_content" android:layout_height="wrap_content" app:useCompatPadding="true" app:srcCompat="@drawable/ic_add_24" app:layout_anchor="@id/navigation_bar" app:layout_anchorGravity="top|right" android:layout_gravity="top" />
行为的实现不要犹豫,使用它!它既简单又友好 ;)(滚动)
class BottomNavigationBehavior : CoordinatorLayout.Behavior<BottomNavigationView> {
constructor(): super()
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
override fun layoutDependsOn(parent: CoordinatorLayout, child: BottomNavigationView,
dependency: View): Boolean {
if (dependency is Snackbar.SnackbarLayout) {
updateSnackbar(child, dependency)
}
return super.layoutDependsOn(parent, child, dependency)
}
private fun updateSnackbar(child: View, snackbarLayout: Snackbar.SnackbarLayout) {
if (snackbarLayout.layoutParams is CoordinatorLayout.LayoutParams) {
val params = snackbarLayout.layoutParams as CoordinatorLayout.LayoutParams
params.anchorId = child.id
params.anchorGravity = Gravity.TOP
params.gravity = Gravity.TOP
snackbarLayout.layoutParams = params
}
}
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: BottomNavigationView,
directTargetChild: View,
target: View,
nestedScrollAxes: Int
): Boolean {
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
}
override fun onNestedPreScroll(
coordinatorLayout: CoordinatorLayout,
child: BottomNavigationView,
target: View,
dx: Int,
dy: Int,
consumed: IntArray
) {
if (dy < 0) {
showBottomNavigationView(child)
} else if (dy > 0) {
hideBottomNavigationView(child)
}
}
private fun hideBottomNavigationView(view: BottomNavigationView) {
view.animate().translationY(view.height.toFloat())
}
private fun showBottomNavigationView(view: BottomNavigationView) {
view.animate().translationY(0f)
}
}
要实现这一点,您必须注意提供给快餐栏的 ViewGroup 是 CoordinatorLayout,否则快餐栏不会显示在底部导航菜单上方。
如果父布局是 Coordinator 布局就可以简单地完成。
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)
snackbar.getView().getLayoutParams();
params.setAnchorId(R.id.navigation); //id of the bottom navigation view
params.gravity = Gravity.TOP;
params.anchorGravity = Gravity.TOP;
snackbar.getView().setLayoutParams(params);
有一个简单的方法,使用新的 Material 设计:
Snackbar snackbar= Snackbar.make(view, text, duration);
snackbar.setAnchorView(bottomBar);
因此 Snackbar
将显示在 BottomNavigationView
上方。
借助 material 组件库,您可以使用 setAnchorView
方法使 Snackbar
出现在特定视图上方。
在您的情况下,如果您使用 BottomAppBar
和 fab,则应在 setAanchorView
中定义 fab。
类似于:
FloatingActionButton fab = findViewById(R.id.fab);
Snackbar snackbar = Snackbar.make(view, "Snackbar over BottomAppBar", Snackbar.LENGTH_LONG);
snackbar.setAnchorView(fab);
结果:
使用 BottomNavigationView
你可以将它定义为 anchorView:
Snackbar snackbar = Snackbar.make(view,"Snackbar over BottomNav",Snackbar.LENGTH_INDEFINITE);
snackbar.setAnchorView(bottomNavigationView);
snackbar.show();
结果:
Snackbar outerSnackBar = Snackbar.make(findViewById(android.R.id.content), "Your text", Snackbar.LENGTH_INDEFINITE);
View view = outerSnackBar.getView();
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)view.getLayoutParams();
params.gravity = Gravity.BOTTOM;
params.setMargins(0, 0, 0, 150);
view.setLayoutParams(params);
对于无法使用 CoordinatorLayout
的任何人,即使使用 Anchor
解决方案,如 my case
了解您可以向 Snackbar
添加侦听器并且最多(如果不是全部)视图可以在运行时添加 layout_marginBottom
可能会有所帮助。
从我最初的测试来看,它似乎运行良好。
以下是我的代码,希望对大家有帮助
public class ItemActivity extends AppCompatActivity {
private ConstraintLayout mBottomViewConstraintLayout;
private int mPrevBottomPadding = 0;
private Snackbar mSnackbar;
private Snackbar.Callback mCallback = new Snackbar.Callback() {
@Override
public void onShown(Snackbar sb) {
super.onShown( sb );
addMarginToBottomView();
}
@Override
public void onDismissed(Snackbar transientBottomBar, int event) {
super.onDismissed( transientBottomBar, event );
initMarginToBottomView();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
//.....
mSnackbar = Snackbar.make(
findViewById( android.R.id.content ),
getString( R.string.snackbar_message_undo_item_removed ),
Snackbar.LENGTH_LONG)
.setAction( getString( R.string.snackbar_action_undo ),
new View.OnClickListener() {
@Override
public void onClick(View v) {
mItemDao.undoDelete( );
}
} )
.addCallback( mCallback )
.setDuration( Constants.SNACKBAR_DURATION_MS );
}
private void addMarginToBottomView() {
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) mBottomViewConstraintLayout.getLayoutParams();
mPrevBottomPadding = layoutParams.bottomMargin;
layoutParams.bottomMargin += mSnackbar.getView().getHeight();
mBottomViewConstraintLayout.setLayoutParams(layoutParams);
}
private void initMarginToBottomView() {
ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) mBottomViewConstraintLayout.getLayoutParams();
layoutParams.bottomMargin = mPrevBottomPadding;
mBottomViewConstraintLayout.setLayoutParams(layoutParams);
}
private void showSnackbar() {
mSnackbar.show();
}
受 Gabriele Mariotti 的启发
Snackbar.make(show_snack_btn, "Yeeeaaay!", Snackbar.LENGTH_LONG).also {
it.anchorView = fab
}.show()
我发现您可以放置一个 GuideLine(或视图,如果您不使用 ConstraintLayout),而不是以编程方式操作 CoordinatorLayout 参数,在其上方可以放置 Snackbar。我在这里使用 1 表示 100%,表示父 ConstraintLayout 的底部:
<androidx.constraintlayout.widget.Guideline
android:id="@+id/snackbar_anchor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="1" />
使用 Viewbinding + 此代码将快餐栏放置在指南上方:
Snackbar
.make(containerView, alert.description, Snackbar.LENGTH_LONG).
.setAnchorView(binding?.snackbarAnchor)
.show()