I have a problem with bottom app bar, because I want the icons to be displayed to me in the first picture
Instead I got this:
You can place a custom layout inside your BottomAppBar.
The only thing is that you will need to align items in your custom layout manually.
<com.google.android.material.bottomappbar.BottomAppBar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="#style/Widget.MaterialComponents.BottomAppBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:backgroundTint="#android:color/white"
app:contentInsetLeft="0dp"
app:contentInsetStart="0dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="#+id/first_menu_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:src="#drawable/ic_first_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/second_menu_item"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageButton
android:id="#+id/second_menu_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:src="#drawable/ic_second_icon"
app:layout_constraintBottom_toBottomOf="#+id/first_menu_item"
app:layout_constraintEnd_toStartOf="#+id/placeholder"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="#+id/first_menu_item" />
<View
android:id="#+id/placeholder"
android:layout_width="70dp"
android:layout_height="0dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="#+id/first_menu_item"
app:layout_constraintEnd_toStartOf="#+id/third_menu_item"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="#+id/second_menu_item"
app:layout_constraintTop_toTopOf="#+id/first_menu_item" />
<ImageButton
android:id="#+id/third_menu_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:src="#drawable/ic_third_icon"
app:layout_constraintBottom_toBottomOf="#+id/first_menu_item"
app:layout_constraintEnd_toStartOf="#+id/fourth_menu_item"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="#+id/placeholder" />
<ImageButton
android:id="#+id/fourth_menu_item"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:src="#drawable/ic_fourth_icon"
app:layout_constraintBottom_toBottomOf="#+id/first_menu_item"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="#+id/third_menu_item"
app:layout_constraintTop_toTopOf="#+id/first_menu_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.bottomappbar.BottomAppBar>
You will have something like this:
The icons in the your BottomAppBar are regular action icons just like the ones in a regular Toolbar. So, you can't really position them like the one in the first picture as they will align to the right.
However, I managed to achieve something that is visually similar by nesting a BottomNavigationView inside a BottomAppBar like this:
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bottom_app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:fabAlignmentMode="center"
app:fabAnimationMode="scale"
app:hideOnScroll="true"
app:layout_scrollFlags="scroll|enterAlways">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="16dp"
app:menu="#menu/bottom_navigation_menu" />
</com.google.android.material.bottomappbar.BottomAppBar>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/fab_icon"
app:layout_anchor="#id/bottom_app_bar" />
You might notice that there's an extra android:layout_marginRight="16dp" attribute in the BottomNavigationView. Try to remove it and you will notice that the BottomNavigationView is pushed to the right and it is not aligned properly in the middle. So, by adding the right margin, it will be aligned perfectly.
Here's a tutorial to guide you on implementing the BottomNavigationView: https://code.tutsplus.com/tutorials/how-to-code-a-bottom-navigation-bar-for-an-android-app--cms-30305
Not sure whether this is the right way but it works. Happy coding!
Based on #amatkivskiy response i made a version with ConstraintLayout, guideline and style. I really appreciate what you did #amatkivskiy.
First, create your xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<!-- Note: A RecyclerView can also be used -->
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="100dp">
<!-- Scrollable content -->
</androidx.core.widget.NestedScrollView>
<com.google.android.material.bottomappbar.BottomAppBar
android:id="#+id/bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:contentInsetRight="0dp"
app:contentInsetStart="0dp"
android:backgroundTint="#color/colorPrimary">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<!--region GuideLine-->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/first_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.20" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/second_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.40" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/third_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.60" />
<androidx.constraintlayout.widget.Guideline
android:id="#+id/last_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.80" />
<!--endregion-->
<!--region icon 1-->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/first_guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/first_icon_title"
style="#style/Menu.Icon.Title"
android:layout_width="0dp"
android:layout_height="0dp"
android:textColor="#android:color/white"
android:text="#string/menu_icon_shop"
app:layout_constraintTop_toBottomOf="#id/second_icon_image"
tools:ignore="MissingConstraints" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/second_icon_image"
style="#style/Menu.Icon.Image"
app:layout_constraintBottom_toTopOf="#+id/first_icon_title"
tools:ignore="MissingConstraints"
app:srcCompat="#drawable/ic_food_selected" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--endregion-->
<!--region icon 2-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/second_guideline"
app:layout_constraintStart_toEndOf="#+id/first_guideline"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/second_icon_title"
style="#style/Menu.Icon.Title"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="#string/menu_icon_shop"
app:layout_constraintTop_toBottomOf="#id/imageView"
tools:ignore="MissingConstraints" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/imageView"
style="#style/Menu.Icon.Image"
app:layout_constraintBottom_toTopOf="#+id/second_icon_title"
app:layout_constraintVertical_chainStyle="packed"
tools:ignore="MissingConstraints"
app:srcCompat="#drawable/ic_food" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--endregion-->
<!--region icon 3-->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/constraintLayout2"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="#+id/last_guideline"
app:layout_constraintStart_toStartOf="#+id/third_guideline"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/third_icon_title"
style="#style/Menu.Icon.Title"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="#string/menu_icon_shop"
app:layout_constraintTop_toBottomOf="#id/third_icon_image"
tools:ignore="MissingConstraints" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/third_icon_image"
style="#style/Menu.Icon.Image"
app:layout_constraintBottom_toTopOf="#+id/third_icon_title"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="#drawable/ic_food"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--endregion-->
<!--region icon 4-->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/last_guideline"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/last_icon_title"
style="#style/Menu.Icon.Title"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="#string/menu_icon_shop"
app:layout_constraintTop_toBottomOf="#id/last_icon_image"
tools:ignore="MissingConstraints" />
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/last_icon_image"
style="#style/Menu.Icon.Image"
app:layout_constraintBottom_toTopOf="#+id/last_icon_title"
app:layout_constraintVertical_chainStyle="packed"
app:srcCompat="#drawable/ic_food"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!--endregion-->
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.bottomappbar.BottomAppBar>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_anchor="#id/bar"
app:srcCompat="#drawable/ic_food" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Note that in order to change size or position, change the Style so all of them will have the same modification.
Next, you need to add those in your style. Put your theme instead of mine
parent="Theme.ChefJeff"
<style name="Menu.Icon.Title" parent="Theme.ChefJeff">
<item name="android:gravity">center</item>
<item name="android:textSize">12sp</item>
<item name="layout_constraintVertical_chainStyle">packed</item>
<item name="layout_constraintBottom_toBottomOf">parent</item>
<item name="layout_constraintEnd_toEndOf">parent</item>
<item name="layout_constraintStart_toStartOf">parent</item>
</style>
<style name="Menu.Icon.Image" parent="Theme.ChefJeff">
<item name="android:layout_width">24dp</item>
<item name="android:layout_height">24dp</item>
<item name="android:layout_marginTop">3dp</item>
<item name="layout_constraintEnd_toEndOf">parent</item>
<item name="layout_constraintStart_toStartOf">parent</item>
<item name="layout_constraintTop_toTopOf">parent</item>
<item name="layout_constraintVertical_chainStyle">packed</item>
</style>
For more details : I used the guideline in order to align my icons and text. You can play with that in order to the percentage that you like. You can even make icon more bigger, remove the text. I feel like with an imageview and a Textview we have more control and it s more easy to custom it.
I did not put the code to control the color when you clic on a item as it depends on your architecture and code style. But you can modify all in kotlin or java at runtime =)
Related
I am trying to use ConstraintWidth_percent in my shop_list_item.xml, which is used inside my shopadapter. The problem I encounter is, that the design tab (how it should look like) and the in-app design (how it looks) are totally different. What am I doing wrong here?
How it should look like
How it looks
Code
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="#+id/mcv_product_item"
android:layout_width="0dp"
android:layout_height="210dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="#android:color/white"
app:cardCornerRadius="4dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.40">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/iv_product_image"
android:layout_width="match_parent"
android:layout_height="110dp"
android:contentDescription="TODO"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:loadImage="#{product.images[0]}"
tools:ignore="ContentDescription, HardcodedText"
tools:src="#drawable/ic_calibrate" />
<ImageView
android:id="#+id/iv_service_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="#null"
android:src="#drawable/ic_service"
app:hideView="#{product.hasService}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/tv_product_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="8dp"
android:ellipsize="end"
android:maxLines="2"
android:text="#{product.name}"
android:textAlignment="textStart"
android:textColor="#color/color_text_dark"
android:textSize="#dimen/textDescriptionNormal1"
app:layout_constraintEnd_toEndOf="#+id/iv_product_image"
app:layout_constraintStart_toStartOf="#+id/iv_product_image"
app:layout_constraintTop_toBottomOf="#+id/iv_product_image"
tools:text="TEST TITLE TO ENSURE STUFF" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/tv_product_price"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:text="#{product.price}"
android:textColor="#color/color_text_blue"
android:textSize="#dimen/textHeadlineNormal1"
android:textStyle="italic|bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="#+id/tv_product_name"
tools:text="4870.00" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/tv_euro"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="#string/currency"
android:textColor="#color/color_text_blue"
android:textSize="#dimen/textHeadlineNormal1"
android:textStyle="italic|bold"
app:layout_constraintBottom_toBottomOf="#+id/tv_product_price"
app:layout_constraintEnd_toEndOf="#+id/tv_product_name"
app:layout_constraintStart_toEndOf="#+id/tv_product_price"
app:layout_constraintTop_toTopOf="#+id/tv_product_price" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Currently not using -->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.30"/>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.70"/>
</androidx.constraintlayout.widget.ConstraintLayout>
When I change constraintWidth_percent to something really high like 0.8, it works like it should (but looks weird in the design tab).
This is a normal behavior as the design preview of Android Studio can have different screen size than your mobile set or the emulator.. You can change the design preview width/height to have similar width/height like your testing emulator/mobile and you'll notice there is no change.
You can change this form:
This can be very obvious as your CardView apparently takes the 40% of the RecyclerView item width. You can notice this will the green guidelines in below pic.
What you can do is to teak the 40% until you feel comfortable with a certain width that can fits for the cart items.
Last thing you can try the below layout for aligning the item in the middle of the RecyclerView item layout
<?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="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="#+id/mcv_product_item"
android:layout_width="0dp"
android:layout_height="210dp"
android:clickable="true"
android:focusable="true"
app:cardBackgroundColor="#android:color/white"
app:cardCornerRadius="4dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.4">
<!-- Add CardView items -->
</com.google.android.material.card.MaterialCardView>
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
</androidx.constraintlayout.widget.ConstraintLayout>
I tried windowSoftInputMode in manifest nothing worked the views get pushed up when the keyboard is opened I tried different approaches but none of them worked. The recycler view collapses with the edit text but it is below the guide line separator.
Image when keyboard is opened:
Image when the keyboard is not opened:
<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"
android:background="#color/bg_color_for_screen">
<!--Guide line left-->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guide_line_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="#dimen/padding_margin_15" />
<!--Guide line right-->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guide_line_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="#dimen/padding_margin_15" />
<!--Guide line for separating image-->
<androidx.constraintlayout.widget.Guideline
android:id="#+id/guide_line_separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.17" />
<!--Image for the top header-->
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toTopOf="#+id/guide_line_separator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#mipmap/splash_bg" />
<!--back button image-->
<ImageView
android:id="#+id/step_back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/padding_margin_15"
android:scaleType="centerCrop"
android:src="#mipmap/ic_back_arrow"
android:tint="#color/white"
android:visibility="visible"
app:layout_constraintStart_toStartOf="#id/guide_line_left"
app:layout_constraintTop_toTopOf="parent" />
<!--title for the screen-->
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/padding_margin_10"
android:layout_marginLeft="#dimen/padding_margin_5"
android:layout_marginTop="#dimen/padding_margin_18"
android:fontFamily="#font/nunito_medium"
android:text="#string/blank"
android:textColor="#color/white"
android:textSize="#dimen/text_size_18"
app:layout_constraintStart_toEndOf="#+id/step_back_button"
app:layout_constraintTop_toTopOf="parent" />
<!--edit text for searching by the keyword-->
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/edit_text_search"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/padding_margin_10"
android:layout_marginTop="#dimen/padding_margin_12"
android:background="#drawable/edit_text_background"
android:drawableStart="#mipmap/ic_search"
android:drawablePadding="#dimen/padding_margin_8"
android:fontFamily="#font/nunito_regular"
android:hint="#string/blank"
android:padding="#dimen/padding_margin_10"
android:textSize="#dimen/text_size_15"
app:layout_constraintEnd_toStartOf="#+id/guide_line_right"
app:layout_constraintStart_toStartOf="#id/guide_line_left"
app:layout_constraintTop_toBottomOf="#+id/label" />
<!--recycler view for the list -->
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="#dimen/padding_margin_5"
android:layout_marginBottom="#dimen/padding_margin_5"
android:background="#color/transparent"
android:visibility="visible"
app:layout_constraintBottom_toTopOf="parent"
app:layout_constraintEnd_toEndOf="#id/guide_line_right"
app:layout_constraintStart_toStartOf="#id/guide_line_left"
app:layout_constraintTop_toBottomOf="#+id/guide_line_separator" />
</androidx.constraintlayout.widget.ConstraintLayout>
Please try to remove this line from rv and then try it.
app:layout_constraintBottom_toTopOf="parent"
I am struggling to get ConstraintLayout to work for me when I want to refactor common layout code into a separate xml file. I can easily do this with RelativeLayouts but I believe RelativeLayouts are deprecated and we should be using ConstraintLayous now.
I have an app with a screen which has a different layout on portrait and landscape as illustrated in the diagram below.
I have 2 layout files with the same name ("my_layout.xml"), one in "layout" folder and one in "layout-land". Currently i have duplicated all the xml in both the layout files and adjusted the constraints so that in the landscape version the views are placed horizontally.
Portrait version
<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">
<!-- OTHER VIEWS 1 -->
<View
android:id="#+id/OtherViews1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- OTHER VIEWS 2 -->
<View
android:id="#+id/OtherViews2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottom="#+id/OtherViews1"
app:layout_constraintBottom_toTopOf="#+id/scrollbar" />
<!-- SCROLL BAR VIEWS -->
<TextView
android:id="#+id/slow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:layout_constraintTop_toTopOf="#+id/scrollbar"
app:layout_constraintBottom_toBottomOf="#+id/scrollbar"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.appcompat.widget.AppCompatSeekBar
android:id="#+id/scrollbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
...
app:layout_constraintBottom_toTopOf="#+id/OtherViews3"
app:layout_constraintStart_toEndOf="#+id/slow"
app:layout_constraintEnd_toStartOf="#+id/fast"/>
<TextView
android:id="#+id/fast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:layout_constraintTop_toTopOf="#+id/scrollbar"
app:layout_constraintBottom_toBottomOf="#+id/scrollbar"
app:layout_constraintStartEnd_toEndOf="parent" />
<!-- OTHER VIEWS 3 -->
<View
android:id="#+id/OtherViews3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Landscape version
<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">
<!-- OTHER VIEWS 1 -->
<View
android:id="#+id/OtherViews1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- OTHER VIEWS 2 -->
<View
android:id="#+id/OtherViews2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottom="#+id/OtherViews1"
app:layout_constraintBottom_toBottomOf="parent" />
<!-- SCROLL BAR VIEWS -->
<TextView
android:id="#+id/slow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:layout_constraintStart_toEndOf="#+id/OtherViews2"
app:layout_constraintTop_toTopOf="#+id/scrollbar"
app:layout_constraintBottom_toBottomOf="#+id/scrollbar"
app:layout_constraintStart_toStartOf="parent"/>
<androidx.appcompat.widget.AppCompatSeekBar
android:id="#+id/scrollbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
...
app:layout_constraintBottom_toTopOf="#+id/OtherViews3"
app:layout_constraintStart_toEndOf="#+id/slow"
app:layout_constraintEnd_toStartOf="#+id/fast"/>
<TextView
android:id="#+id/fast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:layout_constraintTop_toTopOf="#+id/scrollbar"
app:layout_constraintBottom_toBottomOf="#+id/scrollbar"
app:layout_constraintStartEnd_toEndOf="parent" />
<!-- OTHER VIEWS 3 -->
<View
android:id="#+id/OtherViews3"
android:layout_width="wrap_content"
android:layout_height="wrap_content
app:layout_constraintStart_toEndOf="#+id/OtherViews2""
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The xml for the "Slow" textbox, the scrollbar and the "Fast" textbox is duplicated in both files. I would like to move the layout for these 3 elements into a separate file and reference it in both "my_layout.xml" files so its more reusable and no duplication across layout files. I want to keep my hierarchy flat (otherwise i would just use RelativeLayouts). I do not know how to specify the constraints of the new reusable xml layout file for the scrollbar UI.
Portrait version reusing the scrollbar layout
<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">
<!-- OTHER VIEWS 1 -->
<View
android:id="#+id/OtherViews1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- OTHER VIEWS 2 -->
<View
android:id="#+id/OtherViews2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottom="#+id/OtherViews1"
app:layout_constraintBottom_toTopOf="#+id/scrollbar" />
<include
layout="#layout/scrollbar
app:layout_constraintBottom_toTopOf="#+id/OtherViews3"
app:layout_constraintStart_toStartOf="parent" />
<!-- OTHER VIEWS 3 -->
<View
android:id="#+id/OtherViews3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Reusable scrollbar layout
<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="wrap_content"
android:layout_height="wrap_content">
<!-- SCROLL BAR VIEWS -->
<TextView
android:id="#+id/slow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:layout_constraintTop_toTopOf="#+id/scrollbar"
app:layout_constraintBottom_toBottomOf="#+id/scrollbar"
app:layout_constraintStart_toStartOf="parent"/> ******************
<androidx.appcompat.widget.AppCompatSeekBar
android:id="#+id/scrollbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
...
app:layout_constraintBottom_toTopOf="#+id/OtherViews3" ******************
app:layout_constraintStart_toEndOf="#+id/slow"
app:layout_constraintEnd_toStartOf="#+id/fast"/>
<TextView
android:id="#+id/fast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:layout_constraintTop_toTopOf="#+id/scrollbar"
app:layout_constraintBottom_toBottomOf="#+id/scrollbar"
app:layout_constraintStartEnd_toEndOf="parent" /> ******************
</androidx.constraintlayout.widget.ConstraintLayout>
I do not know what to put for the lines marked with ****. If I specify that the "slow" textbox starts at the start of the parent, that will not work for the landscape version. I would like this scrollbar layout to just indicate the slow is to the left, the scrollbar in the middle (taking up all remaining space) and the fast textbox is on the right. How do i do that using Constraint Layout? Also how do i center all 3 views vertically?
RelativeLayout is so much easier, as i would say the slow textbox is alignedParentLeft, the fast is alignedParentRight and the scrollbar is to the right of the slow textbox and to the left of the fast textbox. Finally i would say all 3 views are centered vertically in the parent.
A view in the included layout cannot refer to a view in the layout file in which it is included. You can still use an included layout with a different approach.
Look at the included layout with the fast/slow text and the seek bar as a self-enclosed entity - something like the following:
scrollbar.xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/slow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Slow"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatSeekBar
android:id="#+id/scrollbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="#+id/slow"
app:layout_constraintEnd_toStartOf="#+id/fast"
app:layout_constraintStart_toEndOf="#+id/slow"
app:layout_constraintTop_toTopOf="#+id/slow" />
<TextView
android:id="#+id/fast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fast"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can now include this layout in layouts for portrait and landscape orientation:
activity_main.xml (portrait)
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="#+id/OtherViews1"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#FFCCCCCC"
android:gravity="center"
android:text="OtherViews1"
app:layout_constraintBottom_toTopOf="#+id/OtherViews2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="1" />
<TextView
android:id="#+id/OtherViews2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:background="#FFCCCCCC"
android:gravity="center"
android:text="OtherViews2"
app:layout_constraintBottom_toTopOf="#+id/scrollbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottom="#+id/OtherViews1"
app:layout_constraintTop_toBottomOf="#+id/OtherViews1"
app:layout_constraintVertical_weight="4" />
<include
layout="#layout/scrollbar"
android:id="#+id/scrollbar"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/OtherViews3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/OtherViews2"
app:layout_constraintVertical_weight="0.5" />
<TextView
android:id="#+id/OtherViews3"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="24dp"
android:background="#FFCCCCCC"
android:gravity="center"
android:text="OtherViews3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/scrollbar"
app:layout_constraintVertical_weight="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml (landscape)
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="#+id/OtherViews1"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#FFCCCCCC"
android:gravity="center"
android:text="OtherViews1"
app:layout_constraintBottom_toTopOf="#+id/OtherViews2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="1" />
<TextView
android:id="#+id/OtherViews2"
android:layout_width="342dp"
android:layout_height="0dp"
android:layout_marginTop="24dp"
android:background="#FFCCCCCC"
android:gravity="center"
android:text="OtherViews2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottom="#+id/OtherViews1"
app:layout_constraintTop_toBottomOf="#+id/OtherViews1"
app:layout_constraintVertical_weight="8" />
<include
android:id="#+id/scrollbar"
layout="#layout/scrollbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:layout_constraintBottom_toTopOf="#+id/OtherViews3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="#+id/OtherViews3" />
<!-- OTHER VIEWS 3 -->
<TextView
android:id="#+id/OtherViews3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:background="#FFCCCCCC"
android:gravity="center"
android:text="OtherViews3"
app:layout_constraintBottom_toBottomOf="#+id/OtherViews2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/OtherViews2" />
</androidx.constraintlayout.widget.ConstraintLayout>
The included layout file can be treated, in terms of constraints and size, like its own widget. See Re-using layouts with <include/>.
This is my qff_layout.xml file
<merge
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textview.MaterialTextView
android:id="#+id/questions_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/questions"
android:textSize="#dimen/text_normal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintEnd_toStartOf="#+id/followers_label"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/followers_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/followers"
android:textSize="#dimen/text_normal"
app:layout_constraintStart_toEndOf="#+id/questions_label"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintEnd_toStartOf="#+id/following_label"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/following_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/following"
android:textSize="#dimen/text_normal"
app:layout_constraintStart_toEndOf="#+id/followers_label"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/questions_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="#dimen/text_normal"
android:textStyle="bold"
tools:text="0"
app:layout_constraintStart_toStartOf="#+id/questions_label"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="#+id/questions_label"
android:layout_marginRight="8dp"
app:layout_constraintTop_toBottomOf="#+id/questions_label"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/followers_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="#dimen/text_normal"
android:textStyle="bold"
tools:text="0"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
app:layout_constraintStart_toEndOf="#+id/questions_label"
app:layout_constraintEnd_toStartOf="#+id/following_label"
app:layout_constraintTop_toBottomOf="#+id/followers_label"/>
<com.google.android.material.textview.MaterialTextView
android:id="#+id/following_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="#dimen/text_normal"
android:textStyle="bold"
tools:text="0"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
app:layout_constraintStart_toStartOf="#+id/following_label"
app:layout_constraintEnd_toEndOf="#+id/following_label"
app:layout_constraintTop_toBottomOf="#+id/following_label"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</merge>
This displays 6 textviews in two rows, something you would see in instagram app in profile section with Posts-Followers-Following and bellow numbers under each of those.
Now the problem is when I include that in my main layout which is ConstraintLayout:
profile_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:theme="?attr/actionBarTheme"
android:minHeight="?attr/actionBarSize"
android:elevation="4dp"
android:id="#+id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/profile_image"
android:layout_width="80dp"
android:layout_height="80dp"
app:srcCompat="#drawable/ic_profile_24dp"
tools:src="#drawable/ic_profile_24dp"
app:civ_border_color="#color/secondaryDarkColor"
app:civ_border_width="1dp"
android:layout_marginTop="#dimen/spacing_normal"
android:layout_marginStart="#dimen/spacing_normal"
android:layout_marginLeft="#dimen/spacing_normal"
app:layout_constraintTop_toBottomOf="#+id/title"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.textview.MaterialTextView
android:layout_width="0dp"
android:layout_height="0dp"
android:id="#+id/description"
android:layout_marginEnd="#dimen/spacing_normal"
android:layout_marginRight="#dimen/spacing_normal"
android:layout_marginLeft="#dimen/spacing_normal"
android:layout_marginStart="#dimen/spacing_normal"
app:layout_constraintStart_toEndOf="#+id/profile_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="#+id/profile_image"
app:layout_constraintTop_toTopOf="#+id/profile_image"/>
<include
layout="#layout/qff_layout"
android:id="#+id/qff_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/spacing_small"
app:layout_constraintTop_toBottomOf="#+id/profile_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<com.google.android.material.tabs.TabLayout
android:id="#+id/sliding_tabs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="#color/primaryLightColor"
app:tabMode="fixed"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/qff_layout"/>
<androidx.viewpager.widget.ViewPager
android:id="#+id/view_pager"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="#android:color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/sliding_tabs" />
</androidx.constraintlayout.widget.ConstraintLayout>
Nothing gets displayed.
I tried changing my main layout to linear and it's the same.
But when I remove merge tag from qff_layout.xml and use the same include tag in main layout it works well and displays the qff_layout, How??Why??
What is the use of merge tag then, please do not quote something from the stupidest documentation ever because it makes no sense.
You should use merge when you dont want to repeat same ViewGroup. In this case, it could save you from not using two nested ConstraintLayouts - you could remove ConstraintLayout from qff_layout.xml and use merge as your root element like:
<merge
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- your text view -->
</merge>
If your ConstraintLayout in qff_layout.xml has some other purpose, you just leave it, and include qff_layout.xml without merge as root element.
In other words merge purpose is to replace root ViewGroup.
Read more here.
UPDATE:
You can fix your XML this way without removing marge.
Positioning constraints you defined on element, should be also applied on your ConstraintLayout inside qff_layout.xml like:
<merge
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/qff_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/spacing_small"
app:layout_constraintTop_toBottomOf="#+id/profile_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<!-- your text view -->
</androidx.constraintlayout.widget.ConstraintLayout>
</merge>
Why you need to apply this: When you put layout inside element it will basically get pasted in the line you include it. When you don't define constraints for some layout inside ConstraintLyout, it jumps to the top left corner of the screen and thats what happend to layout you included. It didnt have defined constraints inside qff_layout.xml that will apply when it gets included, so it jumped to the top left corner.
I am new to motionlayout and been following various tutorials online like this to get an understanding of how it works. From a nutshell I have come to know it basically animates constraintSets, you have a start and end constraintSet which you can further customize with KeyFrameSets. I have this layout
I want to mimic Lyft's bottom sheet
With my layout the Where are you going button is suppose to slowly fade out as the search destination textInputs fade in. The recyclerview at the bottom is suppose to hold saved addresses, it will not be affected. I tried this implementation using a standard bottomsheet but had challenges with the animation, it had this weird flickering so I decided to use a MotionLayout with a normal view.
My bottomsheet layout is as follows
<com.google.android.material.card.MaterialCardView 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/cardChooseAddressBottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
app:shapeAppearance="#style/ShapeAppearanceRoundedLargeTopCorners">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/bottomSheetConstraintLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="#+id/swipeUpHandle"
android:layout_width="50dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:background="#drawable/ic_swipe_up_handle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/hiThere"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/medium_margin"
android:text="#string/hi_there"
android:textAppearance="#style/h6_headline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/swipeUpHandle"
/>
<com.google.android.material.button.MaterialButton
android:id="#+id/btnSearch"
style="#style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginTop="#dimen/medium_margin"
android:gravity="start|center_vertical"
android:letterSpacing="0.0"
android:text="#string/where_are_you_going"
android:textAllCaps="false"
android:textAppearance="#style/subtitle1"
android:textColor="#android:color/darker_gray"
app:backgroundTint="#android:color/white"
app:icon="#drawable/ic_search"
app:iconTint="#android:color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/hiThere"
app:shapeAppearanceOverlay="#style/ShapeAppearanceRoundedMediumAllCorners" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/addressViews"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btnSearch">
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/inputOrigin"
style="#style/textInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/medium_margin"
android:hint="#string/search_destination"
android:textColorHint="#android:color/darker_gray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/edtOrigin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:textAppearance="#style/subtitle1"
android:textColor="#android:color/white" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/inputDestination"
style="#style/textInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/medium_margin"
android:hint="#string/search_destination"
android:textColorHint="#android:color/darker_gray"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/inputOrigin">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/edtDestination"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:textAppearance="#style/subtitle1"
android:textColor="#android:color/white" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/recyclerAddresses"
android:layout_marginTop="#dimen/medium_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/addressViews"
tools:listitem="#layout/recycler_view_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
And my parent layout where I include the bottomsheet is as follows
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout 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/motionLayout"
app:layoutDescription="#xml/taxi_bottomsheet_scene"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include
layout="#layout/choose_destination_bottom_sheet_layout"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
And finally my taxi_bottomsheet_scene motion scene is
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Transition
app:constraintSetEnd="#+id/expanded"
app:constraintSetStart="#+id/collapsed"
app:duration="1000">
<OnSwipe
app:touchAnchorId="#+id/btnSearch"
app:touchAnchorSide="top"
app:dragDirection="dragUp"/>
</Transition>
<ConstraintSet android:id="#+id/expanded">
<Constraint
android:id="#+id/cardChooseAddressBottomSheet"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_percent="1"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0" />
<Constraint
android:id="#+id/addressViews"
app:layout_constraintHeight_percent="1"/>
<Constraint
android:id="#+id/btnSearch"
app:layout_constraintHeight_percent="0"/>
</ConstraintSet>
<ConstraintSet android:id="#+id/collapsed">
<Constraint
android:id="#+id/cardChooseAddressBottomSheet"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHeight_percent="0.4"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
<Constraint
android:id="#+id/addressViews"
app:layout_constraintHeight_percent="0.0"/>
<Constraint
android:id="#+id/btnSearch"
app:layout_constraintHeight_percent="0.0"/>
</ConstraintSet>
</MotionScene>
When I launch this app I cannot get the bottomsheet to slide up, it simply does not respond in any way. One thing I noticed though is after adding the app:layoutDescription="#xml/taxi_bottomsheet_scene"attribute, the bottom sheet size changed to what I had specified in the constraintSetStart but the addressViews view did not.
So my layout looks like
So my question is, where I am going wrong for my bottomsheet not to respond to my swipes and addressViews to disappear in the initial state?
I finally managed to make it work, both with MotionLayout and CoordinatorLayout. I will only post the Coordinator solution as it is long and I do not have the time, if someone needs it, comment and I will post.
I created 3 layouts, 1. The main layout with the map, 2. Top bar with the to and from address EditTexts and 3. The bottom layout that slides up and reveals the top bar.
Solution 1 using CoordinatorLayout
The topbar
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="true"
android:layout_marginBottom="#dimen/medium_margin"
app:layout_scrollFlags="noScroll">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/topToolBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navigationIcon="#drawable/ic_arrow_back_black_24dp" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/addressViews"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="#dimen/activity_horizontal_margin"
android:layout_marginEnd="#dimen/activity_horizontal_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/topToolBar">
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/imgOrigin"
android:layout_width="#dimen/activity_horizontal_margin"
android:layout_height="#dimen/activity_horizontal_margin"
android:layout_marginStart="#dimen/medium_margin"
android:layout_marginEnd="#dimen/medium_margin"
app:layout_constraintBottom_toBottomOf="#+id/inputOrigin"
app:layout_constraintEnd_toStartOf="#+id/inputOrigin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/inputOrigin"
app:srcCompat="#drawable/ic_origin"
app:tint="#color/colorAccent" />
<View
android:layout_width="2dp"
android:layout_height="0dp"
android:layout_marginTop="#dimen/xsmall_margin"
android:layout_marginBottom="#dimen/xsmall_margin"
android:background="#drawable/accent_to_color_primary_dark__negative_90_gradient"
app:layout_constraintBottom_toTopOf="#+id/imgDestination"
app:layout_constraintEnd_toEndOf="#+id/imgOrigin"
app:layout_constraintStart_toStartOf="#+id/imgOrigin"
app:layout_constraintTop_toBottomOf="#+id/imgOrigin" />
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/inputOrigin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/medium_margin"
android:hint="#string/pick_up_location"
android:marqueeRepeatLimit="marquee_forever"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/imgOrigin"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/edtOrigin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:inputType="textPostalAddress"
android:singleLine="true"
android:textAppearance="#style/subtitle1" />
</com.google.android.material.textfield.TextInputLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="#+id/imgDestination"
android:layout_width="#dimen/activity_horizontal_margin"
android:layout_height="#dimen/activity_horizontal_margin"
android:layout_marginStart="#dimen/medium_margin"
android:layout_marginEnd="#dimen/medium_margin"
app:layout_constraintBottom_toBottomOf="#+id/inputDestination"
app:layout_constraintEnd_toStartOf="#+id/inputDestination"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/inputDestination"
app:srcCompat="#drawable/ic_destination"
app:tint="#color/colorPrimaryDark" />
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/inputDestination"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/medium_margin"
android:hint="#string/search_destination"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/imgDestination"
app:layout_constraintTop_toBottomOf="#id/inputOrigin">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/edtDestination"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:inputType="textPostalAddress"
android:singleLine="true"
android:textAppearance="#style/subtitle1" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
The bottom layout that acts like a bottom sheet
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cardChooseAddressBottomSheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardBackgroundColor="#color/white"
app:behavior_hideable="false"
app:layout_behavior="#string/bottom_sheet_behavior">
<androidx.constraintlayout.widget.ConstraintLayout
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">
<ImageView
android:id="#+id/swipeUpHandle"
android:layout_width="35dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:background="#drawable/ic_swipe_up_handle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="#+id/hiThere"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hi_there"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:textAppearance="#style/h6_headline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/swipeUpHandle" />
<com.google.android.material.button.MaterialButton
android:id="#+id/btnSearch"
style="#style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:gravity="start|center_vertical"
android:letterSpacing="0.0"
android:text="#string/where_are_you_going"
android:textAllCaps="false"
android:textAppearance="#style/subtitle1"
android:textColor="#android:color/darker_gray"
app:backgroundTint="#android:color/white"
app:icon="#drawable/ic_search"
app:iconSize="#dimen/medium_icon"
app:iconTint="#color/colorAccent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/hiThere"
app:shapeAppearanceOverlay="#style/ShapeAppearanceRoundedMediumAllCorners" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerAddresses"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/btnSearch" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerSearchAddresses"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#color/white"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/btnSearch" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
And finally the two layouts included in my map 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"
android:layout_width="match_parent"
android:id="#+id/coordinator"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.67"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imgMapCenter"
android:layout_marginBottom="#dimen/xlarge_margin"
android:visibility="invisible"
android:tint="#color/colorAccent"
app:layout_constraintBottom_toBottomOf="#id/map"
app:layout_constraintEnd_toEndOf="#id/map"
app:layout_constraintStart_toStartOf="#id/map"
app:layout_constraintTop_toTopOf="#id/map"
app:srcCompat="#drawable/ic_destination" />
</androidx.constraintlayout.widget.ConstraintLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fabMyLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/activity_horizontal_margin"
app:backgroundTint="#color/white"
app:fabSize="mini"
app:layout_anchor="#id/cardChooseAddressBottomSheet"
app:layout_anchorGravity="top|right"
app:srcCompat="#drawable/ic_origin"
app:tint="#color/colorAccent" />
<include layout="#layout/taxi_fragment_set_destination_top_bar" />
<include layout="#layout/taxi_fragment_bottom_sheet_addresses_layout" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And my fragment
final private BottomSheetBehavior.BottomSheetCallback addressBottomSheetCallBack = new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onStateChanged(#NonNull View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_HIDDEN:
break;
case BottomSheetBehavior.STATE_SETTLING:
break;
case BottomSheetBehavior.STATE_EXPANDED:
if (!allPermissionsGranted())
requestForLocationPermissions();
topAddressBar.setVisibility(Visibility.VISIBLE);
fabMyLocation.hide();
break;
case BottomSheetBehavior.STATE_COLLAPSED:
if (!allPermissionsGranted())
requestForLocationPermissions();
topAddressBar.setVisibility(Visibility.INVISIBLE);
fabMyLocation.show();
break;
case BottomSheetBehavior.STATE_DRAGGING:
break;
case BottomSheetBehavior.STATE_HALF_EXPANDED:
break;
}
}
BottomSheetBehavior addressBottomSheetBehavior = BottomSheetBehavior.from(cardChooseAddressBottomSheet);
topAddressBar.post(() -> {
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) cardChooseAddressBottomSheet.getLayoutParams();
layoutParams.height = ((Resources.getSystem().getDisplayMetrics().heightPixels + (int) utils.percentageOf(62, btnSearch.getMeasuredHeight())) - topAddressBar.getMeasuredHeight());
});
addressBottomSheetBehavior.setPeekHeight((int) utils.percentageOf(29, Resources.getSystem().getDisplayMetrics().heightPixels), true);
addressBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
topToolBar.setNavigationOnClickListener(view12 -> {
if (addressBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
utopAddressBar.setVisibility(INVISIBLE);
addressBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else
Navigation.findNavController(view12).navigateUp();
});
addressBottomSheetBehavior.addBottomSheetCallback(addressBottomSheetCallBack);
Notice am using INVISIBLE instead of GONE on the topAddressBar? that is because everytime I called GONE the layout would ideally recalculate according to my assumption and the map would flicker, to stop that I had to use invisible as the layout does not shrink instead it still takes up the same space but just not visible.
Also notice I am adding padding cardChooseAddressBottomSheet.getLayoutParams() this is because I need the Sheet not to go too deep underneath the topAddressBar as not to hide my recyclerview content. The current padding makes sure the recyclerview is fully visible and everything else on top of it is underneath the topAddressBar