BottomAppBar with BottomNavigationDrawer connected to NavigationController - android

Hi I'm trying to use new arch components in my project. Short description what I want to achieve:
When user is on MainFragment I want to display navigation icon (Hamburger) on BottomAppBar. User is able to click navigation icon and display BottomNavigationDrawer
When user select some menu item, or click something on MainFragment he is moved to another fragment, let say DebtDetailsFragment. Then Hamburger should be replaced with 'Back arrow' by NavigationController
Below I pasted my MainActivity code. When I comment line with navigation controller, the Hamburger icon is visible and BottomNavigationDrawer is able to display.
But when I uncomment this line, the Hamburger disappear because NavigationController knows nothing about NavigationView used in BottomNavigationDrawer. I don't use DrawerLayout, so controller thinks Hamburger is not needed.
Method setupWithNavController can control Hamburger icon and back arrow, but I have to provide DrawerLayout as parameter which I don't use.
Documentation for this method:
The Toolbar will also display the Up button when you are on a non-root destination and the drawer icon when on the root destination, automatically animating between them. This method will call [DrawerLayout.navigateUp] when the navigation icon is clicked.
So the question is, how to display Hamburger icon when NavigationController is connected with BottomAppBar but without DrawerLayout? I will handle hamburger click myself in onOptionsItemSelected method.
class MainActivity : BaseActivity() {
#Inject
lateinit var viewModelProvider: ViewModelProvider.Factory
private val viewModel: MainActivityViewModel by lazy {
ViewModelProviders.of(this, viewModelProvider).get(MainActivityViewModel::class.java)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(bottomAppBar)
val navController = findNavController(R.id.main_nav_host_fragment)
//bottomAppBar.setupWithNavController(navController)
onDestroyDisposables += viewModel.uiStateObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(::render, Timber::e)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.bottomappbar_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
android.R.id.home -> {
val bottomNavDrawerFragment = BottomNavigationDrawerFragment()
bottomNavDrawerFragment.show(supportFragmentManager, bottomNavDrawerFragment.tag)
}
}
return super.onOptionsItemSelected(item)
}
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.main_nav_host_fragment).navigateUp()
}
}
Without setted Navigation controller:
BottomNavigationDrawer
With setted NavigationController - Hamburger invisible.

The BottomAppBar should never display an Up button as per the anatomy of the BottomAppBar - it should only ever display the drawer icon. As seen in the behavior documentation, the Up button should be displayed in a top Toolbar.
Therefore, you should never be calling bottomAppBar.setupWithNavController(navController), but instead calling setupWithNavController(navController) using whatever top Toolbar you have.
To set up your BottomAppBar, you should instead set your own drawer icon and handle clicks on the drawer icon yourself.
The DrawerArrowDrawable class is available to give you a correct drawer icon:
val icon = DrawerArrowDrawable(bottomAppBar.context)
bottomAppBar.navigationIcon = icon

I implemented the BottomAppBar with Jetpack navigation drawer component as following way in my app.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
BottomAppBar bottomAppBar = findViewById(R.id.bottomAppBar);
setSupportActionBar(bottomAppBar);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
NavigationView navigationView = findViewById(R.id.nav_view);
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupWithNavController(navigationView, navController);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
return NavigationUI.navigateUp(navController, drawer)
|| 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:headerLayout="#layout/nav_header_main"
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">
<include layout="#layout/content_main" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bottomAppBar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_gravity="bottom"
app:fabCradleVerticalOffset="16dp"
app:navigationIcon="#drawable/ic_baseline_menu_24"
app:navigationContentDescription="#string/nav_header_desc"
app:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
style="#style/Widget.MaterialComponents.BottomAppBar.Colored"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_menu_send"
app:layout_anchor="#id/bottomAppBar"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
The result will be like this

Related

navigation drawer with navigation component back arrow not show when drawer is open [duplicate]

