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.
Currently if I want attach bindings I do
DataBindingUtil.setContentView(this, R.layout.main_activity);
I'm having troubles with a custom third-party Activity that needs to call
setContentView(R.layout.main_activity);
How I can attach binding to an activity where the layout was set with setContentView
You need to inflate you layout first, call setContentView(inflatedView) and set the DataBinding:
/* I don't know if passing 'null' as parent breaks something in your layout, I tested it in
mine and it did work.*/
View rootView = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
setContentView(rootView);
ActivityMainBinding binding = DataBindingUtil.bind(rootView);
I'm studying fragments from this link : http://developer.android.com/guide/components/fragments.html
There's a piece of code given as:
public static class ExampleFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
I was confused about the attachToRoot parameter so I looked up on Stack Overflow for some help and found good answers on similar problem. So what I understand is if you set it to true, the fragment gets attached to the activity's root layout, and derives its layoutparams from there. If it's false it would simply return the root of the inflated layout and acts like a standalone view for a fragment (deriving the layout params from the passed in container).
Now I read further down in the documentation regarding attachToRoot for the above example:
A boolean indicating whether the inflated layout should be attached to
the ViewGroup (the second parameter) during inflation. (In this case,
this is false because the system is already inserting the inflated
layout into the container—passing true would create a redundant view
group in the final layout.)
I don't get the last parenthesis statement where it says that it should be false because we're already inserting the layout into the container. What does it mean that we're already inserting into the container without attachToRoot as true? If the parameter is true, how would the final layout have redundant view groups. An example to elaborate this part would be a great help. Thanks.
I don't usually answer my own questions, but after doing a bit more research for this, I thought that maybe this will help someone else. Although Marcin's answer is correct, but I'm just answering a bit more in detail.
As per code :
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
The second parameter container is a framelayout with id fragment_container that activity uses to add fragment to it's layout.
Now, if we dive deeper into inflate method of LayoutInflater class, this is the code (I'm just highlighting the relevant parts of code rather than the whole part) :
// The view that would be returned from this method.
View result = root;
// Temp is the root view that was found in the xml.
final View temp = createViewFromTag(root, name, attrs, false);
Firstly, it creates a temp view from the supplied root.
In case attachToRoot is true, it does this :
if (root != null && attachToRoot) {
root.addView(temp, params);
}
It adds the temp view created above to the root view (i.e. container).
In case attachToRoot is false, it does this:
if (root == null || !attachToRoot) {
result = temp;
}
As quite evident, in case attachToRoot is true, it simply returns the root (fragment_container i.e the id activity uses to place the fragment inside it.) after adding the temp view to it (Root view in example_fragment in this case)).
In case attachToRoot is false, it simply returns the root of the fragment's xml, i.e. the container parameter is just used to get layoutParams for fragment's root view (since it doesn't have a root, so it needs params from somewhere).
The problem in case of true, arises in above example, because the return value is root (fragment_container with added view temp, and fragment_container by default has a parent already.). Now, if you try to do a fragment transaction, you're trying to add a child view fragment_container(which already has a parent to another xml (framelayout that you defined to add the fragment to).
Due to this, Android throws the following exception:
if (child.getParent() != null) {
throw new IllegalStateException("The specified child already has a parent. " +
"You must call removeView() on the child's parent first.");
}
Problem while setting it to true and returning is that the view returned already has a parent, so can't be used else where. Other way, you could make a separate view group inside onCreateView (maybe a LinearLayout), set the parameter to true, and return the view. Then it'll work fine because the viewgroup won't have an existing parent.
This is my understanding of the above problem, I might be wrong in which case I'd like any Android expert to correct this.
It means that in case on onCreateView() returned View will be attached to container view anyway, so setting it to true in your onCreateView() would cause it to be added twice to container layout, which isn't what you usually want. And setting root view to not null and still have attachToRoot false allows inflated view to derive from root, w/o being added.
check this
View v =getLayoutInflater.inflate(R.layout.example_fragment, viewgroup);
and also this
View v =getLayoutInflater.inflate(R.layout.example_fragment, null);
now if you call this for the first scenario
Log.v("testing",String.valueOf(v.getParent() == null));
you will get false as the output, but when you check that on the second line you will get true.
Simple understanding is if you specify a parent(ViewGroup) it is attached to it, but if you add the last parameter the you are telling the inflater to skip the attachment of the View so your returned View has no parent, but if you specify null, who's going to be attached to? NOBODY so the returned View has no parent, and also like you stated it derives layoutParameters for the View.
If you dont add the last parameter the View will have its parent as the Activity View and hence will be returned in your OncreateView having already a parent, before it will be added, which will cause some Exceptions as a View cannot have different daddies and the Fragment will not be able to manage the View itself.
if add true return null from your oncreateView
The View that you return from onCreateView() method of a fragment, will be attached to the container of the fragment by the system. So the third parameter of inflate() method should be false in this case. If you set it to true then it will be attached to the root twice. So you should set it to false in this case.
Now, the inflated view derives its layout parameters from the second parameter of inflate() method not from the third boolean parameter.
ok from android official documentation Android documentation
What is Fragment?
A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a "sub activity" that you can reuse in different activities).
How to Use Fragment in Activity?
A fragment must always be embedded in an activity and the fragment's lifecycle is directly affected by the host activity's lifecycle. For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments. However, while an activity is running (it is in the resumed lifecycle state), you can manipulate each fragment independently, such as add or remove them.
What is Fragment Transaction
you can manipulate each fragment independently, such as add or remove them. When you perform such a fragment transaction, you can also add it to a back stack that's managed by the activity—each back stack entry in the activity is a record of the fragment transaction that occurred. The back stack allows the user to reverse a fragment transaction (navigate backwards), by pressing the Back button.
How Fragment and Activity Interact with each other
When you add a fragment as a part of your activity layout, it lives in a ViewGroup inside the activity's view hierarchy and the fragment defines its own view layout. You can insert a fragment into your activity layout by declaring the fragment in the activity's layout file, as a element, or from your application code by adding it to an existing ViewGroup.
Fragment Lifecycle methods onCreateView()
onCreate()
onPause()
And your question is
What does it mean that we're already inserting into the container without attachToRoot as true? If the parameter is true, how would the final layout have redundant view groups
and here is answar
suppose you have added already in your activity layout then you do not' need to make last parameter to true.because each time you perform add or remove of fragment their is a fragment transaction.
However if you have not added already a fragment in your layout then you can set it to true.
read this in doc
You can insert a fragment into your activity layout by declaring the fragment in the activity's layout file, as a element, or from your application code by adding it to an existing ViewGroup. However, a fragment is not required to be a part of the activity layout; you may also use a fragment without its own UI as an invisible worker for the activity.
all fragment transaction are added on activity back stack
i'm reading this android dev book and im stuck understanding how this line of code is error free (please keep in mind i've gotten rid of some code because for more focus on this part.
public View onCreateView(LayoutInflater layoutToInflate, ViewGroup parent, Bundle saveState)
{
View v = layoutToInflate.inflate(R.layout.activity_main_fragment,parent,false);
return v;
}
from what i believe, i need a method that returns a view because the Class extends from a Fragment class, not an activity so i have to explicitly find the view, the parameters are straight forward what i dont understand is how we create a view and set it equal to layoutToInflate...false;
I think you have a misunderstanding what the concepts of Fragments are. They reside inside an Activity. If a Fragment has a UI, it needs the parent Activity to also have a UI. That also means Fragments have a ViewParentbelonging to an Activity. This parent is given to the Fragment by the ViewGroup parent argument. So when creating a Fragment with a UI, you need to inflate the layout belonging to your Fragment and pass it to the Activity, which adds it to the ViewGroup parent. So that's why you get a LayoutInflater to inflate your Fragment's view:
View v = layoutToInflate.inflate(R.layout.activity_main_fragment,parent,false);
Afterwards you return it to give it to the parent Activity.
layoutToInflate is a variable of LayoutInflater and R.layout.activity_main_fragment is the name of the layout file to be inflated.
I am trying to save my View states in my fragment but I am concerned I make be leaking my Activity. Here is what I am doing:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state){
if(mView != null){
View oldParent = mView.getParent();
if(oldParent != container){
((ViewGroup)oldParent).removeView(mView);
}
return mView;
}
else{
mView = inflater.inflate(R.id.fragview, null)
return mView;
}
}
I am concerned because I know all Views hold onto a context and I don't know if it is the Activity context or Application context if inflated from the inflater. Perhaps it would be a better idea to pragmatically create the view and set its attributes using getActivity().getApplication() rather than use the inflater. I would appreciate any feedback on this.
Thanks!
EDIT: Confirmed Activity leak, although this code works great don't do it :*(
I am trying to save my View states in my fragment but I am concerned I make be leaking my Activity.
Use onSaveInstanceState() (in the fragment) and onRetainConfigurationInstance() (in the activity) for maintaining "View states" across configuration changes. I am not quite certain what other "View states" you might be referring to.
I am concerned because I know all Views hold onto a context and I don't know if it is the Activity context or Application context if inflated from the inflater.
Since using the Application for view inflation does not seem to work well, you should be inflating from the Activity. And, hence, the views will hold a reference to the Activity that inflated them.
#schwiz é have implemented something similar.
I use the setRetainInstance in the fragment. on the fragment layout I have a FrameLayout placeholder where I put my webView inside.
This webView is created programmatically on the Fragment's onCreate using the Application Context, and I do a addView(webView) on the inflated layout in onCreateView().
webView = new WebView(getActivity().getApplicationContext());
And on onDestroyView I simply remove the webView from my framelayout placeholder.
It work really well, unless you try to play a video in fullscreen. That doesn't work because it expects an Activity Context
I am trying to save my View states in my fragment
In order to save and retain view's state you can just use View.onSaveInstanceState () and View.onRestoreInstanceState (Parcelable state)
It will help you to handle saving and restoring view state independantly from neither activity or fragment.
See this answer for more information about it (how to prevent custom views from losing state across screen orientation changes)