I have a video application - a video list and tapping on an item goes to video details. I want video details to be a full screen dialog fragment (to be able to do the motion layout animation from here https://medium.com/vrt-digital-studio/picture-in-picture-video-overlay-with-motionlayout-a9404663b9e7). But the previous dialog is not preserved behind (a blank screen is displayed). Weird enough BottomSheetDialogFragment works but DialogFragment does not.
So the question is - Should a dialog fragment have the previous fragment displayed under it when using jetpack navigation? Why bottom sheet works and dialog not?
Navigation is done using android jetpack navigation:
<action
android:id="#+id/action_videosFragment_to_video_details_nav_graph"
app:destination="#id/video_details_nav_graph"/>
and details nav graph:
<navigation 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/video_details_nav_graph"
app:startDestination="#id/videoDetailsFragment">
<dialog
android:id="#+id/videoDetailsFragment"
android:name="com.myapp.mobile.ui.video.VideoDetailsFragment"
android:label="VideoDetailsFragment"
tools:layout="#layout/fragment_video_details">
</dialog>
</navigation>
Am I missing something? Thanks
After looking more into it, managed to make it work with:
override fun onStart() {
super.onStart()
val dialog: Dialog? = dialog
if (dialog != null) {
val width = ViewGroup.LayoutParams.MATCH_PARENT
val height = ViewGroup.LayoutParams.MATCH_PARENT
dialog.window?.setLayout(width, height)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.window?.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
}
}
Related
I have countered a situation that needs to implement a scrollable modal bottom sheet. Basically, it will show half off the screen at first, then when scrolling up, it will stretch to 90% of the screen. If keep swiping up, it will scroll the content inside bottom sheet.
What I have tried
layout.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/design_bottom_sheet"
style="#style/RootLayout"
android:background="#drawable/rounded_shape"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<...Content.../>
</android.core.widget.NestedScrollView>
</FrameLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
Bottom Sheet Dialog class
I followed the answer here accepted answer
Result
It only shows half of the screen when opened, and not scroll the bottom sheet to Expanded State when I scroll the contents up like this result.
Expectation
I expected the above code should behave like this expectation
Does any one have the solution for this situation?
Thank you
So after more than a week, I found a solution for this problem,
I override the onCreateDialog method like below
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// This will disable the behavior of bottom sheet to have a flat corner
//https://github.com/material-components/material-components-android/pull/437#issuecomment-678742683
val dialog = BottomSheetDialog(requireContext(), theme)
dialog.behavior.disableShapeAnimations()
dialog.setOnShowListener { dialogInterface ->
val bottomSheetDialog = dialogInterface as BottomSheetDialog
val parentLayout =
bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
parentLayout?.let { it1 ->
val behaviour = BottomSheetBehavior.from(it1)
it1.layoutParams.also {
it.height =
context?.resources?.displayMetrics?.heightPixels?.times(0.9)?.toInt()!!
}
behaviour.peekHeight =
context?.resources?.displayMetrics?.heightPixels?.times(0.6)?.toInt()!!
}
}
return dialog
}
What is happening in this code?
I was NOT use the dialog provider by BottomSheetDialog neither super.onCreateDialog()
Instead I used BottomSheetDialog(context,theme) in order to create my custom bottom sheet theme.
Then, I set listener for dialog, get parent layout and bottom sheet dialog like below. The most important part is dealing with behavior.
After achieved the behavior correctly, you are done. Setting it's state to be expanded as recommended by #Nitish Chaudhary and setting the layout height to the height that you want. That's all I need, also this code below is reusable to me wherever you follow the modal bottom sheet XML layout rule.
Happy coding.
Forgive me if this sounds absurd. Here is the scenario, I have one activityViewModel that share communication between a screen fragment and a bottom sheet fragment. A bottom sheet fragment is used to select any payment card.Selecting a card from the bottom sheet leads to, selected card info will be displayed on the screen fragment: display card info(as text values) and status(as if the card is default card to pay that will set visible the default otherwise set gone)
My problem is, the display card info works(updating text values), but the status doesn't(set visibility)
I know this sounds like a matter of debugging, but really I striped down everything(including data binding), I couldn't find where the mistake is.
Here are codes snippet
In activity
private val viewModel by viewModels<PayingActivityViewModel>()
In Screen Fragment
private val activityViewModel by activityViewModels<PayingActivityViewModel>()
//...
activityViewModel.cardStatusDisplay.observe(viewLifecycleOwner, {
// THIS DOESN'T WORK
if(it == true) scanPaying_tv_defaultStatus.visibility = View.VISIBLE
else scanPaying_tv_defaultStatus.visibility = View.GONE
})
Bottom sheet fragment
private val activityViewModel by activityViewModels<PayingActivityViewModel>()
...
selectExistingCards_btn_confirm.setOnClickListener {
with(activityViewModel) {
setSelectedPayingCard(selectedCard)
setStatus(selectedCard?.cardData?.isDefault == true) // this is a code line that update live data in activity view model
setSelectedCardPosition(selectedPosition)
}
dismiss()
}
Thanks for helping.
Update answer
I found out the root cause for that horrible error, is that I'm using MotionLayout for the Screen Fragment layout, which prevent the visibility setting from working as expected. I'm working on the solution, however just wanted to update here, so you guys know why it is so weird. Thanks community.
Are you sure activityViewModel.cardStatusDisplay.observe is called? If so, you can try View.INVISIBLE instead of View.GONE(and set scanPaying_tv_defaultStatus to invisible as well in your xml layout file)
If activityViewModel.cardStatusDisplay.observe is not called, then perhaps you forgot to trigger a refresh in setStatus():
activityViewModel.cardStatusDisplay.value = activityViewModel.cardStatusDisplay.value
The answer to this is actually related to Motion Layout scene setup. Tldr reason is motion layout blocks control of visibility of all its children. MotionLayout is children of ConstraintLayout therefore it should be working the same thinking appears to be quite naive.
To resolve it, the visibility needs to be ignored as PropertySet, under the ConstraintSet start.
<Constraint
android:id="#id/defaultStatus">
<Layout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="17dp"
android:layout_marginLeft="17dp"
android:layout_marginBottom="16dp"
motion:layout_constraintBottom_toBottomOf="#id/card3"
motion:layout_constraintLeft_toRightOf="#id/cardNumber"
motion:layout_constraintTop_toBottomOf="#id/paymentMethod"
/>
<PropertySet
android:visibility="visible"
motion:visibilityMode="ignore" />
</Constraint>
Reference: https://blog.stylingandroid.com/motionlayout-visibility/ Thanks Mark for the awesome blog post.
Using android jetpack navigation in combination with toolbar and drawer is that the root destination has a hamburger menu icon (to toggle the drawer) and in child fragments there is a back button.
Also an animation exists when opening / closing child fragments on the back arrow.
Now the problem: In one of my child fragments I set a custom navigation back button
toolbar_main.setNavigationIcon(R.drawable.ic_clear)
This also works, but upon closing there is a "glitch" where
The custom icon disappears
The back arrow is visible for a short while (this is the "glitch"
The child closes and the root fragment (with burger icon) is visible again
Question:
Is this "glitch" a bug or do I have to call something other than setNavigationIcon (like ActionBarDrawerToggle or similar) ?
Solution: in each Fragment
override fun onAttach(context: Context) {
super.onAttach(context)
val activity = context as BaseActivity
if (navController.backStack.size > 3) {
activity.toolbar.setNavigationIcon(getNavigationIcon())
}
}
more than 3 because:
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/navigation"
app:startDestination="#+id/startFragment"> <----- always +1
<fragment
android:id="#+id/startFragment"> <----- startFragment + 1 = 2
<fragment
android:id="#+id/fragment_1"> <----- startFragment + 1 = 3
<fragment
android:id="#+id/fragment_2"> <----- startFragment + 1 = 3
</navigation>
I am able to open my BottomSheetDialogFragment with
val bottomSheet = BottomSheetFragment()
bottomSheet.show(fragmentManager!!, "BottomSheet")
but it only opens to show half of its content - I would like it to expand on opening to the full height of the screen without having to drag it up.
I have looked around and it seems one way is to set the BottomSheetBehavior state to STATE_EXPANDED, but I have not been able to find a solution on how to do this in Kotlin.
Any help would be appreciated!
You can set the BottomSheetBehavior state by placing this inside
onViewCreated of your BottomSheetDialogFragment.
dialog.setOnShowListener { dialog ->
val d = dialog as BottomSheetDialog
val bottomSheet = d.findViewById<View>(R.id.design_bottom_sheet) as FrameLayout
val bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}
You may also want to set the peek height to the height of your dialog to prevent the dialog getting stuck half way when attempting to dismiss it.
bottomSheetBehavior.peekHeight = bottomSheet.height
I'm having a problem where Dialogs don't scroll when their content is too large for the screen. I assume this is because Dialogs are not displayed within Scrollable containers.
Screenshot contains content wrapped in a ScrollView - You can see only the content is scrollable
Extra fields added to artificially increase dialog size for this example
You can see from the Android Developer Documentation that Dialogs should be wrapped within DialogFragments (This gives the benefit of having your dialogs survive an orientation change and response to lifecycle events) and this is the set up I'm trying to make work.
Much of the other answers I've found are all similar to one another and revolve around ensuring the window is set to "adjustResize". However, this would only make the parent view smaller on keyboard opening, it wouldn't make a View scrollable if it wasn't in a Scrollable container.
If someone could let me know if they have info to make a Dialog scrollable or to confirm that you cannot make a Dialog scrollable I'd appreciate it.
I have achieved scrolling in dialog fragment,
Dialog layout design
<LinearLayout>
<NestedScrollView>
<ConstrainLayout>
</ConstrainLayout>
</NestedScrollView>
</LinearLayout>
In DialogFrament class ,
override fun onStart() {
super.onStart()
dialog.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZ)
}
This is possibly the dirtiest and most likely to break solution to this problem:
After creating the Dialog in onCreateDialog() but before returning it you can achieve scrolling by adding this code:
final ViewGroup content = (ViewGroup) dialog.findViewById(android.R.id.content);
content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
content.getViewTreeObserver().removeGlobalOnLayoutListener(this);
View inner = content.getChildAt(0);
content.removeViewAt(0);
ScrollView scrollView = new ScrollView(getContext());
scrollView.addView(inner);
content.addView(scrollView);
}
});
I had the same problem with DialogFragment, but I changed:
<fragment android:layout_height="425dp"
...
to this:
<fragment android:layout_height="wrap_content"
...
And now it's scrolling when the keyboard is open, or when the screen is in landscape mode.
NOTE: I have ScrollView as a rootView in DialogFragment layout, of course.