Not sure if I messed up or if I should file a bug report with Google. Probably I messed up.
My app has the nav host (and nav drawer) in MainActivity and a view pager and tabs in a fragment. When the fragment that hosts the viewpager gets loaded the viewpager is the same size as the contents of the first fragment at the first tab. When I click on another tab to load a fragment with much more content into the viewpager, the content of fragment 2 is clipped to the size and position of the content of fragment one. If I then use the options menu to go to a different fragment and then back again with the <- button, then the second fragment is displayed correctly. What I need is for every fragment to display correctly every time regardless. In fact, I want the viewpager content to fill up the available space even if the fragment being loaded is smaller than the available space. I have tried setting every height attribute I can find to "match_parent" instead of "wrap_content", but no luck. The "design" view of the layout editor listed "0dp" as "match constraint" but when I tried that it vanished altogether. Help greatly appreciated.Screenshot of before and after using <- button
Code for fragment class hosting ViewPager 2:
class PagerHostFragment : Fragment() {
private lateinit var binding: FragmentPagerHostBinding
private lateinit var viewPager: ViewPager2
private lateinit var tabTitleList: List<String>
private lateinit var tabLayout: TabLayout
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
super.onCreateView(inflater, container, savedInstanceState)
binding = FragmentPagerHostBinding.inflate(inflater, container, false)
tabLayout = binding.tabs
viewPager = binding.pager
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//have also tried activity instead of getActivity(). same result
val pagerAdapter = ViewPagerAdapter(getActivity())
viewPager.adapter = pagerAdapter
tabTitleList = listOf(
(getString(R.string.tab_title_first)),
(getString(R.string.tab_title_second)),
(getString(R.string.tab_title_third))
)
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = tabTitleList[position]
}.attach()
}
}
Code for Adapter class
class ViewPagerAdapter(fa: FragmentActivity?) : FragmentStateAdapter(fa!!) {
private val tabbedFragments: Array<Fragment> = arrayOf(
FirstFragment(),
SecondFragment(),
ThirdFragment()
)
override fun getItemCount(): Int {
return tabbedFragments.size //Number of fragments displayed
}
// this is flagged by Android Studio as redundant but it's here because a previous
// answer put it in. I've tried with and without, same result
override fun getItemId(position: Int): Long {
return super.getItemId(position)
}
override fun createFragment(position: Int): Fragment {
return tabbedFragments[position]
}
}
XML for fragment hosting tabs and pager
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
</data>
<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"
app:layout_constraintBottom_toBottomOf="parent"
tools:context=".ui.PagerHostFragment">
<com.google.android.material.tabs.TabLayout
android:id="#+id/tabs"
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:tabIndicatorHeight="48dp">
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="102dp"
android:foregroundGravity="bottom"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/tabs" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
XML for first fragment loaded on first tab
<?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>
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.FirstFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="First Frag"
android:textAlignment="center"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
XML for second fragment loaded on second tab
<?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>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.SecondFragment">
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button1" />
<Button
android:id="#+id/button3"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button2" />
<Button
android:id="#+id/button4"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button3" />
<Button
android:id="#+id/button5"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button4" />
<Button
android:id="#+id/button6"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="6"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button5" />
<Button
android:id="#+id/button7"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="7"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button6" />
<Button
android:id="#+id/button8"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="8"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button7" />
<Button
android:id="#+id/button9"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="9"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button8" />
<Button
android:id="#+id/button10"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="10"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/button9" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Classes associated with all 3 fragments that are loaded into the pager adapter are only boilerplate, I'm just trying to get the UI working right. XML for the 3rd fragment is identical to the first except for name and text view content.
Edited to add: Setting ViewPager2 minHeight to 426 did not change anything. Shouldn't the viewpager match in size whatever it's displaying, automatically? Is this a bug or have I done this wrong?
Related
I have noticed that sometimes the DialogFragment is displayed horrendously. For example, given the following XML:
<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"
android:padding="#dimen/spacing_large">
<ImageView
android:id="#+id/close"
android:layout_width="#dimen/icon_size_medium"
android:layout_height="#dimen/icon_size_medium"
android:layout_gravity="end"
android:src="#drawable/ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
style="#style/Text.Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a test with 'match_parent'"
android:textAlignment="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/close" />
</androidx.constraintlayout.widget.ConstraintLayout>
It displays this:
On the other hand, the following XML is broken:
<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"
android:padding="#dimen/spacing_large">
<ImageView
android:id="#+id/close"
android:layout_width="#dimen/icon_size_medium"
android:layout_height="#dimen/icon_size_medium"
android:layout_gravity="end"
android:src="#drawable/ic_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
style="#style/Text.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="This is a test with '0dp'"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/close" />
</androidx.constraintlayout.widget.ConstraintLayout>
I have only made these changes:
android:layout_width="match_parent" --> android:layout_width="0dp"
add --> app:layout_constraintEnd_toEndOf="parent"
I have experienced this with other layouts (LinearLayout, FrameLayout) as well. Why is this ?
I have seen many questions about settings the sizes of dialogs and I believe it's due to this bug. Most set new layout params in onResume but it feels hacky and I feel like there is an explanation.
Here the DialogFragment implementation used:
class SomeFragment : DialogFragment() {
private lateinit var binding: FragmentSomeBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = FragmentSomeBinding.inflate(inflater, container, false)
return binding.root
}
}
EDIT:
This is an attempt with LinearLayout with similar unexpected behaviour.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/spacing_large">
<ImageView
android:layout_width="#dimen/icon_size_medium"
android:layout_height="#dimen/icon_size_medium"
android:layout_gravity="end"
android:src="#drawable/ic_close"/>
<TextView
style="#style/Text.Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a test with 'LinearLayout'"
android:textAlignment="center"/>
</LinearLayout>
the following XML is broken
Both are broken.
Your ConstraintLayout has android:layout_width="match_parent", and you have no control over the parent. The parent's width presumably is wrap_content, given your screenshots, but you should not be relying upon that, as it could vary. Either give the ConstraintLayout a specific width or use wrap_content for its width.
The items in my RecyclerView Grid are stretched automatically if the left item is shorter and the right item is taller or vice versa (2 grid spans) because I want to use layout_height="wrap_content". How to keep that left item from being stretched automatically? I still want to use the GridLayout style because in my case it is not suitable if using StaggeredGridLayout (because it is more suitable for photo galleries).
<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/very_light_gray"
android:paddingBottom="61dp"
tools:context="Fragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_explore_buyer_outlet"
android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:overScrollMode="ifContentScrolls"
android:paddingEnd="#dimen/_20sdp"
android:paddingBottom="28dp"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="2"
tools:ignore="RtlSymmetry"
tools:itemCount="18"
tools:listitem="#layout/item_grid" />
</androidx.constraintlayout.widget.ConstraintLayout
</androidx.coordinatorlayout.widget.CoordinatorLayout>
item_grid.xml
<androidx.cardview.widget.CardView
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="177dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/_14sdp"
android:layout_marginTop="#dimen/_14sdp"
android:layout_marginEnd="#dimen/_minus18sdp"
android:layout_marginBottom="-7dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardCornerRadius="14dp"
app:cardElevation="1dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/iv_item_outlet_buyer_photo"
android:layout_width="match_parent"
android:layout_height="112dp"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
android:src="#drawable/outlet10"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/tv_item_outlet_buyer_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="9dp"
android:background="#drawable/outlet_buyer_status_open_label"
android:fontFamily="#font/notosansjp_medium"
android:gravity="center"
android:includeFontPadding="false"
android:paddingHorizontal="8dp"
android:paddingTop="3dp"
android:paddingBottom="3.75dp"
android:textColor="#color/white"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="#+id/iv_item_outlet_buyer_photo"
app:layout_constraintEnd_toEndOf="parent"
tools:ignore="SmallSp"
tools:text="Open" />
<TextView
android:id="#+id/tv_item_outlet_buyer_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10.5dp"
android:fontFamily="#font/notosansjp_bold"
android:includeFontPadding="false"
android:lineSpacingExtra="1.5dp"
android:paddingStart="15dp"
android:paddingEnd="#dimen/_17sdp"
android:paddingBottom="2dp"
android:textColor="#color/dark_gray"
android:textSize="#dimen/_10ssp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/iv_item_outlet_buyer_photo"
tools:text="Guttenburg, Alazka" />
<ImageView
android:id="#+id/iv_item_outlet_buyer_dotted_line"
android:layout_width="0dp"
android:layout_height="5dp"
android:layout_marginTop="5dp"
android:importantForAccessibility="no"
android:layerType="software"
android:paddingStart="16dp"
android:paddingEnd="15dp"
android:src="#drawable/dotted_shape"
app:layout_constraintEnd_toEndOf="#+id/tv_item_outlet_buyer_name"
app:layout_constraintStart_toStartOf="#+id/tv_item_outlet_buyer_name"
app:layout_constraintTop_toBottomOf="#+id/tv_item_outlet_buyer_name" />
<TextView
android:id="#+id/tv_item_outlet_buyer_category"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4.5dp"
android:layout_marginBottom="14dp"
android:fontFamily="#font/notosansjp_regular"
android:includeFontPadding="false"
android:lineSpacingExtra="1.5dp"
android:paddingHorizontal="15dp"
android:textColor="#color/dark_gray"
android:textSize="11sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/iv_item_outlet_buyer_dotted_line"
tools:ignore="SmallSp"
tools:text="Food ∙ Drink ∙ Snack" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
I want make my GridLayout like this (edited in Figma):
But I got like this if last textView have constraintBottom_toBottomOf(parent):
And, I got like this if last textView not have constraintBottom_toBottomOf(parent):
What should I do?
You just need to make sure your TextView's layout_height is wrap_content, and give that the black background. The containing layout should be transparent, so when it resizes to fit the grid, the extra space is just "empty". The TextView shouldn't resize with it (and neither should anything else in the item's layout), so the black area stays the same - the extra height is unused.
Here's a quick example
MainFragment.kt
class MainFragment : Fragment(R.layout.fragment_main) {
lateinit var binding: FragmentMainBinding
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// fragment_main.xml has a RecyclerView in it with "recycler" for its ID
binding = FragmentMainBinding.bind(view)
with(binding) {
recycler.layoutManager = GridLayoutManager(requireContext(), 2)
recycler.adapter = Adapterino(requireContext())
}
}
private class Adapterino(val context: Context) : RecyclerView.Adapter<Adapterino.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
ItemViewBinding.inflate(LayoutInflater.from(context)).run(::ViewHolder)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// just generating random amounts of text to get some variation
holder.binding.textView.text =
generateSequence { "cats" }.take(Random.nextInt(5, 20)).joinToString(" ")
}
override fun getItemCount(): Int = 20
private class ViewHolder(val binding: ItemViewBinding) : RecyclerView.ViewHolder(binding.root)
}
}
item_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp"
>
<View
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#color/black"
/>
<ImageView
android:layout_width="match_parent"
android:layout_height="240dp"
android:src="#mipmap/ic_launcher"
/>
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/black"
android:textColor="#color/white"
android:textSize="20sp"
/>
</LinearLayout>
I have a Constraint layout which 2 main elements inside it: a nested scroll view with a list of elements defined inside another layout file and a textview used as a footer.
This is the 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"
android:orientation="vertical">
<androidx.core.widget.NestedScrollView
android:id="#+id/menu_scrollview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:context=".sections.main.menu.MenuFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/menu_title"
style="#style/AppTheme.TextTitle1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/mid_small_margin"
android:text="#string/menu_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include layout="#layout/list_item_menu_fragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<TextView
android:id="#+id/menu_title_2"
style="#style/AppTheme.TextTitle1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/mid_small_margin"
android:text="#string/menu_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
This is the list_item_menu_fragment.xml layout defined in it:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/menu_elements_list"
style="#style/RectangularTextViewMenu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="#dimen/mid_small_margin"
android:layout_marginTop="20dp"
android:layout_marginBottom="32dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="#+id/menu_item_profile"
app:layout_constraintBottom_toBottomOf="#+id/links_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/charge_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:fontFamily="#font/montserrat_bold"
android:gravity="center_vertical"
android:textColor="#color/black"
android:text="#string/menu_pay_charge"
app:drawableStartCompat="#drawable/ic_menu_charge"
app:drawableEndCompat="#drawable/ic_arrow_right" />
<androidx.appcompat.widget.AppCompatTextView
android:id="#+id/pay_online_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:fontFamily="#font/montserrat_bold"
android:gravity="center_vertical"
android:textColor="#color/black"
android:text="#string/menu_pay_online"
app:drawableStartCompat="#drawable/ic_menu_pay_online"
app:drawableEndCompat="#drawable/ic_arrow_right" />
</LinearLayout>
This is my fragment:
class MenuFragment : Fragment() {
private var _binding: FragmentMenuBinding? = null
private val menuViewModel by viewModels<MenuViewModel>()
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentMenuBinding.inflate(inflater, container, false)
return binding.root
}
If I tryo access to my list of elements using binding.chargeButton it says that i cannot do it (because it is in another layout). If I do binding.menuTitle it works perfectly. How can I access to those elements?
As written in ViewBinding
An instance of a binding class contains direct references to all views
that have an ID in the corresponding layout.
You have to assign an ID to the included layout and then you can call it from the parent view binding class.
Change from
<include layout="#layout/list_item_menu_fragment" />
to
<include android:id="#+id/listItemMenu" layout="#layout/list_item_menu_fragment" />
Now you can call binding.listItemMenu.chargeButton
So I have a nested scroll view inside the bottom sheet fragment and the default behavior is to expand the bottom sheet to the top of the screen first and then scroll the long text inside the nested scroll view. how can change the default behavior to scroll the long text inside nested scroll view first and disable draggable when the user tries to scroll the long text and allow the user to draggable manual from the top like the youtube app
Default behavior
https://s10.gifyu.com/images/default.gif
Expected behavior
https://s10.gifyu.com/images/expected.gif
Note : I can't upload the preview to stack overflow because of the 2MB upload limit
<LinearLayout
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"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="10dp">
<ImageButton
android:id="#+id/close_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="#null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="#drawable/ic_close" />
<TextView
android:id="#+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="Title text"
android:textSize="20sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/close_btn"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="#+id/title_tv">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/short_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Short text"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="#+id/example_btn"
style="#style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Example Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/short_tv" />
<TextView
android:id="#+id/long_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:padding="15dp"
android:text="#string/long_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/example_btn" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>
ExampleBottomSheet.kt
class ExampleBottomSheet : BottomSheetDialogFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.bottom_sheet, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
You are using BottomSheetDialogFragment which is less "flexible". Try to redesign your activity/fragment to use CoordinatorLayout and embed layout with bottom sheet behavior there.
<!-- this should be the new root of your main activity/fragment -->
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<!-- Root LinearLayout from your bottom_sheet.xml, just add style and layout_behavior property -->
<LinearLayout
android:id="#+id/bottomSheet"
style="?attr/bottomSheetStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
...
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
then you can create instance of BottomSheetBehavior in your activity/class using BottomSheetBehavior.from(View). This will gain you access to control state of your bottom sheet.
Here you can read more about standard bottom sheet: https://material.io/components/sheets-bottom/android#standard-bottom-sheet
And here you have nice comparison between standard and modal (BottomSheetDialogFragment) bottom sheets: https://medium.com/over-engineering/hands-on-with-material-components-for-android-bottom-sheet-970c5f0f1840
I have recently came across to this video Keyframe Animations with ConstraintLayout and ConstraintSet , it seems pretty cool . so Implemented this way following the steps showed in a blog post . There is just one difference though between the blog and my use case . I am working with fragment . I thought theoretically implementation supposed to be as it is . Here is my work around for keyframe animation on two constrainSet()
ConstrainLayout initial (point A so to speak)
<?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"
android:id="#+id/fragment_root">
<ImageView
android:id="#+id/imageView"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:src="#android:drawable/ic_menu_camera"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="#+id/textView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.687" />
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:padding="10dp"
android:text="#string/missing_permission"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/imageView"
app:layout_constraintVertical_bias="0.476" />
<Button
android:id="#+id/setting_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="76dp"
android:text="#string/settings"
app:layout_constraintBottom_toTopOf="#+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="#+id/imageView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.511" />
</android.support.constraint.ConstraintLayout>
ConstrainLayout initial (point B the destination , the final look of the layout)
<?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/fragment_root">
<ImageView
android:id="#+id/imageView"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="136dp"
android:src="#android:drawable/ic_menu_camera"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:gravity="center_vertical"
android:padding="10dp"
android:text="#string/missing_permission"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="#+id/imageView"
app:layout_constraintEnd_toEndOf="#+id/imageView"
app:layout_constraintStart_toStartOf="#+id/imageView"
app:layout_constraintTop_toBottomOf="parent"
app:layout_constraintVertical_bias="0.858" />
<Button
android:id="#+id/setting_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="164dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="#string/settings"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#+id/imageView"
app:layout_constraintStart_toStartOf="#+id/imageView" />
</android.support.constraint.ConstraintLayout>
Here is my fragment with keyframe transition code
var goToSettings: () -> Unit = {}
private lateinit var root: ConstraintLayout
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
= inflater.inflate(R.layout.nocamera_permission_fragment_alt, container, false).also {
root = it.findViewById(R.id.fragment_root)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) =
super.onViewCreated(view, savedInstanceState).run {
setting_button.setOnClickListener { goToSettings() }
val constraintSet = ConstraintSet()
constraintSet.clone(activity, R.layout.nocamera_permission_fragment)
val transition = ChangeBounds().apply {
interpolator = OvershootInterpolator()
duration = 1000
}
TransitionManager.beginDelayedTransition(root, transition)
constraintSet.applyTo(root)
}
I has no effect on the layout , app renders the final layout R.layout.nocamera_permission_fragment after run . Does anybody have any idea ?
You are applying transition too soon (View is not even attached to window yet and had no layout pass), so delayed transition is discarded. Try using post instead of run.