With my most recent app, I've decided to use Conductor for the whole of the navigation. It's a single activity app, and everything works fine, except for the splash screen - for some reason, the initial controller refuses to stretch under the status bar, even though the main layout and the controller host are both CoordinatorLayouts with appropriate fitsSystemWindows flags.
The host activity:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="net.fonix232.app.viewmodel.MainViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:fitsSystemWindows="true">
<net.fonix232.app.common.ChangeHandlerCoordinatorLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
The splash controller's layout:
<?xml version="1.0" encoding="utf-8"?>
<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">
<data>
<variable
name="viewModel"
type="net.fonix232.app.viewmodel.SplashViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:fitsSystemWindows="true">
[... splash content ...]
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
The screen after splash:
<?xml version="1.0" encoding="utf-8"?>
<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">
<data>
<variable
name="viewModel"
type="net.fonix232.app.viewmodel.SplashLandingViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:fitsSystemWindows="true">
[... splash landing content ...]
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
The first splash layout does not stretch the content (a custom image layer setup) under the status bar, however on the second splash (SplashLanding) screen, it is properly stretched - and since I use shared element transition, I get a nice image resize effect on the background as well.
Where is this going wrong?
Related
I am trying to delay inflating some views in a large fragment to avoid a freeze.
in fragment.xml :
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="vm"
type="exm.ViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- some other views -->
<ViewStub
android:id="#+id/fragment_stub"
android:inflatedId="#+id/fragment_content"
android:layout="#layout/fragment_stub_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:vm="#{vm}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_stub_content.xml:
<?xml version="1.0" encoding="utf-8"?>
<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">
<data>
<variable
name="vm"
type="exm.ViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ACustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="#{()->vm.toggleDetails()}"
app:isExpanded="#{vm.canShowDetails}"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Function toggleDetails in vm toggles boolean livedata canShowDetails. I can see that this function is successfully called by a log. But canShowDetails (A MutableLiveData<Boolean>) does not change despite being observed in a view. A trivial code would be fragment_stub?.inflate() in my Fragment's onViewCreated() which is called successfully.
this is some code in my parent xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="(..).viewmodels.TestViewModel"/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:theme="#style/AppTheme.AppBarOverlay"
>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<include
android:id="#+id/layout"
bind:viewModel="#{viewModel}"
layout="#layout/test_content"/>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Here's the child include layout
<layout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="(..).TestViewModel" />
</data>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="horizontal"
tools:layout_editor_absoluteX="74dp"
tools:layout_editor_absoluteY="1dp"
xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/disable"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#color/orange"
android:text="#{viewModel.buttonText}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</layout>
As you can see in this example I data binded text to the viewmodel and passed it down from parent to child via bind:viewModel="#{viewModel}"
Here's the code in the activity:
val binding:TestContentBinding = DataBindingUtil.setContentView(this, R.layout.test_content)
binding.setLifecycleOwner(this)
binding.viewModel = testViewModel
If I set the binding to the child xml then the data gets binded, however if i set the binding to the parent activity the viewmodel doesn't get passed down.
The back end certainly has the correct data however I don't know how to bind data to the child.
I've even tried manually setting the layout's viewModel however that doesn't work. How do I pass the viewmodel?
You could try changing the name of the variable in your included layout.
Here is a quick article explaining how to use include layout with ViewModel.
Is there a way to pass data variable to included layout?
parent layout
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:showIn="#layout/fragment_settings">
<data>
<variable
name="viewModel"
type="......settings.SettingsFragmentViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:overScrollMode="never"
android:padding="#dimen/spacing_default">
<include
android:id="#+id/main"
layout="#layout/layout_settings_main" />
</androidx.constraintlayout.widget.ConstraintLayout>
and want to have viewModel in included 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"
tools:showIn="#layout/fragment_settings">
<data>
<variable
name="viewModel"
type="......settings.SettingsFragmentViewModel" />
</data>
<merge>
...........
or is it possible only when setting like this:
binding = FragmentSettingsBinding.inflate(inflater, container, false).apply {
lifecycleOwner = this#SettingsFragment
viewModel = this#SettingsFragment.viewModel
main.viewModel = this#SettingsFragment.viewModel
}
In parent xml inside include tag pass data variable to included layout using bind:viewModel.
<?xml version="1.0" encoding="utf-8"?>
<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"
xmlns:bind="http://schemas.android.com/apk/res-auto"
tools:showIn="#layout/fragment_settings">
<data>
<variable
name="viewModel"
type="......settings.SettingsFragmentViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:overScrollMode="never"
android:padding="#dimen/spacing_default">
<include
android:id="#+id/main"
layout="#layout/layout_settings_main"
bind:viewModel="#{viewModel}" />
</androidx.constraintlayout.widget.ConstraintLayout>
Your included layout will get object instance in viewModel.
For more details check this tutorial
Open build.grade and add dataBinding { enabled = true } in block android{ // ...}
Ok so, at the beginning of this article I’ve said that it depends whether it’s possible to pass the variable to a secondary layout or not. The reason for this is the following:
Data binding does not support include as a direct child of a merge element. For example, the following layout is not supported:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data>
<variable name="user" type="it.communikein.myunimib.User"/>
</data>
<merge>
<include layout="#layout/name"
bind:user="#{user}"/>
<include layout="#layout/contact"
bind:user="#{user}"/>
</merge>
</layout>
I have the following activity layout:
<?xml version="1.0" encoding="utf-8"?>
<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">
<data>
<import type="com.aurelhubert.ahbottomnavigation.AHBottomNavigation" />
<variable
name="viewModel"
type="[...]android.ui.MainViewModel" />
</data>
<RelativeLayout
android:id="#+id/wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.MainActivity">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/bottom_navigation" />
<com.aurelhubert.ahbottomnavigation.AHBottomNavigation
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="#dimen/bottom_navigation_height"
android:layout_alignParentBottom="true"
app:accentColor="#color/bottomNavActive"
app:defaultBackgroundColor="#{#color/bottomNavBg}"
app:inactiveColor="#color/bottomNavInactive"
app:titleState="#{AHBottomNavigation.TitleState.ALWAYS_SHOW}"
app:useElevation="#{false}" />
<FrameLayout
android:id="#+id/floating_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"/>
</RelativeLayout>
</layout>
And a fragment that is displayed in FrameLayout:
<?xml version="1.0" encoding="utf-8"?>
<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">
<data>
<variable
name="viewModel"
type="[...]android.ui.home.jobs.JobsViewModel" />
<variable
name="navigationViewModel"
type="[...]android.ui.NavigationViewModel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="#dimen/extended_toolbar_height"
android:theme="#style/AppBarLayoutTheme">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="#+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:expandedTitleMarginBottom="#dimen/extended_toolbar_title_margin_bottom"
app:expandedTitleMarginEnd="#dimen/content_indent_end"
app:expandedTitleMarginStart="#dimen/content_indent_start"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:title="#string/jobs_title" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:itemBinding="#{viewModel.itemBinding}"
app:items="#{viewModel.items}"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
The whole fragment should be displayed above the AHBottomNavigation as folows:
Desired state
However, sometimes when switching tabs or rotating screen the result is as folows (content is overlapped by the bottom navigation; cannot scroll more):
Bottom menu overlapping the content
It seems that the root of the issue is somehow in the CollapsingToolbarLayout, not the AHBottomNavigation (does not work even if AHBottomNavigation is replaced by something else).
Do you have any idea where could be the problem? Thanks for any suggestions.
while integrating the BottomSheet we need to make the parent layout as the CoordinatorLayout but in databinding we use <layout>. While implementing this it throws an exception :-
Caused by: java.lang.IllegalArgumentException: The view is not a child of CoordinatorLayout.
How to integrate BottomSheet with databinding
<layout>
<data>
<import type="android.view.View" />
<variable
name="cabLayoutBinder"
type="newage.com.hopin.rideBooking.CabSelectActivity" />
<variable
name="modelBinder"
type="newage.com.hopin.rideBooking.model.DataBinders" />
<variable
name="fareSetters"
type="newage.com.hopin.rideBooking.model.FareDetails" />
</data>
<android.support.design.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=".rideBooking.CabSelectActivity">
</android.support.design.widget.CoordinatorLayout>
</layout>
You put context in your coordinator, but in databinding you don't implement this way.
Try to remove this line and try again:
tools:context=".rideBooking.CabSelectActivity"
Let me know if it worked.
This is how I am using it
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".com.abc.Activity">
<data>
<variable
name="viewModel"
type=".com.abc.ViewModel" />
</data>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include
layout="#layout/toolbar" />
</FrameLayout>
<!-- Adding bottom sheet after main content -->
<include layout="#layout/bottom_sheet" />
</android.support.design.widget.CoordinatorLayout>
</layout>