I'm having some trouble to add options menu on a single fragment because it's breaking the navigation Up. Here my code
I have an single Activity with NoActionBar style and with this layout
<layout 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:fitsSystemWindows="true"
tools:context=".ui.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<fragment
android:id="#+id/mainNavigationFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/main_graph" />
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbarLayout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="top">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:labelVisibilityMode="labeled"
app:menu="#menu/main_bottom_nav" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
in activity onCreate I make this setup for navigation
private fun setupNavigation() {
val navController = findNavController(R.id.mainNavigationFragment)
//each fragment of botton nav
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.actionSchedule,
R.id.actionPayment,
R.id.actionNotification,
R.id.actionAccount))
toolbar.setupWithNavController(navController, appBarConfiguration)
bottomNavigationView.setupWithNavController(navController)
}
override fun onSupportNavigateUp() =
findNavController(R.id.mainNavigationFragment).navigateUp()
on each botton nav fragment I have some destinations and everything work as expected.
Now I need to add an menu only on right most fragment of botton nav, then on this specific fragment I add setHasOptionsMenu(true) in onCreate and inflate menu in onCreateOptionsMenu, but the menu does not appear.
Then I add setSupportActionBar(toolbar) on activity onCreate.
Now the menu appear only on this fragment but it's broke all 'UP' (back arrow on toolbar) of any destination (the back arrow appears, but when i press nothing happens). If I remove setSupportActionBar(toolbar) of activity the UP work again but not the toolbar menu.
What I need to do to make the menu work only in one fragment without broke anything else?
Thanks
If you're using setSupportActionBar, you must use setupActionBarWithNavController(), not toolbar.setupWithNavController as per the documentation.
if using toolbar do this:
in Acitvity:
class MyActivity : AppCompatActivity() {
private var currentNavController: NavController? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
currentNavController = findNavController(R.id.settingNavHost)
currentNavController?.let {
val appBarConfiguration = AppBarConfiguration
.Builder()
.setFallbackOnNavigateUpListener {
onBackPressed()
true
}.build()
setSupportActionBar(toolbar)
toolbar.setupWithNavController(it, appBarConfiguration)
}
}
}
and in fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.my_menu, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_save) {
saveInfo()
}
return super.onOptionsItemSelected(item)
}

Navigation Drawer replace fragment with another problematically retaining the drawer option

I have a basic app, containing a drawer layout. Each option in the drawer opens up a new fragment page for the user to view. Here is the drawer layout code.
<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:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
Here is the app_bar_main.xml code.
<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=".DashBoardActivity">
<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" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And here is the content_main.xml. And also the main activity DashBoardActivity.kt
class DashBoardActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
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_company_emissions, R.id.nav_your_emissions, R.id.nav_contact,
R.id.nav_privacy_policy
), 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()
}
}
From my understanding when you click a button in the drawer layout the fragment in the host fragment is replaced with the relevant clicked on fragment.
My questions is... How do I programatically put another fragment in this nav host view which hasn't been set up with the drawer layout. My problem is one of the fragments contains a profile for a user with an edit button. I want to open a new fragment when the edit button is clicked and wish for the user to still have access to the drawer when they do so don't want to go to another activity.
In my mind I just want to replace the profile_fragment with the edit_fragment. But when I've tried something like this below its placed the fragment onto of the profile one so that you have this weird looking screen with two fragments on top of one another.
val frag: Fragment = EditYourEmissionsFragment()
val fragMan:FragmentManager = activity!!.supportFragmentManager
val fragTran: FragmentTransaction = fragMan.beginTransaction()
fragTran.add(R.id.nav_host_fragment, frag)
fragTran.addToBackStack(null)
fragTran.commit()
I'm still a relative novice when it comes to Android and Kotlin so am struggling to understand how I would go about doing my user case.
Scenario for trying to help paint a picture of what I want to happen:
User loads app and lands on home_fragment.
Opens drawer menu, clicks profile_fragment.
Once in profile_fragment opens the user presses edit.
A new fragment takes over the screen, but retains the ability to open the menu drawer.
User can now either open the drawer menu and navigate to other places across the app. Or save their profile and get taken back to the profile_fragment.
Thank you in advance for any help. Apologies if its not particularly clear.
include the EditYourEmissionsFragment in the navigationGraph like this:
<fragment
android:id="#+id/EditYourEmissionsFragment"
android:name="com.example.application.EditYourEmissionsFragment"
android:label="Edit Profile"
tools:layout="#layout/fragment_edit_omissions"/>
and then on your ProfileFragment set the onClick to open the destination like this;
val navController = Navigation.findNavController(view)
navController.navigate(R.id.EditYourEmissionsFragment)
and in your MainActivity
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.my_nav_host)
return navController.navigateUp(myAppBarConfiguration) || super.onSupportNavigateUp()
}

