如何将 BottomAppBar + FAB 与 BottomNavigationView 结合使用
Posted
技术标签:
【中文标题】如何将 BottomAppBar + FAB 与 BottomNavigationView 结合使用【英文标题】:How to combine BottomAppBar + FAB with BottomNavigationView 【发布时间】:2019-05-20 17:32:11 【问题描述】:我想使用FloatingActionButton
,以及它锚定在BottomAppBar、BottomNavigationView顶部时的行为。
我想出了一个相当“hacky”的技巧,只需将 BottomNavigationView
放在 BottomAppBar 的顶部,而不提供背景,从而使其透明。
乍一看似乎效果很好,但我发现只有在触摸按钮的上半部分时才能点击 fab 按钮(所以顶部没有透明的BottomNavigationView
)。
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_
android:layout_
android:layout_gravity="bottom"
app:layout_constraintBottom_toBottomOf="parent">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_
android:layout_
android:clickable="true"
android:focusable="true"
app:layout_anchor="@id/bar" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bar"
android:layout_
android:layout_
android:layout_gravity="bottom"
android:backgroundTint="@color/colorPrimaryDark" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_
android:layout_
android:layout_gravity="bottom"
app:itemIconTint="@android:color/darker_gray"
app:itemTextColor="@android:color/white"
app:labelVisibilityMode="labeled"
app:menu="@menu/navigation" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
有什么方法可以实现这个想法,我可以完全点击FloatingActionButton
?
【问题讨论】:
为什么在单击 fab 图标时调用的中心底部导航视图按钮时不调用相同的函数? 由于中间导航项是不可见的,这也是我想到的一个可能的解决方案。 FAB 是可点击/可聚焦的,因此在完美的场景中,我不希望在点击下方的按钮时产生涟漪效应。BottomAppBar
只是一个专门的Toolbar
。您是否考虑过尝试将自己的标签按钮直接添加到其中?似乎比覆盖BottomNavigationView
少一些hacky。
这种hacky方法的原因是将FAB的行为保留在BottomAppBar中。 (例如动画)。也许有人对我有一个有用的解决方案,否则我应该使用我自己的按钮。
不,我的意思是,保留BottomAppBar
(连同它的FloatingActionButton
)并向其添加按钮,而不是覆盖BottomNavigationView
。它不应该影响 FAB。
【参考方案1】:
第一道
试试这个你可以创建一个CustomBottomNavigationView
这是CustomBottomNavigationView
的好文章
How I draw custom shapes in BottomNavigationView
示例代码
import android.content.Context;
import android.graphics.*;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
public class CustomBottomNavigationView extends BottomNavigationView
private Path mPath;
private Paint mPaint;
/** the CURVE_CIRCLE_RADIUS represent the radius of the fab button */
private final int CURVE_CIRCLE_RADIUS = 128 / 2;
// the coordinates of the first curve
private Point mFirstCurveStartPoint = new Point();
private Point mFirstCurveEndPoint = new Point();
private Point mFirstCurveControlPoint1 = new Point();
private Point mFirstCurveControlPoint2 = new Point();
//the coordinates of the second curve
@SuppressWarnings("FieldCanBeLocal")
private Point mSecondCurveStartPoint = new Point();
private Point mSecondCurveEndPoint = new Point();
private Point mSecondCurveControlPoint1 = new Point();
private Point mSecondCurveControlPoint2 = new Point();
private int mNavigationBarWidth;
private int mNavigationBarHeight;
public CustomBottomNavigationView(Context context)
super(context);
init();
public CustomBottomNavigationView(Context context, AttributeSet attrs)
super(context, attrs);
init();
public CustomBottomNavigationView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
init();
private void init()
mPath = new Path();
mPaint = new Paint();
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setColor(ContextCompat.getColor(getContext(),R.color.colorAccent));
setBackgroundColor(Color.TRANSPARENT);
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
super.onLayout(changed, left, top, right, bottom);
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
super.onSizeChanged(w, h, oldw, oldh);
// get width and height of navigation bar
// Navigation bar bounds (width & height)
mNavigationBarWidth = getWidth();
mNavigationBarHeight = getHeight();
// the coordinates (x,y) of the start point before curve
mFirstCurveStartPoint.set((mNavigationBarWidth / 2) - (CURVE_CIRCLE_RADIUS * 2) - (CURVE_CIRCLE_RADIUS / 3), 0);
// the coordinates (x,y) of the end point after curve
mFirstCurveEndPoint.set(mNavigationBarWidth / 2, CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4));
// same thing for the second curve
mSecondCurveStartPoint = mFirstCurveEndPoint;
mSecondCurveEndPoint.set((mNavigationBarWidth / 2) + (CURVE_CIRCLE_RADIUS * 2) + (CURVE_CIRCLE_RADIUS / 3), 0);
// the coordinates (x,y) of the 1st control point on a cubic curve
mFirstCurveControlPoint1.set(mFirstCurveStartPoint.x + CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4), mFirstCurveStartPoint.y);
// the coordinates (x,y) of the 2nd control point on a cubic curve
mFirstCurveControlPoint2.set(mFirstCurveEndPoint.x - (CURVE_CIRCLE_RADIUS * 2) + CURVE_CIRCLE_RADIUS, mFirstCurveEndPoint.y);
mSecondCurveControlPoint1.set(mSecondCurveStartPoint.x + (CURVE_CIRCLE_RADIUS * 2) - CURVE_CIRCLE_RADIUS, mSecondCurveStartPoint.y);
mSecondCurveControlPoint2.set(mSecondCurveEndPoint.x - (CURVE_CIRCLE_RADIUS + (CURVE_CIRCLE_RADIUS / 4)), mSecondCurveEndPoint.y);
mPath.reset();
mPath.moveTo(0, 0);
mPath.lineTo(mFirstCurveStartPoint.x, mFirstCurveStartPoint.y);
mPath.cubicTo(mFirstCurveControlPoint1.x, mFirstCurveControlPoint1.y,
mFirstCurveControlPoint2.x, mFirstCurveControlPoint2.y,
mFirstCurveEndPoint.x, mFirstCurveEndPoint.y);
mPath.cubicTo(mSecondCurveControlPoint1.x, mSecondCurveControlPoint1.y,
mSecondCurveControlPoint2.x, mSecondCurveControlPoint2.y,
mSecondCurveEndPoint.x, mSecondCurveEndPoint.y);
mPath.lineTo(mNavigationBarWidth, 0);
mPath.lineTo(mNavigationBarWidth, mNavigationBarHeight);
mPath.lineTo(0, mNavigationBarHeight);
mPath.close();
@Override
protected void onDraw(Canvas canvas)
super.onDraw(canvas);
canvas.drawPath(mPath, mPaint);
现在这样使用
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinatorlayout"
android:layout_
android:layout_
android:orientation="vertical">
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_
android:layout_
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"
android:layout_marginBottom="30dp"
android:clickable="true"
android:focusable="true" />
<neel.com.demo.CustomBottomNavigationView
android:id="@+id/customBottomBar"
android:layout_
android:layout_
android:layout_alignParentBottom="true"
android:background="@color/colorAccent"
app:labelVisibilityMode="labeled" />
</RelativeLayout>
活动
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CustomBottomNavigationView curvedBottomNavigationView = findViewById(R.id.customBottomBar);
curvedBottomNavigationView.inflateMenu(R.menu.bottom_menu);
输出
第二种方式
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_
android:layout_gravity="bottom">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_
android:layout_
android:clickable="true"
android:focusable="true"
app:layout_anchor="@id/bar" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bar"
android:layout_
android:layout_
android:layout_gravity="bottom"
android:backgroundTint="@color/colorPrimaryDark">
<LinearLayout
android:layout_
android:layout_
android:orientation="horizontal">
<TextView
style="?android:attr/borderlessButtonStyle"
android:layout_
android:layout_
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:drawableTop="@drawable/ic_favorite"
android:gravity="center"
android:orientation="vertical"
android:text="Personal"
android:textColor="#FFFFFF">
</TextView>
<TextView
style="?android:attr/borderlessButtonStyle"
android:layout_
android:layout_
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:drawableTop="@drawable/ic_favorite"
android:gravity="center"
android:orientation="vertical"
android:text="Personal"
android:textColor="#FFFFFF">
</TextView>
<TextView
style="?android:attr/borderlessButtonStyle"
android:layout_
android:layout_
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:drawableTop="@drawable/ic_favorite"
android:gravity="center"
android:orientation="vertical"
android:textColor="#FFFFFF"
android:visibility="invisible">
</TextView>
<TextView
style="?android:attr/borderlessButtonStyle"
android:layout_
android:layout_
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:drawableTop="@drawable/ic_favorite"
android:gravity="center"
android:orientation="vertical"
android:text="Personal"
android:textColor="#FFFFFF">
</TextView>
<TextView
style="?android:attr/borderlessButtonStyle"
android:layout_
android:layout_
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:drawableTop="@drawable/ic_favorite"
android:gravity="center"
android:orientation="vertical"
android:text="Personal"
android:textColor="#FFFFFF">
</TextView>
</LinearLayout>
</com.google.android.material.bottomappbar.BottomAppBar>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
输出
【讨论】:
@CasparGeerlings 欢迎让我知道是否需要任何帮助 @NileshRathod 我发现“第二种方式”可能是最接近我希望的方式。再次,非常感谢! 我尝试了它的第一种工作方式,我想在工厂的两边只放一个项目,我该怎么做 @NileshRathod 在第二种方法中,BottomAppBar 中的第一个 textView 之前留有一些空间,请您帮忙删除它吗? 对于第二种解决方案,任何人都知道如何设置 textview 的样式,以便在单击时显示为活动状态?【参考方案2】:有一种更简单的方法可以组合这 3 个小部件。我们可以有类似的东西:
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator"
android:layout_
android:layout_>
<androidx.fragment.app.FragmentContainerView
android:layout_
android:layout_
android:layout_marginBottom="?attr/actionBarSize" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottomBar"
android:layout_
android:layout_
android:layout_gravity="bottom"
app:backgroundTint="?attr/colorPrimary"
app:contentInsetEnd="0dp"
app:contentInsetStart="0dp"
app:fabAlignmentMode="end">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_
android:layout_
app:backgroundTint="@android:color/transparent"
app:elevation="0dp"
android:layout_marginEnd="100dp"
app:itemIconTint="@android:color/white"
app:itemRippleColor="@android:color/white"
app:itemTextColor="@android:color/white"
app:menu="@menu/menu_activity_home_bottom_navigation" />
</com.google.android.material.bottomappbar.BottomAppBar>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:src="@drawable/ic_add_white_24dp"
android:layout_
android:layout_
app:layout_anchor="@id/bottomBar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
就是这样。
根据 FAB 的锚定位置 (CENTER|END),菜单图标将放置在左侧或中间:
对于此示例,结果将类似于:
如果您认为有更好的方法,请随时编辑/评论,以便我修复帖子:)
【讨论】:
FAB 锚定在 CENTER 时不起作用。 @Shefchenko 它适用于中心模式。只需使用app:fabAlignmentMode="center"
@Shefchenko 要在中间工作,只需像 GrafOrlov 所说的那样添加 app:fabAlignmentMode="center"
并删除 android:layout_marginEnd="100dp"
这似乎不适用于 API 21,在我的手机上运行最新版本的 Android 运行完美,但在 API 21 上的模拟器中运行它会显示 BottomAppBar 两倍大并且 BottomNavigationView 保持不变固定在顶部,好像它有一个 margin_bottom
你如何像底部导航中的其他项目一样处理 FAB 操作?【参考方案3】:
我找到了一个更快的解决方案。我将bottomnavigationview包裹在frameLayout中,一切都按预期工作。试试这个:
<?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:id="@+id/lt_content"
android:layout_
android:layout_
android:background="@color/white"
android:fitsSystemWindows="false">
<ViewPager
android:id="@+id/main_pager"
android:layout_
android:layout_
android:layout_above="@+id/bottom_navigation"
android:layout_alignParentStart="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottom_bar"
android:layout_
android:layout_
android:layout_gravity="bottom"
android:clickable="false"
app:fabAlignmentMode="center" />
<FrameLayout
android:layout_
android:layout_
android:layout_gravity="bottom">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
style="@style/BottomNavigationStyle"
android:layout_
android:layout_
android:clickable="false"
app:menu="@menu/bottom_menu" />
</FrameLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_
android:layout_
app:layout_anchor="@id/bottom_bar" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
【讨论】:
完美运行,尤其是在使用 BottomAppBar 添加 FAB 时。过去几个小时一直在尝试解决此问题,谢谢! 你在BottomNavigationStyle中定义了什么样的样式 style="@style/BottomNavigationStyle" 你如何像底部导航中的其他项目一样处理 FAB 操作?【参考方案4】:致所有使用Nilesh Rathod comment 的第二种方法并希望在第一次文本视图之前删除意外空格的人:
只需将app:contentInsetStart="0dp"
设置为BottomAppBar
【讨论】:
【参考方案5】:我尝试了几天,最后我做了一个带有空BottomAppBar
的FAB,上面叠加了一个透明背景的BottomNavigationView
。
在我的例子中,代码如下所示:
<?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_
android:layout_>
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottom_app_bar"
style="@style/Widget.MaterialComponents.BottomAppBar"
android:layout_
android:layout_
android:layout_gravity="bottom"
app:backgroundTint="@color/colorGray"
app:fabAlignmentMode="center" />
<FrameLayout
android:layout_
android:layout_
android:layout_gravity="bottom">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_
android:layout_
android:layout_gravity="bottom"
android:background="#80FFFFFF"
android:icon="@drawable/bottom_nav_ic_assignment"
app:itemIconTint="@color/bottom_nav_item_color"
app:itemTextColor="@color/bottom_nav_item_color"
app:labelVisibilityMode="selected"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
</FrameLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/addFab"
android:layout_
android:layout_
android:focusable="true"
android:onClick="onFabClicked"
android:src="@drawable/ic_add_white"
app:backgroundTint="@color/colorBlue"
app:fabSize="auto"
app:layout_anchor="@+id/bottom_app_bar"
app:layout_anchorGravity="center|top" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
【讨论】:
【参考方案6】:你可以把BottomAppBar当作工具栏,这意味着,这个类扩展了ViewGroup,所以你可以在BottomAppBar标签内添加相对布局、约束布局等。这是在 BottomAppBar 中显示 2 个按钮的代码片段。
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bar"
style="@style/Widget.MaterialComponents.BottomAppBar"
android:layout_
android:layout_
android:layout_gravity="bottom"
app:backgroundTint="@color/cardview_dark_background"
app:fabAlignmentMode="center">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_
android:layout_>
<Button
android:id="@+id/leftbutton"
android:layout_
android:layout_
android:layout_marginRight="32dp"
android:text="LEFT"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/rightbutton"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/rightbutton"
android:layout_
android:layout_
android:layout_marginLeft="32dp"
android:layout_marginRight="16dp"
android:text="RIGHT"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/leftbutton"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.bottomappbar.BottomAppBar>
【讨论】:
以上是关于如何将 BottomAppBar + FAB 与 BottomNavigationView 结合使用的主要内容,如果未能解决你的问题,请参考以下文章