I'm using navigation component and want to connect drawer layout with toolbar in each fragment, not the activity.
I tried this answer which is called on onViewCreated() but any view referenced from the activity is null. I guess it's because fragment is inflated in the layout before returning from the activity's onCreate method.
I use this extension function to connect the drawer with the fragment's toolbar, I tried to call it from onCreateView() and onViewCreated() but did't work and the activity's drawer layout is always null. I works only if it's called from onStart() but I don't think it's the right way:
private fun AppCompatActivity.setToolbar() {
setSupportActionBar(binding.toolbar)
setHasOptionsMenu(true)
val drawer = findViewById<DrawerLayout>(R.id.drawer)
binding.toolbar.setupWithNavController(findNavController(), drawer)
}
What's the right place to call this function?
When you call setContentView(R.id.activity_layout), the entire view hierarchy is first inflated, then attached to the Activity. It is only after setContentView() returns that findViewById() will find any of the newly inflated views.
When you use the <fragment> tag, the Fragment's view and the views of all of its child fragments are synchronously created as part of that inflation call. This means that setContentView() has not completed by the time the onCreateView() and onViewCreated() methods are called. This is why that calling findViewById() returns null - the activity's view hasn't actually finished being created.
FragmentContainerView was specifically built to avoid these special cases and instead use the same mechanisms as other fragments - namely, it just uses a normal FragmentTransaction to add your Fragment - the same as if you called beginTransaction()+commitNow() in your onCreate() method yourself. This means that the Fragment is not forced to synchronously create its view as part of setContentView(), but can do it alongside every other Fragment after setContentView() returns. This is what allows findViewById() from onCreateView() or onViewCreated() work.
You can achieve this by simply using an interface which you need to implement in activity. No need to reference activity's drawerLayout from fragment.
Interface like this:
interface DrawerWithToolbar {
fun setupToolbarDrawer(toolbar: MaterialToolbar)
}
Then in activity:
override fun setupToolbarDrawer(toolbar: MaterialToolbar) {
toolbar.setupWithNavController(navController,drawerLayout)
}
In fragment:
(activity as DrawerWithToolbar).setupToolbarDrawer(toolbar)
Related
I'm using navigation component and want to connect drawer layout with toolbar in each fragment, not the activity.
I tried this answer which is called on onViewCreated() but any view referenced from the activity is null. I guess it's because fragment is inflated in the layout before returning from the activity's onCreate method.
I use this extension function to connect the drawer with the fragment's toolbar, I tried to call it from onCreateView() and onViewCreated() but did't work and the activity's drawer layout is always null. I works only if it's called from onStart() but I don't think it's the right way:
private fun AppCompatActivity.setToolbar() {
setSupportActionBar(binding.toolbar)
setHasOptionsMenu(true)
val drawer = findViewById<DrawerLayout>(R.id.drawer)
binding.toolbar.setupWithNavController(findNavController(), drawer)
}
What's the right place to call this function?
When you call setContentView(R.id.activity_layout), the entire view hierarchy is first inflated, then attached to the Activity. It is only after setContentView() returns that findViewById() will find any of the newly inflated views.
When you use the <fragment> tag, the Fragment's view and the views of all of its child fragments are synchronously created as part of that inflation call. This means that setContentView() has not completed by the time the onCreateView() and onViewCreated() methods are called. This is why that calling findViewById() returns null - the activity's view hasn't actually finished being created.
FragmentContainerView was specifically built to avoid these special cases and instead use the same mechanisms as other fragments - namely, it just uses a normal FragmentTransaction to add your Fragment - the same as if you called beginTransaction()+commitNow() in your onCreate() method yourself. This means that the Fragment is not forced to synchronously create its view as part of setContentView(), but can do it alongside every other Fragment after setContentView() returns. This is what allows findViewById() from onCreateView() or onViewCreated() work.
You can achieve this by simply using an interface which you need to implement in activity. No need to reference activity's drawerLayout from fragment.
Interface like this:
interface DrawerWithToolbar {
fun setupToolbarDrawer(toolbar: MaterialToolbar)
}
Then in activity:
override fun setupToolbarDrawer(toolbar: MaterialToolbar) {
toolbar.setupWithNavController(navController,drawerLayout)
}
In fragment:
(activity as DrawerWithToolbar).setupToolbarDrawer(toolbar)
I'm using navigation component and want to connect drawer layout with toolbar in each fragment, not the activity.
I tried this answer which is called on onViewCreated() but any view referenced from the activity is null. I guess it's because fragment is inflated in the layout before returning from the activity's onCreate method.
I use this extension function to connect the drawer with the fragment's toolbar, I tried to call it from onCreateView() and onViewCreated() but did't work and the activity's drawer layout is always null. I works only if it's called from onStart() but I don't think it's the right way:
private fun AppCompatActivity.setToolbar() {
setSupportActionBar(binding.toolbar)
setHasOptionsMenu(true)
val drawer = findViewById<DrawerLayout>(R.id.drawer)
binding.toolbar.setupWithNavController(findNavController(), drawer)
}
What's the right place to call this function?
When you call setContentView(R.id.activity_layout), the entire view hierarchy is first inflated, then attached to the Activity. It is only after setContentView() returns that findViewById() will find any of the newly inflated views.
When you use the <fragment> tag, the Fragment's view and the views of all of its child fragments are synchronously created as part of that inflation call. This means that setContentView() has not completed by the time the onCreateView() and onViewCreated() methods are called. This is why that calling findViewById() returns null - the activity's view hasn't actually finished being created.
FragmentContainerView was specifically built to avoid these special cases and instead use the same mechanisms as other fragments - namely, it just uses a normal FragmentTransaction to add your Fragment - the same as if you called beginTransaction()+commitNow() in your onCreate() method yourself. This means that the Fragment is not forced to synchronously create its view as part of setContentView(), but can do it alongside every other Fragment after setContentView() returns. This is what allows findViewById() from onCreateView() or onViewCreated() work.
You can achieve this by simply using an interface which you need to implement in activity. No need to reference activity's drawerLayout from fragment.
Interface like this:
interface DrawerWithToolbar {
fun setupToolbarDrawer(toolbar: MaterialToolbar)
}
Then in activity:
override fun setupToolbarDrawer(toolbar: MaterialToolbar) {
toolbar.setupWithNavController(navController,drawerLayout)
}
In fragment:
(activity as DrawerWithToolbar).setupToolbarDrawer(toolbar)
When trying to inflate and set my presenter to my databinding component in this way my presenter methods are not called.
val fragmentBinding = FragmentListEditBinding.inflate(layoutInflater)
fragmentBinding.presenter = ListEditorPresenter(this, requireContext())
but when using this
val fragmentBinding = DataBindingUtil.setContentView<FragmentListEditBinding(requireActivity(), R.layout.fragment_list_edit)
fragmentBinding.presenter = ListEditorPresenter(this, requireContext())
It works fine, but then the layout is covering the full screen.
Any ideas how to fix this issue?
Please tell me if more context is needed.
The second method is for activity, not for the fragment, For fragment, you have to do it in the first method.
Before DataBinding and ViewBinding, In and activity we call setContentView(R.layout.activity_main) to set the view for the activity, but for fragment, we override the onCreateView method and inflate a view and return it.
So the way of setting view for activity and fragment is different from the beginning.
So the DataBindingUtil.setContentView is made for activity, while the FragmentListEditBinding.inflate custom/manual inflation is made for fragment. As i have already mentioned it above.
Hello i create class that implement BottomSheetDialogFragment with dynamic content. The content is a Fragment. So when initialize the BottomSheet i passing fragment object, and attach it to specific Container ID inside this BottomSheetDialogFragment. Looks like this :
private fun attachContentFragment() {
val transaction = childFragmentManager.beginTransaction()
transaction.apply {
replace(R.id.flContent, state.layoutContent)
commit()
}
}
state.layoutContent is my attached Fragment
I need to dismiss the BottomSheet if every action called in that fragment.
As far as i know, i need to get the object of BottomSheet that hold me(Fragment) and dismiss it.
But how i can get that BottomSheet object?
Thanks
So, technically it is a fragment inside fragment situation. I think there is several solutions here:
Call Activity from your child fragment. BottomSheetDialogFragment will subscribe to Activity for such events and react on them.
Get the instance of a BottomSheetDialogFragment by calling proper FragmentManager (which possible is an Activity one). You can get an instance of a fragment byTag for example.
Or you can call getParentFragment from a child Fragment.
In a Fragment's Lifecycle, the onAttach() method is called before the onCreate() method. I can't wrap my head around this. Why would you attach a Fragment first?
TL;DR:
In order to not break the design consistency amongst different UI components in android,the onCreate() method will have similar functionality across all of them.
When linking Containers to Contents like Window to Activity and Activity to Fragment a preliminary check needs to be done to determine the state of container.
And that explains the use and position of onAttach() in the fragment lifecycle.
Too short;Need longer:
The answer is in the archetype code itself,
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnFragmentInteractionListener");
}
}
Another example would be in Jake Wharton's ActionBarSherlock library.
Why would you want to use a method like onCreate() which is has the same purpose in an activity ,service.
The onCreate() is meant to handle issues with respect to that particular context creation.It does not make sense if onCreate() is used to check the state of its container.
The second reason that I can come determine is that a fragment is designed to be activity independent.The onAttach() provides an interface to determine the state/type/(other detail that matters to the fragment) of the containing activity with reference to the fragment before you initialize a fragment.
EDIT:
An activity exists independently and therefore has a self sustaining lifecycle.
for a fragment :
The independent lifecycle components(same as any other components):
onCreate()
onStart()
onResume()
onPause()
onStop()
onDestroy()
The interaction based components:
onAttach()
onCreateView()
onActivityCreated()
onDestroyView()
onDetach()
from the documentation:
The flow of a fragment's lifecycle, as it is affected by its host
activity, (...) each successive state of the activity determines which
callback methods a fragment may receive. For example, when the
activity has received its onCreate() callback, a fragment in the
activity receives no more than the onActivityCreated() callback.
Once the activity reaches the resumed state, you can freely add and
remove fragments to the activity. Thus, only while the activity is in
the resumed state can the lifecycle of a fragment change
independently.
However, when the activity leaves the resumed state, the fragment
again is pushed through its lifecycle by the activity.
answering another question which came up in the comments:
Caution: If you need a Context object within your Fragment, you can call getActivity(). However, be careful to call getActivity() only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle, getActivity() will return null.
The design philosophy states that a Fragment is designed for reuse. A fragment (by design) could(and should) be used across multiple activities.
The onCreate by definition is responsible to create a fragment.
Consider the case of orientation,your fragment could be:
- using different layouts in different orientations.
- applicable only in portrait orientation and not landscape
- To be used only on tables and mobile phones.
All these situations would require a check before the fragment is initialized from the android perspective(onCreate()) and the view inflated(onCreateView()).
Also consider the situation of a headless fragment.The onAttach() provides you the interface required for preliminary checks.
Because onAttach() assigns hosting activity to the Fragment. If it had been called after onCreate() then there would be no context for your fragment (getActivity() would return null) and you would not be able to do anything in onCreate() method without that context anyway.
Another fitting reason is that Fragment's lifecycle is similar to Activity's lifecycle. In Activity.onAttach() activity gets attached to its parent (a window). Similarly in Fragment.onAttach() fragment gets attached to its parent (an activity), before any other initialization is done.
This is related to retained fragments. Following Fragment setRetainInstance(boolean retain) documentation:
If set, the fragment lifecycle will be slightly different when an activity is recreated:
onDestroy() will not be called (but onDetach() still will be, because the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being re-created.
onAttach(Activity) and onActivityCreated(Bundle) will still be called.
Take a look at the source code (android.support.v4.app.FragmentManager, v21):
void moveToState(Fragment f,
int newState,
int transit,
int transitionStyle,
boolean keepActive) {
...
f.onAttach(mActivity);
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
mActivity.onAttachFragment(f);
}
if (!f.mRetaining) {
f.performCreate(f.mSavedFragmentState); // <- Here onCreate() will be called
}
...
}
Example
Case 1: not retained fragment or setRetainInstanceState(false)
Application is started. Fragment is added dynamically using FragmentManager or inflated from XML via setContentView().
onAttach() called after Activity super.onCreate() call - Activity is already initialised.
MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#1be4f2dd
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#1be4f2dd
Configuration changed. Activity recreates fragments from saved state, fragments are added/attached from inside Activity super.onCreate() call:
MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#2443d905
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#2443d905
MainActivity﹕ super.onCreate() returned
Case 2: setRetainsInstanceState(true)
Application is started. Fragment is added dynamically using FragmentManager or inflated from XML via setContentView(). Same as above:
onAttach() called after Activity super.onCreate() call - Activity is already initialised.
MainActivity﹕ call super.onCreate() before
MainActivity﹕ super.onCreate() returned
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#3d54a168
MainFragment﹕ onCreate() getActivity=com.example.MainActivity#3d54a168
Configuration changed.
Fragment onCreate() not called, but onAttach() still called - you need to know, that hosting Activity has changed. But still fragment is already created, so no onCreate() called.
MainActivity﹕ call super.onCreate() before
MainFragment﹕ onAttach() getActivity=com.example.MainActivity#d7b283e
MainActivity﹕ super.onCreate() returned
Two Points from Android developer Site hints at why onAttach() is called before onCreate() in case of Fragment Life cycle.
A fragment must always be embedded in an activity. Now this means for Fragment to EXIST, there has to be a "living" Activity.
Also, When you add a fragment as a part of your activity layout, it lives in a ViewGroup inside the activity's view hierarchy.
So Fragment must FIRST "attach" itself to activity to defines its own view layout
onCreateis Called to do initial creation of a fragment.
It is obvious that you will create something only when its pre-condition of creation is in place (and the pre-condition is A fragment must always be embedded in an activity, and it must be attached to its Activity)