No drawer view found with gravity LEFT - Android

My menu was opening from left to right, but I want it to be opened from right to left. I have arranged the code to be opened from right to left, but now the code does not work I get the following error.
java.lang.IllegalArgumentException: No drawer view found with gravity LEFT
I get an error on this line : return NavigationUI.navigateUp(navController, mAppBarConfiguration)
MAİN CLASS
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
NavigationView navigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_sss, R.id.nav_gelisim,
R.id.nav_destek,R.id.nav_hakkımızda,R.id.nav_instagram)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
return NavigationUI.navigateUp(navController, mAppBarConfiguration)
|| super.onSupportNavigateUp();
}
}
XML
<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:layoutDirection="rtl"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:itemTextColor="#color/white"
app:itemTextAppearance="#style/NavDrawerTextStyle"
android:layout_gravity="right"
android:theme="#style/NavigationView"
android:background="#color/NavItem"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main2"
app:menu="#menu/activity_main_drawer"/>
</androidx.drawerlayout.widget.DrawerLayout>
You need to handle navigation click on Toolbar like below:
ViewCompat.setLayoutDirection(toolbar, ViewCompat.LAYOUT_DIRECTION_RTL);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (drawer.isDrawerOpen(GravityCompat.END))
drawer.closeDrawer(GravityCompat.END);
else
drawer.openDrawer(GravityCompat.END);
}
});
Also don't forgot to close drawer whenever needed like below:
drawer.closeDrawer(GravityCompat.END)
You could possible find your answer here in this previous post:
Android DrawerLayout - No drawer view found with gravity
You have to set layout Direction in Your Root Drawer Layout
android:layoutDirection="rtl"
If you set the layout direction of the root drawer as follows:
android:layoutDirection="rtl"
All elements will change direction. To solve this, place all elements within a LinearLayout and set LinearLayout direction to ltr.
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
**android:layoutDirection="rtl"**
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
**android:layoutDirection="ltr"**>
<!--place your layout here-->
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:layoutDirection="rtl"
app:menu="#menu/drawer_menu" />
</androidx.drawerlayout.widget.DrawerLayout>
If you are using the navigation component use the below code in your activity class.
override fun onCreate(savedInstanceState: Bundle?) {
//...
val navHostFragment = supportFragmentManager.findFragmentById(
R.id.my_nav_host_fragment
) as NavHostFragment
navController = navHostFragment.navController
binding.navigationView.setupWithNavController(navController)
//...
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController, binding.drawerLayout)
}
override fun onBackPressed() {
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
It seems like the DrawerLayout need to be in the start direction every time,
if you want it to open from the left you need to do the following:
make your DrawerLayout android:layoutDirection="ltr"
remove tools:openDrawer from your DrawerLayout XML
put your NaviagtionView android:layout_gravity="start"
if you want your content to be RTL don't forget to do this in your contentView container android:layoutDirection="rtl"
and it will work like a charm!

How to fully implement the Navigation Drawer template in Kotlin

I'm expecting this to be a really simple answer. I am developing my first real app on android (a workout tracker) and I am wanting it to have a navigation drawer layout for the majority of the app. I have a bunch of pages that I want the drawer to navigate to. I have figured out how to change the names of the menu items in activity_main_drawer.xml menu file, but I don't know how to attach a navigation to when the user taps on them. My code is the default code from the template:
MainActivity.kt
package com.example.grahamfitnesstracker
import android.os.Bundle
import android.support.design.widget.FloatingActionButton
import android.support.design.widget.Snackbar
import android.support.v4.view.GravityCompat
import android.support.v7.app.ActionBarDrawerToggle
import android.view.MenuItem
import android.support.v4.widget.DrawerLayout
import android.support.design.widget.NavigationView
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
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 toggle = ActionBarDrawerToggle(
this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
navView.setNavigationItemSelectedListener(this)
}
override fun onBackPressed() {
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
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 onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
when (item.itemId) {
R.id.nav_current_workout -> {
// Handle the camera action
}
R.id.nav_log -> {
}
R.id.nav_exercises -> {
}
R.id.nav_workouts -> {
}
R.id.nav_stats -> {
}
R.id.nav_settings -> {
}
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
drawerLayout.closeDrawer(GravityCompat.START)
return true
}
}
And the nav drawer menu activity_main_drawer.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="#+id/nav_current_workout"
android:icon="#drawable/ic_menu_play_filled"
android:title="#string/menu_current_workout"/>
<item
android:id="#+id/nav_log"
android:icon="#drawable/ic_menu_log"
android:title="#string/menu_log"/>
<item
android:id="#+id/nav_exercises"
android:icon="#drawable/ic_menu_weight"
android:title="#string/menu_exercises"/>
<item
android:id="#+id/nav_workouts"
android:icon="#drawable/ic_menu_person"
android:title="#string/menu_workouts"/>
<item
android:id="#+id/nav_stats"
android:icon="#drawable/ic_menu_chart"
android:title="#string/menu_stats"/>
</group>
<item android:title="">
<menu>
<item
android:id="#+id/nav_settings"
android:icon="#drawable/ic_menu_settings"
android:title="#string/menu_settings"/>
</menu>
</item>
</menu
And finally content_main.xml which I assume is where I need to put fragments (which I still don't fully understand...). From one guide I changed it to including a FrameLayout as:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/app_bar_main"
tools:context=".MainActivity">
<!-- Note : This is the container Frame Layout for all the fragments-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainFrame">
</FrameLayout>
</android.support.constraint.ConstraintLayout>
I really just don't know what to add into the onNavigationItemSelected items in MainActivity.kt in order to handle navigation. I have learned that I need to replace the content in content_main.xml with some fragment where I put in my page's ui, but I don't know how to do that. Can anyone help me out? I've looked at a bunch of examples, but they usually implement their own custom nav bars, or are using java which confuses me as a newcomer.
Have you included the NavigationView in your activity_main and add this nav_header_man to that view like this:
activity_main
<?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:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" >
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>
And since you asked for what to add into the onNavigationItemSelected items in MainActivity.kt,
MainActivity.kt
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
val i = Intent()
when (item.itemId) {
R.id.nav_current_workout -> {
i.setClass(this, CurrentWorkoutActivity::class.java)
startActivity(i)
}
R.id.nav_log -> {
//similarly start activity with Intent
}
R.id.nav_exercises -> {}
R.id.nav_workouts -> {}
R.id.nav_stats -> {}
R.id.nav_settings -> {}
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
drawerLayout.closeDrawer(GravityCompat.START)
return true
}
}
Hope this helps.
Xml Layout for navigation drawer:
<include
layout="#layout/dashboard_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"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/drawer_menu" />
Implementation In Kotlin file:
class MainActivity :
AppCompatActivity(),NavigationView.OnNavigationItemSelectedListener {
private lateinit var drawer: DrawerLayout
private lateinit var navigationView: NavigationView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar: Toolbar = findViewById(R.id.toolbar_main)
setSupportActionBar(toolbar)
drawer = findViewById(R.id.drawer_layout)
toggle = ActionBarDrawerToggle(
dis,
drawer,
toolbar,
R.string.navigation_drawer_open,
R.string.navigation_drawer_close
)
drawer.addDrawerListener(toggle)
toggle.setDrawerIndicatorEnabled(true)
toggle.syncState()
navigationView = findViewById(R.id.nav_view)
navigationView.setNavigationItemSelectedListener(dis)
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.login_signup -> {
startActivity(Intent(dis, LoginActivity::class.java).putExtra("go_to","0"))
}
}
drawer.closeDrawers()
return true
}
}

