What happens if onCreateView returns null instead of View object?
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
....
return null;
}
I tested the application and it seemed to work fine even with null.
My question is: Why does only this Lifecycle function needs to return a View while the others don't need to and just use void and where it gets used?
Related
I have a ConstraintLayout (the child) nested in another ConstraintLayout (the parent). I want to be able to call the child from within my Fragment class, but outside onCreateView. This what I have so far:
public class HomeFragment extends Fragment {
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
HomeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
View root = inflater.inflate(R.layout.fragment_home, container, false);
return root;
}
ConstraintLayout MyLayout = (ConstraintLayout) getView().findViewById(R.id.my_layout);
}
Which results in a NullPointerException:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference
I even tried declaring a global root variable in the Fragment class and assigning the inflated view result to it, but the problem persists.
I cannot place myLayout inside OnCreateView so I need a solution where I can use it outside of it.
Your issue stems from a misunderstanding about when and for how long a fragment's view exists.
Currently, you are assigning your MyLayout variable during construction of your fragment.
According to the Android documentation on a Fragment's lifecycle a fragment won't have a view associated with it until after onCreateView is called. Later on in the fragment's lifecycle, the view is destroyed when onDestroyView is called.
So, the fragment's view only lives during the intervening time between onCreateView and onDestroyView. If you call getView before onCreateView is called, or after onDestroyView is called, you will get null.
So, if you want to set listeners on views, do so either from onCreateView or onViewCreated and remove them in onDestroyView.
Also, if you want to hold onto your view via a member variable, set it in onCreateView and null it out in onDestroyView and any place you reference it, make sure to check for null first.
You can declare global variable for your view and initialize with a method.
public class HomeFragment extends Fragment {
private ConstraintLayout MyLayout;
private void init(View v) {
MyLayout = v.findViewById(R.id.my_layout);
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_home, container, false);
init(root);
return root;
}
}
Or you can declare your root and you can find your views with root variable:
public class HomeFragment extends Fragment {
private View root;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_home, container, false);
init()
return root;
}
private void init(){
ConstraintLayout MyLayout = root.findViewById(R.id.my_layout);
.
.
.
}
}
I can also suggest you ViewBinding. It simplifies the syntax.
https://medium.com/androiddevelopers/use-view-binding-to-replace-findviewbyid-c83942471fc
But definitely learn about basic Android lifecycle and reasons why you cannot access views before onCreateView.
How are these methods different from each other when trying to get the view?
First:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, container, false);
listView = view.findViewById(R.id.listview);
return view;
}
Second:
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
listView = getActivity().findViewById(R.id.listview); }
* some say this is used to get activity views but i used it for getting fragment views(which didn't existed in the activity) and it worked fine.
Third:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
listView = getView().findViewById(R.id.listview);
}
Three methods are good. In the onCreateView you create the view (!), it's the really first time you can use what you inflated. Then onViewCreated is called with the view you returned in the onCreateView, You can directly use the view given as parameter, it is the same the getView() provides. My advice is to initialise your UI variables here
For onActivityCreated, it is the best place to modify your UI elements. It is called when fragment creation is complete and when fragment is re-attached. There you can use the variables you initialised before, without having to get the activity just for that purpose.
in my Fragment.onCreateView(inflater), I keep a reference to a widge:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_shipping, container, false);
...
mMyBtn = (Button) view.findViewById(R.id.mybtn);
So I can use it later:
#Override
public void onResume() {
super.onResume();
...
mMyBtn.setEnabled(false);
Somehow, I get nullpointer in mMyBtn.setEnabled reported in Firebase Crashlytics occasionally (100% Android 8 devices).
Question: did I do it wrong in the wrong lifecycle callback? i.e., instead of keep the widget reference in onCreateView(...), should I do it in onViewCreated(...)? I have been doing this way forever wondering if I am out of dated ...
Any suggestion, to make my code more defensive?
Is it possible/recommanded to let different fragments inherit from each other in Android?
What would be the best way to initialize things that are already initialized in the superclass and add things to it ? (-> for example like the normal subclasses that use super() in their constructor and then initializing other objects )
I looked on the internet but i didn't found much information on this.
I know that it's possible to do return super.onCreateView() but you can't initialize other objects/views after that....
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
super.onCreateView()???
//initialize other objects here
//you have to return a view ...
}
Yes, it is allowed. Why not? For example, if you have a number of Fragments, that display lists, you could put all common methods in FragmentList, and then inherit other fragments, adding only unique methods or overriding the ones from super if needed.
But overriding onCreateView() could raise difficulties in layouts handling. In my recent project I instead created a method inflateFragment() in the super class as follows:
BaseFragment.java
protected View inflateFragment(int resId, LayoutInflater inflater, ViewGroup container) {
View view = inflater.inflate(resId, container, false);
FrameLayout layout = (FrameLayout)view.findViewById(R.id.fragment_layout);
/*
* Inflate shared layouts here
*/
. . .
setHasOptionsMenu(true);
return view;
}
Because of the structure, each and every fragment layout resource is wrapped in a FrameLayout with id = fragment_layout. But you're free to use LinearLayout or whatever parent view you need.
And then in inherited fragments:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflateFragment(R.layout.my_fragment, inflater, container);
/*
* Do things related to this fragment
*/
...
return view;
}
I am in trouble when trying to get the baseView(inflated in onCreateView and returned) in fragment, because I receive unexpected NoSaveStateFrameLayout.
I don't know why, can anyone explain?
Code:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LogUtil.d(TAG, "onCreateView ...");
View v = inflater.inflate(R.layout.gen_blank, container, false);
return v;
}
When I run this code in all life cycles:
LogUtil.v(TAG, "getview = "+getView());
It logs the same msg: getview = android.support.v4.app.NoSaveStateFrameLayout#41afdb70
The support library inserts an additional view into the view hierarchy. According to a comment in NoSaveStateFrameLayout.java: "Pre-Honeycomb versions of the platform don't have View.setSaveFromParentEnabled(), so instead we insert this between the view and its parent."
So you will need to either check for this with something like:
View myView = (NoSaveStateFrameLayout)getView();
if (myView != null) {
myView = myView.getChildAt(0);
}
Or keep track of the view returned by onCreateView in an instance variable.