Android 如何在AppBar下方创建剪裁的NavigationDrawer?
如何在AppBar下方创建剪裁的NavigationDrawer 最新的Android Studio(3.5.3)生成了一个全高的NavigationDrawer,我的问题是需要做哪些更改才能得到一个裁剪过的NavigationDrawer ( 请不要通过链接到包含大量古代答案的古代(如2015年)问题,将此问题标记为“重复”。 现在是2020年,我希望现在会有一种简单的方法来实现clipped NavigationDrawer。 希望现在有一个简单的解决方案可以很好地使用androidx和jetpack导航以及Kotlin方法,例如Android 如何在AppBar下方创建剪裁的NavigationDrawer?,android,navigation-drawer,clipped,Android,Navigation Drawer,Clipped,如何在AppBar下方创建剪裁的NavigationDrawer 最新的Android Studio(3.5.3)生成了一个全高的NavigationDrawer,我的问题是需要做哪些更改才能得到一个裁剪过的NavigationDrawer ( 请不要通过链接到包含大量古代答案的古代(如2015年)问题,将此问题标记为“重复”。 现在是2020年,我希望现在会有一种简单的方法来实现clipped NavigationDrawer。 希望现在有一个简单的解决方案可以很好地使用androidx和je
setupActionBarWithNavController
。
当我提到用Android Studio生成的上述代码时,我现在谈论的是Android Studio 3.5.3,即当前的最新版本,以及它的项目模板“导航抽屉活动”,带有Kotlin和最低API级别19,即Android 4.4。
当今天的开发人员想找到一种方法来实现这一点,并使用google和stackoverflow进行搜索时,我们就不想找到并滚动浏览包含大量旧/过时页面答案的长页面。
由于这个问题现在是在2020年2月提出的,所以大家都会清楚,下面所有可能的答案也都将迟于2020年2月。
)
奇怪的是,似乎很难找到关于如何用Android实现裁剪抽屉的文档。
这里提到了导航抽屉的两种类型(“全高”和“剪裁”):
引述:
“标准导航抽屉可以使用以下其中一个立面位置:
At the same elevation as a top app bar (full-height)
At a lower elevation than a top app bar (clipped)"
在上面的网页上还有一个指向android特定页面的链接:
但是,该页面目前没有提到任何关于如何创建裁剪NavigationDrawer的内容。
此外,android页面似乎没有太多更新,因为它当前链接到关于抽屉布局的旧SupportV4库
当我看到androidx关于抽屉布局的新页面时,我仍然找不到关于“剪裁”抽屉的任何信息。
(既然“clipped”是谷歌材料设计中使用的术语,那么谷歌也应该使用这个词在文档页面中进行搜索)
这里有一些页面可以找到关于“剪辑”的信息,但不幸的是,目前还没有:
为了说明我在寻找什么(独立于上面可能会改变的材料设计页面),我在下面提供了一些图片
下面的第一个屏幕截图是使用Android Studio 3.5.3(目前是最新版本)生成Android应用程序,使用Kotlin和最低API级别19(Android 4.4)生成“导航抽屉活动”后的结果(经过两次修改,见下文)
我(在“activity\u main.xml”中)所做的两个更改是,我从NavigationView中删除了app:headerLayout,并将android:layout\u height=“match\u parent”替换为android:layout\u height=“wrap\u content”。
然后我用GIMP编辑了一些截图来说明我真正想要的,如下图所示。
“汉堡包图标”应该可以关闭NavigationDrawer,即使用它进行切换
下面是使用“全高”NavigationDrawer生成的一些相关文件,我的问题是,要获得上述编辑图片中的“剪裁”NavigationDrawer,我必须进行哪些更改
MainActivity.kt
package com.myapplication
import android.os.Bundle
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import android.view.Menu
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val fab: FloatingActionButton = findViewById(R.id.fab)
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show()
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow,
R.id.nav_tools, R.id.nav_share, R.id.nav_send
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
app_bar_main.xml
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:showIn="@layout/app_bar_main">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
这比我想象的要复杂一些,但这条错误消息帮助我达到了目的: 抽屉布局必须使用MeasureSpec精确测量 以下是Kotlin的解决方案:
class CustomDrawerLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : DrawerLayout(context, attrs, defStyleAttr) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec),MeasureSpec.EXACTLY)
var newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec),MeasureSpec.EXACTLY)
super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec)
}
}
android:layout_height="wrap_content"
var drawerLayout : CustomDrawerLayout = findViewById(R.id.clipped_drawer_layout)
android:layout_marginTop="?android:attr/actionBarSize"
<com.mullr.neurd.Miscellaneous.CustomDrawerLayout
android:id="@+id/clipped_drawer_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:openDrawer="start"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="?android:attr/actionBarSize"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</com.mullr.neurd.Miscellaneous.CustomDrawerLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main)
// NAVIGATION
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
var drawerLayout: CustomDrawerLayout = findViewById(R.id.full_drawer_layout)
navView = findViewById(R.id.nav_view)
// This needs to come after drawerLayout has been initialized
toolbar.setNavigationOnClickListener {
if(drawerLayout.isDrawerOpen(GravityCompat.START)){
drawerLayout.closeDrawer(GravityCompat.START)
}
else{
drawerLayout.openDrawer(GravityCompat.START)
}
}
navController = findNavController(R.id.nav_host_fragment)
navView.itemIconTintList = null
toolbar.setupWithNavController(navcontroller,drawerLayout)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home, R.id.nav_favorites, R.id.nav_settings
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
最后,从styles.xml(v21)文件中删除这一行,以便状态栏不被覆盖:
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
@android:color/transparent
应该这样做。
当导航抽屉打开时,保持应用程序栏在屏幕上的新答案: main\u drawer.xml-包含自定义抽屉布局、应用程序栏主布局和导航视图的新布局文件
<com.example.myapp.Miscellaneous.CustomDrawerLayout 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/full_drawer_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.navigation.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"
app:menu="@menu/activity_main_drawer">
</com.google.android.material.navigation.NavigationView>
</com.example.myapp.Miscellaneous.CustomDrawerLayout>
最终结果应该如下所示:
<com.mullr.neurd.Miscellaneous.CustomDrawerLayout
android:id="@+id/clipped_drawer_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:openDrawer="start"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<include
layout="@layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_marginTop="?android:attr/actionBarSize"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
</com.mullr.neurd.Miscellaneous.CustomDrawerLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main)
// NAVIGATION
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
var drawerLayout: CustomDrawerLayout = findViewById(R.id.full_drawer_layout)
navView = findViewById(R.id.nav_view)
// This needs to come after drawerLayout has been initialized
toolbar.setNavigationOnClickListener {
if(drawerLayout.isDrawerOpen(GravityCompat.START)){
drawerLayout.closeDrawer(GravityCompat.START)
}
else{
drawerLayout.openDrawer(GravityCompat.START)
}
}
navController = findNavController(R.id.nav_host_fragment)
navView.itemIconTintList = null
toolbar.setupWithNavController(navcontroller,drawerLayout)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home, R.id.nav_favorites, R.id.nav_settings
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
要保留工具栏上“后退”按钮(或“向上”按钮)的行为,请确保在活动的onCreate方法()中包含此行:
最后,从styles.xml(v21)文件中删除这一行,以便状态栏不被覆盖:
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
@android:color/transparent
很抱歉,但没有。我现在已经尝试过了,但实际上我在您的屏幕截图中也看到,您似乎误解了什么。在您的屏幕截图中,导航抽屉的标题仍然位于黑色应用程序栏的顶部,隐藏着“汉堡包”图标“。我希望抽屉内容位于应用程序栏的下方,而应用程序栏中的汉堡包图标是可见的,并且可以进行切换,正如我在问题中的第二张(GIMP编辑)图片所示。我更新了我的回答,您必须向XML文件添加一些属性才能实现这一点。很好,但并不完美(如我所希望的那样)。所需的切换效果正在工作(某种程度上