DrawerLayout getting stuck on swipe

I am playing around with DrawerLayout and I am encountering an issue. Basically sometimes when i swipe from the edge of the screen the DrawerLayout will get stuck until i lift my finger off the screen (See screenshot below)
I am not sure what is up, I followed the code sample from the google sdk exactly. Any ideas?
And here is the only thing i have in my FragmentActivity:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final String[] names =
getResources().getStringArray(R.array.nav_names);
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(
getActionBar().getThemedContext(),
android.R.layout.simple_list_item_1, names);
final DrawerLayout drawer =
(DrawerLayout)findViewById(R.id.drawer_layout);
final ListView navList =
(ListView) findViewById(R.id.drawer);
navList.setAdapter(adapter);
navList.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> parent,
View view, final int pos, long id)
{
drawer.setDrawerListener(
new DrawerLayout.SimpleDrawerListener()
{
#Override
public void onDrawerClosed(View drawerView)
{
super.onDrawerClosed(drawerView);
}
});
drawer.closeDrawer(navList);
}
});
}
EDIT:I'm adding a bounty on this, as this is a very old issue that exists even today with the latest Android-X (sample available here). Here's how it looks:
I've reported about it to Google (here and later again here), but it didn't help.
I've tried all existing solutions here on this thread, and none worked. If anyone has a good workaround for this (while still using DrawerLayout or extending it, or something similar), please put a working solution.
Note that you can get around this 20dp peek feature by setting the clickable attribute to true on the FrameLayout within the DrawerLayout.
android:clickable="true"
for instance :
http://developer.android.com/training/implementing-navigation/nav-drawer.html
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" />
<!-- The navigation drawer -->
<ListView
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#111"
android:choiceMode="singleChoice"
android:divider="#android:color/darker_gray"
android:dividerHeight="1dp" />
</android.support.v4.widget.DrawerLayout>
If you had shown us your layout xml, we could see if you have DrawerLayout as ROOT element. And whether the inside two children are Main Layout and Navigation Drawer.
According to Create a Drawer Layout:
To add a navigation drawer, declare your user interface with a DrawerLayout object as the root view of your layout. Inside the DrawerLayout, add one view that contains the main content for the screen (your primary layout when the drawer is hidden) and another view that contains the contents of the navigation drawer.
I've managed to work around this by implementing this DrawerListener:
drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() {
#Override
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
if (drawerLayout.isDrawerVisible(Gravity.LEFT) && !drawerLayout.isDrawerOpen(Gravity.LEFT)) {
drawerLayout.closeDrawer(Gravity.LEFT);
}
}
});
Whenever the state changes, if the drawer is visible but not open, it means it's 'peeking', so I close it.
This 20dp Peek feature can be achieved When user drags the drawer to 20dp than open the drawer.
Here is code working fine for me.
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
lateinit var mToggle: ActionBarDrawerToggle
lateinit var mDrawerLayout: DrawerLayout
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()
}
mDrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = findViewById(R.id.nav_view)
mToggle = ActionBarDrawerToggle(
this, mDrawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close
)
mDrawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
override fun onDrawerStateChanged(newState: Int) {
Log.d("onDrawerStateChanged", "$newState")
mToggle.onDrawerStateChanged(newState)
}
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
if ((slideOffset >= (dpToPx(19.9f)/drawerView.width)) && (slideOffset <= (dpToPx(20.1f)/drawerView.width))){
Log.d("onDrawerSlide", "true")
mDrawerLayout.openDrawer(GravityCompat.START)
}
Log.d("onDrawerSlide", "$slideOffset")
mToggle.onDrawerSlide(drawerView,slideOffset)
}
override fun onDrawerClosed(drawerView: View) {
mToggle.onDrawerClosed(drawerView)
}
override fun onDrawerOpened(drawerView: View) {
mToggle.onDrawerOpened(drawerView)
}
})
mToggle.syncState()
navView.setNavigationItemSelectedListener(this)
}
override fun onBackPressed() {
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
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 onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
when (item.itemId) {
R.id.nav_home -> {
// Handle the camera action
}
R.id.nav_gallery -> {
}
R.id.nav_slideshow -> {
}
R.id.nav_tools -> {
}
R.id.nav_share -> {
}
R.id.nav_send -> {
}
}
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
drawerLayout.closeDrawer(GravityCompat.START)
return true
}
fun dpToPx(dps : Float) : Float{
return (dps * Resources.getSystem().displayMetrics.density)
}
}
I can't comment, but the highest voted answer works for me on Android 11. I did it like this:
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true" >
<include
layout="#layout/main_activity"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/side_nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:menu="#menu/side_nav_menu"/>
</androidx.drawerlayout.widget.DrawerLayout>

Categories

Resources