My Fragment appears underneath the Toolbar regardless of how I change constraints - android

No matter how I add constraints, nothing changes in terms of my Fragment which contains a RecyclerView.
I have added a toolbar:
OnCreate in MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)//TODO find inconsistencies
val demo = DemoDataBuilder()
// Set the toolbar as support action bar
setSupportActionBar(toolbar)
// Now get the support action bar
val actionBar = supportActionBar
// Set toolbar title/app title
actionBar!!.title = "AwezaMed"
// Set action bar/toolbar sub title
actionBar.subtitle = "App subtitle"
// Set action bar elevation
actionBar.elevation = 4.0F
toolbar.setNavigationIcon(R.drawable.ic_arrow_back_black_24dp)
toolbar.setNavigationOnClickListener { _ -> onBackPressed()}
startCategoryHome(savedInstanceState)
}
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"
android:id="#+id/root_layout">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="?attr/colorPrimary"
android:minHeight="57dp"
android:padding="1dp"
app:layout_constraintTop_toTopOf="parent"
app:subtitleTextColor="#f5fbff"
app:titleTextColor="#fff" />
<fragment
android:id="#+id/categoyList_fragment"
android:name="za.co.aweza.awezamediphrases.fragments.CategoryListFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="112dp"
app:layout_constraintTop_toBottomOf="#+id/toolbar"
tools:layout="#layout/fragment_hcp_category" />
</android.support.constraint.ConstraintLayout>
You'll notice that in the layout code above, I have a very farfetched constraint that I tried to use.
Here is how it appears in designView:
But regardless of anything I try, the first cell of the RV in the Fragment sits mostly underneath the toolbar. What am I missing?

instead of fragment put this in your xml:
`<FrameLayout
android:id="#+id/frameLayout_fragment_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#id/toolbar" />`
Replace the frame layout with any fragment you want using this function:
` fun openFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.frameLayout_fragment_container, fragment)
transaction.addToBackStack(null)
transaction.commit()
}`

#EliodeBeirut's answer worked, but I found that changing the layout activity_main.xml from Constraint layout to Linear Layout worked as well.

Related

Bottom Navigation Bar Android Jumps on changing fragments

I implemented the bottom navigation bar using Navigation Component, it works fine when changing fragments using the bottom navigation bar. But when I navigate from within a fragment, it jumps and creates a white space - gif
How can I fix this?
Here is the XML for activity_main
<?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:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_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_toTopOf="#+id/bottom_nav_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_nav_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/bottom_menu"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Edit :
This problem was caused because I tried to show and hide the top toolbar.
I used the following code to show/hide toolbar in the OnCreateView() function of the fragments
code : (activity as AppCompatActivity).supportActionBar?.show() and (activity as AppCompatActivity).supportActionBar?.hide()
How can I fix the above problem without losing the hide/show toolbar functionality?
This might be a bit late, but perhaps helps someone else.
So, to solve this, you have to do 3 things:
1: Add the action bar as an overlay over your layout system:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
<item name="windowActionBarOverlay">true</item>
</style>
This ensures that hiding the BottomNavigationView doesn't jump, flicker, or create any unwanted white space. Obviously, this will overlap your layout, so you could set a marginTop of the preferred action bar height.
So, so far no more white space and jumps on the bottom, but this leads to another problem, there will be a permanent whitespace when the action bar is hidden. Combatting this leads to the next point
2: Add an auxiliary view instead of a top margin of height exactly as of the action bar's:
<?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:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/toolbar_placeholder"
android:layout_width="match_parent"
android:layout_height="#dimen/toolbar_height" />
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/toolbar_placeholder" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="#dimen/bottom_nav_height"
android:layout_alignParentBottom="true"
app:itemBackground="#drawable/bg_bottom_nav_item"
app:itemIconTint="#drawable/nav_item_color_state"
app:itemTextColor="#drawable/nav_item_color_state"
app:labelVisibilityMode="labeled" />
</RelativeLayout>
We will show/hide this auxiliary view at the top of the layout, named toolbar_placeholder
3: Actually hide and show the toolbar placeholder:
myRecyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
val mainActivity = requireActivity() as MainActivity
var scrollDown = false
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (scrollDown) {
// set toolbar_placeholder visibility to View.GONE
// hide the action bar
// hide the bottom navigation bar (preferably with animation)
} else {
// set toolbar_placeholder visibility to View.VISIBLE
// show the action bar
// show the bottom navigation bar (preferably with animation)
}
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 50) {
scrollDown = true
} else if (dy < -5) {
scrollDown = false
}
}
})
PS: (you can try setting a marginTop anc changing the LayoutParams alltogether programatically instead of hiding and showing the toolbar placeholder View, but I chose the latter one)
Cheers!

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 change at runtime a fragment defined in a layout by <fragment>

I am building an activity (MainActivity.kt) with a "android.support.design.widget.BottomNavigationView" which has multiple tabs. When a user taps a tab then "BottomNavigationView.OnNavigationItemSelectedListener" should present a corresponding fragment.
Question:
How to set at runtime in onCreateView method a different fragment than the defined in layout file by "android:name" attribute.
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
}
Layout/ActivityMain.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:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.developer.myapplication.Locator"
android:id="#+id/fragment"
/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/navigation"
app:layout_constraintTop_toBottomOf="#+id/fragment"
app:layout_constraintStart_toStartOf="parent"/>
</android.support.constraint.ConstraintLayout>
If you want to change a fragment at runtime you should be using a FrameLayout as a container for your fragment instead of using the fragment tag. Once you use a FrameLayout, it is very easy to swap fragments during runtime; you can just use getFragmentManager().beginTransaction().replace(R.id.FrameLayoutId, new MyFragment()).commit(). Furthermore, you normally use the fragment tag for fragments that are there to stay (meaning you are not planning to replace them or modify them during runtime). For more information you can take a look at how master-detail view is implemented with fragments (Android: when / why should I use FrameLayout instead of Fragment?)
Hope this helps! :)

How to change navigation draver icon size?

I was looking for different ways to do this using styles or programmatically, but not one way worked. Is it possible to increase the size of this icon in any way?
Toolbar:
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:background="#color/color_primary"
android:layout_width="match_parent"
android:layout_height="#dimen/toolbar_h">
<RelativeLayout
android:id="#+id/toolbar_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:clickable="true"
android:focusable="true"
....
Activity:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<include
layout="#layout/toolbar"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.DrawerLayout
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="end">
....
Activity code:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_skeleton)
val toggle = object : ActionBarDrawerToggle(this, drawer, toolbar, R.string.nd_open, R.string.nd_close) {}
drawer.addDrawerListener(toggle)
toggle.syncState()
}
You can change the size of Navigation Drawer icons by overriding design_navigation_icon_size attribute in dimens.xml
<dimen name="design_navigation_icon_size" tools:override="true">40dp</dimen>
If you want to add a custom nav icon to your action bar you should be doing as follows -
onCreate(Bundle savedInstanceState){
......
setSupportActionBar(binding.toolbar);
if (
getSupportActionBar() != null
) {
setTitle("");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.icon_menu);
}
......
This will set a custom drawable button to your action bar. and as you are setting it itself, now make it as per your size. or even if you want you can set different icon for different screen size.

Categories

Resources