setSupportActionBar and toolbar.inflateMenu don't work together? - android

Calling setSupportActionBar and inflating a menu as follows results in not showing the menu:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(myToolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
myToolbar.inflateMenu(R.menu.my_menu)
<?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"
tools:context=".MainActivity">
<androidx.appcompat.widget.Toolbar
android:id="#+id/myToolbar"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/test"
android:title="Test"
app:showAsAction="always" />
</menu>
I know the alternative is to use the traditional onCreateOptionsMenu method.
Other alternative is to remove the line setSupportActionBar, but I need to show displayHomeAsUpEnabled.
Is it possible to use both myToolbar.inflateMenu() and setSupportActionBar simultaneously or is it incompatible?

When you use setSupportActionBar(), yes, you must solely use the ActionBar APIs and directly manipulating the Toolbar, be it by adding a Menu or other changes, are not supported.
If you don't want to use the ActionBar APIs, then don't call setSupportActionBar and use the Toolbar APIs directly (those are what the ActionBar APIs call anyways, so the Toolbar APIs are a superset of the ActionBar APIs already).
Toolbars have their own APIs for handling the navigation button (which is what displayHomeAsUpEnabled plugs into).
You can take advantage of this by adding the Up icon to your Toolbar:
<androidx.appcompat.widget.Toolbar
android:id="#+id/myToolbar"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="?attr/homeAsUpIndicator" />
Then you can get a callback on when the button is pressed by setting a click listener:
myToolbar.setNavigationOnClickListener {
// handle the navigation button
}

Related

SupportActionBar: How to change the title?

This Layout came from a Studio 3.5.1 template. I can't figure it out. But my question hopefully has an easy answer.
Here is the XML layout:
<?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=".ChecklistActivity">
<androidx.viewpager.widget.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/tabAppBarLayout"
android:theme="#style/AppTheme.AppBarOverlay">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabLayoutView"
app:tabMode="scrollable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary" />
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
In my app Activity (Java), I would like to change the android:label="#string/app_name text (specified in the Activity Manifest) as it appears on the Activity header (appBar, toolBar, whatever it is). I have not been able to do that as of yet.
No, you cannot add or remove or alter anything in the manifest as the name says, it's a manifest file, which system reads when it installs your app.
If you are just trying to change the title you can use:
If you are using ActionBar:
getActionBar().setTitle(int titleId)
If you are extending AppCompatActivity:
getSupportActionBar().setTitle(int titleId)
For example:
public class ChecklistActivity extends AppCompatActivity {
#Override
public void onCreate(...) {
doMyTimerThing();
}
public void doMyTimerThing() {
onTick(long timeLeft) {
...(long to string here)...
getSupportActionBar().setTitle(timeToShowOnActionBar)
}
}
}
Set Up the app bar Android Dev Guide.
ActionBar vs SupportActionBar
ActionBar Docs
This is a simple implementation, If you want to have more control you over your ActionBar as your app progresses then you can use Toolbar. Toolbar has to be added to your XML.

How can I use a toolbar in a fragment but not in activity using Jetpack's Navigation

I have start using Android Jetpack's Navigation library. So far, I find it very easy to use. The problem I'm facing is with the toolbar.
What I want to achieve is:
User opens the app and the login page is shown. This is the start of the navigation, and toolbar should not be shown.
If user logs for the first time, a fragment for resetting his password (autogenerated by administrator) will show, so that the user changes it. Here I want ton show a white toolbar with the back arrow in it (and no text)
If user has logged before, we show a loading screen (with no toolbar either)
I've tried so many things but not succeeded. This first two attempts are using an activity with a NoActionBar style.
Toolbar in the Reset password fragment
Custom Appbar with toolbar. It is showing, but the back arrow is not shown. I tried adding an Imageview inside my toolbar and I also tried setting the icon from java, but neither of those worked.
<?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"
android:background="#color/white"
tools:context=".ResetPasswordFragment">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBar_resetPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScroll="true"
>
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar_resetPassword"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/white"
/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="#+id/scrollView_resetPassword"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<!-- Linear layout -->
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Toolbar in the Activity
This attempt worked. It added a toolbar in my activity like this:
<?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"
android:background="#color/colorAccent"
android:orientation="vertical"
tools:context=".LoginActivity">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
android:background="#color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
/>
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout >
and in my Controller, I added this:
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
AppBarConfiguration appBarConfiguration =
new AppBarConfiguration.Builder(navController.getGraph()).build();
Toolbar toolbar = findViewById(R.id.toolbar);
NavigationUI.setupWithNavController(toolbar, navController, appBarConfiguration);
The problem here is that the toolbar is shown in my first fragment, and I don't want that. I tried removing it from my first fragment and it worked, but when I tried to show it again in my second fragment, for some reason the findViewById didn't recognize my toolbar. So I ended up showing the toolbar again in my first Fragment code, just before the second fragment shows. It worked but you can notice the animation when the toolbar is showing and it doesn't look good, and I know hiding/showing the toolbar directly from my fragment is not ok.
What do I do?
So, what do I need to do achieve this properly? In the future, I'll have an activity with some other fragments too, and I will need a similar behavior as the one I wrote before. I think it will be better if I do this fine since now.
Thanks!
You can Listen for navigation events by using OnDestinationChangedListener.
For example :
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
#Override
public void onDestinationChanged(NavController controller,
NavDestination destination, Bundle arguments) {
// getting the current fragment id.
int currentFragmentID = destination.getId();
if(currentFragmentID == R.id.fragment_with_toolbar){
// showing the toolbar
toolbar.setVisibility(View.VISIBLE);
} else if(currentFragmentID == R.id.fragment_without_toolbar){
// hiding the toolbar
toolbar.setVisibility(View.GONE);
}
}
});

