How to access a view from parent layout with ViewBinding? - android

I have a layout that contains a view pager and a fab.
The view pager has fragments as pages.
What I want is to access the FAB from inside a fragment.
fragment_user_anime_list(parent)
<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"
android:fitsSystemWindows="true"
tools:context="com.destructo.sushi.ui.user.animeList.MyAnimeListFragment">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
style="#style/ToolBarWithNavigation"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="#style/PopupMenu"
app:title="#string/my_anime_list"
app:titleTextAppearance="#style/TextAppearance.Sushi.H1" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/my_anime_list_tablayout"
style="#style/TabLayoutStyle"
android:layout_width="fill_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:tabIndicator="#drawable/custom_tab_layout_indicator"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/my_anime_list_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
<ProgressBar
android:id="#+id/progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_gravity="center"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/random_fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="#dimen/margin_normal"
android:layout_marginBottom="#dimen/margin_normal"
android:layout_gravity="bottom|right"
android:src="#drawable/ic_question_line"
app:layout_behavior="#string/hide_bottom_view_on_scroll_behavior"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
fragment_my_anime_list(child viewpager page)
<layout xmlns:android="http://schemas.android.com/apk/res/android"
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">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/userAnimeRecycler"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="#+id/user_anime_list_pagination_progressbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="#+id/user_anime_list_progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/userAnimeRecycler" />
<ProgressBar
android:id="#+id/user_anime_list_pagination_progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
in the child fragment
private lateinit var binding: FragmentUserAnimeListBinding
private lateinit var randomAnimeFab: FloatingActionButton
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentUserAnimeListBinding
.inflate(inflater, container, false).apply {
lifecycleOwner = viewLifecycleOwner
}
randomAnimeFab = binding.root.random_fab
return binding.root
}
This throws Null pointer exception. view cannot be null
How do I get a reference to the fab using viewBinding?
I found a solution without using view binding. Don't know if it's the best one.
You get the parentfragment and reference it's views directly.
val fab = requireParentFragment().random_fab
and then you can check if it's null and do whatever you want
fab?.let{
//Do something
}
If anyone knows how to do it using viewBinding let me know.

How do I get a reference to the fab using viewBinding?
Now you want to access the FAB from one of ViewPager pages/fragments through data binding.
Normally views should be accessed within its lifecycle owner (i.e. its activity/fragment), so views of the parent fragment (which hosts the FAB & the ViewPager), should be touched by this fragment, not by a page fragment.
You did a milestone by using requireParentFragment().
val fab = requireParentFragment().random_fab
But instead of referencing the FAB directly from the page, you can create a method in the parent fragment instead and access this method from the page. And do the changes on FAB on this method.
And through this method, you can pass data between a page and the parentFragment without having to touch views of a fragment from another fragment.
So you could have a method in the parent fragment:
fun changeMyFab() {
binding.random_fab ...
}
And call requireParentFragment().changeMyFab() from the page, and you can pass some data as method parameters to change the FAB behavior.

Related

ViewStub inflation of ConstraintLayout

I want to leverage ViewStub to optionally show a portion of UI. When the root of my inflated item is ConstraintLayout then it doesn't render.
But if that root is MaterialCardView then it displays as intended.
<!-- my_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"
android:id="#+id/fragmentRootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ViewStub
android:id="#+id/fragmentViewStub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="#+id/fragmentViewStub"
android:layout="#layout/item"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- item.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"
android:id="#+id/itemRootLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.textview.MaterialTextView
android:id="#+id/itemLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/itemLabelText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
/* MyFragment.kt */
#AndroidEntryPoint
class MyFragment : Fragment(R.layout.my_fragment) {
private val viewModel: MyViewModel by viewModels()
private val viewBinding: MyFragmentBinding by viewLifecycleLazy {
MyFragmentBinding.bind(requireView())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewBinding.fragmentViewStub.inflate()
}
}
I've tried matching the ViewStub's id/inflatedId attributes and programmatically setting dimensions/constraints but neither solved the issue.
What am I overlooking about making these pieces work together?
Forgot to update with my answer/oversight. This worked as expected in both cases all along.
Nightmode was enabled and the behavior of MaterialCardView has automatic contrast between its card and content. Meanwhile, when ConstraintLayout inverts its theme the label appears to be missing but is indeed present, as white text on white background.

Fragment not using full height of screen, but only after coming back from another Activity

I have an Activity with a TabLayout, a ViewPager2 and a FragmentContainerView like this:
<RelativeLayout 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"
android:theme="#style/AppTheme"
tools:context="com.safirecctv.easyview.activities.main.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/activity_main_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/visiotech_red"
app:popupTheme="#style/AppTheme" />
<com.google.android.material.tabs.TabLayout
android:id="#+id/activity_main_tab_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/visiotech_red"
app:tabIconTint="?attr/tabLayoutTabColor"
app:tabIndicatorColor="?attr/colorSecondary" />
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/activity_main_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/main_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/app_bar" />
</RelativeLayout>
When I first launch the Activity with its Fragment (I've reduced it to one tab and one fragment for the sake of simplicity), everything works as expected, the Fragment takes the whole screen. But when I come back from another Activity by calling:
mActivity?.finish()
The fragment loads well, but only takes half of the screen height. I've tried to inspect with Layout Inspector, but if I attach it before coming back, the app force closes, and if I attach it after (when the Fragment is loaded and only taking half of the screen height), then it recreates and works well again.
I am testing in a real device, not in the emulator.
This is my Fragment XML:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable name="viewModel" type="com.safirecctv.easyview.viewmodels.main.DeviceManagementViewModel"/>
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:orientation="vertical"
android:id="#+id/list_view_devices"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/listViewBackgroundColor"
app:data="#{viewModel.devices}" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_margin="16dp"
android:src="#drawable/icon_plus"
app:backgroundTint="#color/visiotech_red"
app:tint="?attr/buttonTextColor"
tools:ignore="ContentDescription" />
</RelativeLayout>
</layout>
And this is the code behind:
package com.safirecctv.easyview.activities.add_edit_device.fragments
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.safirecctv.easyview.R
import com.safirecctv.easyview.databinding.ActivityAddEditDeviceIpddnsFragmentBinding
import com.safirecctv.easyview.viewmodels.add_edit_device.IpDdnsDeviceViewModel
import dagger.hilt.android.AndroidEntryPoint
/**
* Fragment used to add or edit IP/DDNS devices to/from the database.
* As of June, 2021, the following vendors support IP/DDNS login: Safire/Hikvision, XSecurity/Dahua, Uniview.
*/
#AndroidEntryPoint
class AddEditIPDDNSDeviceFragment : Fragment(R.layout.activity_add_edit_device_ipddns_fragment) {
private val mViewModel: IpDdnsDeviceViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = ActivityAddEditDeviceIpddnsFragmentBinding.bind(view)
binding.apply {
btnSaveDeviceIpDdns.setOnClickListener { trySaveDevice() }
viewModel = mViewModel
lifecycleOwner = viewLifecycleOwner
}
}
//endregion
//region Device methods
private fun trySaveDevice() {
//mViewModel.trySaveDevice()
activity?.finish()
}
//endregion
}
Could it be related to the fact that I'm not calling onCreateView in the Fragment but instead delegating it to the constructor?
Thank you very much.
EDIT: I could open Layout Inspector finally, and this is what it shows: RecyclerViewImpl is taking the whole space but the FrameLayout inside it only takes like half of the screen:
I finally found it. The problem was in the Activity's XML. Everything was inside AppBarLayout and that was causing the issue. I just left the MaterialToolbar inside AppBarLayout and the rest as a sibling, as explained in this post (in Spanish). Then everything started working as intended.

Android: Adding RecyclerView below Fragment