How to replace a fragment using NavController.navigate instead of adding it on top

I'm using the "new" Android Jetpack navigation, instead of relying on FragmentManager. I've got a simple main layout:
<?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"
tools:context=".activities.MainActivity">
<FrameLayout
android:id="#+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#id/bottomNav">
<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:navGraph="#navigation/navigation_app" />
</FrameLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/bottom_menu"
app:layout_constraintBottom_toBottomOf="#+id/root"/>
</android.support.constraint.ConstraintLayout>
And two additional layouts: "Splashscreen" with a TextView, "Login" with a button. The splashscreen fragment is used as starting point, and has a listener defined as follows:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
textView.setOnClickListener{
findNavController(nav_host_fragment).navigate(R.id.action_splashScreenFragment_to_loginFragment)
}
}
screenshot
Instead of replacing TextView fragment with Button fragment, the Button fragment is stacked on top.
I've only encountered workarounds such as setting a background color, and clickable parameter, which I consider to be more of a hack, as I believe that the behaviour is exactly the same, but the fragment below is simply hidden. How to properly switch fragments using the NavController?
Issue seems to have occured because of these two lines:
val host = NavHostFragment.create(R.navigation.navigation_register_login)
supportFragmentManager.beginTransaction().replace(R.id.nav_host_fragment,host).setPrimaryNavigationFragment(host).commit()
I guess they shouldn't be called, and all basic requirements are fulfilled by this line in layout:
android:name="androidx.navigation.fragment.NavHostFragment"
You need to use popUpTo and popUpToInclusive to clear the backstack.
Check the Android Developers docs

How to remove AppBar shadow after app start

I have drawer layout, where I need to remove AppBar shadow for particular page (fragment).
For this I am using this code
fun setToolbarShadow(dropShadow: Boolean) {
if (dropShadow) {
ViewCompat.setElevation(appBar, Utils.dp2px(4, resources))
} else {
ViewCompat.setElevation(appBar, 0f)
}
}
and it works well for others than the first fragment.
I tried put it inside onCreate of activity, onStart, onCreateView and onViewCreated of fragment, but nothing works.
How to set it properly after activity and fragment are created?
EDIT:
I made simple hello world app with one activity to try it, here is the code:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
tools:context="cz.svobodaf.myapplication.MainActivity">
<android.support.design.widget.AppBarLayout
android:id="#+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"/>
<FrameLayout
android:id="#+id/top_nav_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</android.support.design.widget.AppBarLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
ViewCompat.setElevation(appBar, 0f)
}
override fun onResume() {
super.onResume()
ViewCompat.setElevation(appBar, 0f)
}
}
This also doesn't work after start, but when I lock device and unlock again, onResume is called and suddenly it works.
I need to know ehere to put the code to set elevation after app start.
I found solution by setting AppBar outlines to null.
appBar.outlineProvider = null
But I couldn't figure out why elevation doesn't work. It looks like elevation is set after all starting methods are called -.-

Is it possible to embed the floating action button into the action bar?

I want to get something like this. Is it possible?
The answer to this question is not applicable without an actual header item, which I don't have here. Action bar is not an item I can anchor to, or at least I don't know a way to do that.
This can be done using a custom toolbar as supportActionBar.
And instead of using layout_anchor to place the FAB, we can constraint it to the tool bar using ConstraintLayout as a root view.
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="64dp"
android:layout_marginRight="64dp"
android:src="#drawable/ic_baseline_play_arrow_24"
app:layout_constraintBottom_toBottomOf="#id/appbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/appbar" />
</androidx.constraintlayout.widget.ConstraintLayout>
And make sure to use AppTheme with NoActionBar like Theme.MaterialComponents.DayNight.NoActionBar
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
Then set supportActionBar in activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
}
Preview:
I have tried a solution but it isn't direct one and hope it will help
in the root coordinate layout create a child empty layout at the top of it and change the background color to be the same color of action bar , then new empty layout will be after action bar directly and it will be shown as part of the action bar and give it proper height as you want .and your code will be like that
Empty layout:
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:id="#+id/tst"
android:background="#android:color/black"
android:layout_height="50dp"/>
you can increase the height as you want but it is the best height for my device
Floating Actio Button:
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="#id/tst"
app:layout_anchorGravity="top|center"
/>

Categories

Resources