I'm trying to add a RecyclerView underneath a Fragment that I add programmatically, but it doesn't show up. I would like the RecyclerView to dynamically use the remaining space of the screen (since the Fragment above it can vary in size) so I put constraints at the edges and set the height to 0dp. The RecyclerView populates just fine, but it displays somewhere below the screen (I have to put the height of the RecyclerView to ~1300 dp to make it show up).
activity_main.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:background="#color/colorPrimary">
<ImageView
android:id="#+id/Toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#color/colorPrimary"
android:contentDescription="#null"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<RelativeLayout
android:id="#+id/FragmentContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/Toolbar" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/RecyclerView"
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/FragmentContainer"/>
</androidx.constraintlayout.widget.ConstraintLayout>
I then use this code to programmatically add the Fragment to my FragmentContainer:
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Remove current fragment(s)
for (fragment in supportFragmentManager.fragments) {
supportFragmentManager.beginTransaction().remove(fragment!!).commit()
}
val transaction: FragmentTransaction = supportFragmentManager.beginTransaction()
var fragment: Fragment
if (someBoolean)
fragment = FirstFragment()
else
fragment = SecondFragment()
// Add correct fragment to container
transaction.add(R.id.FragmentContainer, fragment)
transaction.commit()
}
I have tried putting the RecyclerView into various layouts, but the results weren't any better. Any ideas are appreciated!

Where to add toolbar and navigation bar

I'm using android navigation controller.
My main activity hosts a nav fragment but where should other components like toolbars and nav bottom bar go, in the main activity or child fragments?
activity_main.xml
<?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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="#style/AppTheme.PopupOverlay" />
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:navGraph="#navigation/navigation" />
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/menu_bottom_nav" />
</LinearLayout>
fragment_home.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</RelativeLayout>
or should ToolBar and BottomNavigationView be in fragment_home.xml like so
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="#style/AppTheme.PopupOverlay" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_nav_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/menu_bottom_nav" />
</RelativeLayout>
I'll supply you with an example. Keep in mind, I'm compiling with the latest androidx for SDK 28+ so if you are on older, your namespaces will be slightly different.
I'm also using Databinding and Kotlin, so don't use the layout and data tags if you are not using databinding.
TOOLBAR
<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>
PUT BINDING VARIABLES HERE
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/SSTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
android:theme="#style/ToolbarTextAppearance">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
MAIN ACTIVITY
<?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>
<variable name="activity" type="com.a35.activities.MainActivity"/>
<variable name="iBindingRecyclerView" type="com.a35.interfaces.IBindingRecyclerView"/>
</data>
<androidx.drawerlayout.widget.DrawerLayout
android:id="#+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
tools:openDrawer="start">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--TOP TOOLBAR-->
<include
android:id="#+id/toolbarMain"
layout="#layout/toolbar_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!--TOP BLACK LINE-->
<View
android:id="#+id/vRedLine"
android:layout_width="match_parent"
android:layout_height="#dimen/dp_2"
android:background="#color/black" />
<!-- FrameLayout is used to insert fragments to display -->
<FrameLayout
android:id="#+id/fragPlaceholder"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</LinearLayout>
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
android:background="#color/colorPrimary">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/nav_drawer_header"
android:id="#+id/navHeader"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/lstMenuItems"
android:layout_below="#+id/navHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:bindRcvInterface="#{iBindingRecyclerView}"
app:bindRcvList="#{activity.getDrawerItemList}"/>
<ImageView
android:id="#+id/imgBottomLogo"
android:layout_width="#dimen/dp_160"
android:layout_height="#dimen/dp_35"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="#dimen/dp_35"
android:src="#drawable/scott_logo" />
</RelativeLayout>
</com.google.android.material.navigation.NavigationView>
</androidx.drawerlayout.widget.DrawerLayout>
</layout>
Notice the Main Activity holds Navigation View that is inside a drawer layout which contains the Recycler View for building your drawer content.
Outside of that you will see the content placeholder for fragments called fragPlaceHolder.
Lastly notice the parent layout is a LinearLayout with the first element being the include for the toolbar to include as we see fit.
Next your Styles. You'll need to use a style that does not rely on the action bar if you plan to use the toolbar as the actionbar. (for the record, you should be using the toolbar)
STYLE
<!--Full Screen-->
<style name="A35.FullScreen">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
</style>
MANIFEST
<application
android:name=".application.A35Application"
android:allowBackup="true"
android:icon="#mipmap/a35_logo"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/A35.FullScreen">
You can also put it on individual Activities if that fits your need better.
Now you have two steps left.
You need to call setActionToolbar and pass your found UI or use synthetic views if you are using kotlin and databinding that is simple to do. So add in your onCreate AFTER inflating the view.
setSupportActionBar(toolbar)
You didn't ask this, but I'll supply it as well. When you need to change out your fragment, I typically have a BaseActivity with a helper method that does it like so.
protected fun swapFragment(fragment: BaseFragment, #Nullable bundle: Bundle?, hideCurrentFrag: Boolean = false) {
if (fragment.isVisible) {
A35Log.e(mClassTag, "swapFragment called on already visible fragment")
return
}
A35Log.v(mClassTag, "swapFragment( ${fragment.javaClass.simpleName} )")
val currentFragBundle = fragment.arguments
if (currentFragBundle == null && bundle != null) {
fragment.arguments = bundle
A35Log.v(mClassTag, "current bundle is null, so setting new bundle passed in")
} else if (bundle != null) {
fragment.arguments?.putAll(bundle)
A35Log.v(mClassTag, "current fragment bundle was not null, so add new bundle to it")
}
val fragmentManager = supportFragmentManager
fragmentManager.executePendingTransactions()
val fragmentTransaction = fragmentManager.beginTransaction()
//Make sure the requested fragment isn't already on the screen before adding it
if (fragment.isAdded) {
A35Log.v(mClassTag, "Fragment is already added")
if (fragment.isHidden) {
A35Log.v(mClassTag, "Fragment is hidden, so show it")
fragmentTransaction.show(fragment)
if(hideCurrentFrag) {
A35Log.v(mClassTag, "hideCurrentFlag = true, hiding current fragment $mSelectedFragment")
fragmentTransaction.hide(mSelectedFragment!!)
}else{
A35Log.v(mClassTag, "hideCurrentFlag = false, removing current fragment $mSelectedFragment")
fragmentTransaction.remove(mSelectedFragment!!)
}
}else{
A35Log.v(mClassTag, "Fragment is already visible")
}
}else if(mSelectedFragment == null){
A35Log.v(mClassTag,"mSelectedFragment = null, so replacing active fragment with new one ${fragment.javaClass.simpleName}")
fragmentTransaction.replace(R.id.fragPlaceholder, fragment)
}else{
A35Log.v(mClassTag, "Fragment is not added, so adding to the screen ${fragment.javaClass.simpleName}")
fragmentTransaction.add(R.id.fragPlaceholder, fragment)
if(hideCurrentFrag) {
A35Log.v(mClassTag, "hideCurrentFlag = true, hiding current fragment $mSelectedFragment")
fragmentTransaction.hide(mSelectedFragment!!)
}else{
A35Log.v(mClassTag, "hideCurrentFlag = false, removing current fragment $mSelectedFragment")
fragmentTransaction.remove(mSelectedFragment!!)
}
}
A35Log.v(mClassTag, "committing swap fragment transaction")
fragmentTransaction.commit()
A35Log.v(mClassTag, "mSelectedFragment = ${fragment.javaClass.simpleName}")
mSelectedFragment = fragment
}
NOTE* While swapping fragments is fairly universal, you need to make sure you are handling your needs properly. Should it be hiding, or removing. Should it be handling the bundles or ignoring them. The method I supplied you is basically showing if non-existent and hiding fragment instead of removing if told to hide it.
Handling building an adapter and list for the nav drawer and wiring up listener clicks on those items is not shown here as that was not your question and is beyond the scope of this question. I don't want to make my answer too bloated, so hopefully that is all you need to get going.
Happy Coding.

BottomSheetBehavior is not a child of CoordinatorLayout

this is my XML layout with name songlist :
<android.support.design.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:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/viewA"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.6"
android:background="#android:color/holo_purple"
android:orientation="horizontal"/>
<android.support.v4.widget.NestedScrollView
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_bright"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="308dp"
/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:src="#drawable/personlog"
app:layout_anchor="#id/viewA"
app:layout_anchorGravity="bottom|center"/>
</android.support.design.widget.CoordinatorLayout>
and this is my fragment which contain this layout :
public class SongList extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.songlist,container,false);
textView=(TextView)view.findViewById(R.id.txt);
View bottomSheet = view.findViewById(R.id.bottom_sheet);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
bottomSheetBehavior.setPeekHeight(200);
return view;}
}
but when lunch the app give me this error :
java.lang.IllegalArgumentException: The view is not a child of CoordinatorLayout
from this line :
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
how can fix this ? seems all things work fine but give that error ... if any one can please help
The BottomSheetBehavior is
An interaction behavior plugin for a child view of CoordinatorLayout to make it work as a bottom sheet.
At the moment you bottom sheet NestedScrollView is a child of LinearLayout. So just drop the outer-most LinearLayout all completely.
<android.support.design.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:layout_height="match_parent">
<LinearLayout
android:id="#+id/viewA"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.6"
android:background="#android:color/holo_purple"
android:orientation="horizontal"/>
<android.support.v4.widget.NestedScrollView
android:id="#+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_bright"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="308dp" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:clickable="true"
android:src="#drawable/personlog"
app:layout_anchor="#id/viewA"
app:layout_anchorGravity="bottom|center" />
</android.support.design.widget.CoordinatorLayout>
But now you have some more issues with the bottom sheet you're trying to implement. Firstly you should not use wrap_content with a scroll view. Secondly you should not use a list view inside a scroll view, since it's implementing its own scrolling. You might be able to simplify this by only using the list view as a bottom sheet.
If you are using data binding and including the layout to the fragment you have to do like following
val sheetBehavior = BottomSheetBehavior.from(binding.layoutBottomSheet.root)
Note, if you don't use a CoordinatorLayout and instead use the BottomSheetDialogFragment (which makes one for you), I noticed that you cannot navigate to that dialog fragment with Fragment directions using the current version of the Nav component library (2.1.0-alpha05) and must instantiate it as a new fragment dialog otherwise you get this error, i.e. instead of using this:
navController().navigate(MerchantHistoryFragmentDirections.showDateSelection())
You must use this:
fragmentManager?.let {
val dateSelection = DateSelectionFragment.newInstance()
dateSelection.setTargetFragment(this, RC_DATE_SELECTION)
dateSelection.show(it)
}
It's a kind of obtuse error so hopefully this helps someone.
In my case I have used the following solution to solve the issue
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener { dialogInterface ->
val bottomSheetDialog = dialogInterface as BottomSheetDialog
setupFullHeight(bottomSheetDialog)
}
return dialog
}
val bottomSheet = bottomSheetDialog
.findViewById<FrameLayout>(R.id.design_bottom_sheet)
val behavior: BottomSheetBehavior<*>?
if (bottomSheet != null) {
behavior = BottomSheetBehavior.from(bottomSheet)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
behavior.isDraggable = false
}
In my case instead of
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
I used <android.support.constraint.ConstraintLayout (in a fragment or activity that contains a layout and a BottomSheet).
you have to set your bottom sheet layout in appbar layout below your content layout
you should change your layout design to
<androidx.coordinatorlayout.widget.CoordinatorLayout
it works for me

Categories

Resources