Behavior of Fragments in Android - android

I cannot find anything on this... I am wondering when I have a fragment within an activity why I can't call certain things such as getPackageName(), and getContentResolver()?

Call from a non static function inside the fragment, after it attaches to the activity.
getActivity().getPackageName();
getActivity().getContentResolver();
As your commenter stated, these are not fragment functions, you have to get the contextWrapper instance.

Because as per this documentation, the methods you mentioned are methods of the Context class. Activity is a subclass of Context, therefore it has these methods available. Fragment, however, is not. There are 2 ways to get hold of these methods from a fragment:
First option: After your fragment has been attached (it's onAttach() method has been called) you can use the getActivity() method to get the activity that it has been attached to. Then you can use getPackageName() and getContentResolver(). Such as this: getActivity().getPackageName(). To be extra safe, you might want to do:
Activity myActivity = getActivity();
if (myActivity != null)
{
myActivity.getPackageName();
myActivity.getContentResolver();
}
else
{
//deal with the null problem
}
Second option: In the onCreateView() method, your fragment won't yet be attached so you can't use the above method. You may use the LayoutInflater to get a View. Then call getContext() on the view. Such as this:
View myView = inflater.inflate(R.layout.my_fragment_layout, container, false);
myView.getContext().getPackageName();
myView.getContext().getContentResolver();

Related

onCreateView from Fragment not called

I am refactoring an Android App to use fragments. I have a fragment which I add to the layout with transaction.replace method but whose onCreateView method is not called. Code looks as follows:
FragmentManager fragmentManager = m_Activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
m_StartNewGameFragment = new StartNewGameToggleButtonsFragment();
fragmentTransaction.replace(R.id.bottom_pane, m_StartNewGameFragment);
fragmentTransaction.commit();
String winnerText = null;
if (isComputerWinner)
winnerText = m_Activity.getResources().getText(R.string.computer_winner).toString();
else
winnerText = m_Activity.getResources().getText(R.string.player_winner).toString();
m_StartNewGameFragment.updateStats(computerWins, playerWins, winnerText);
The method updateStats of fragment is as follows:
public void updateStats(int computer_wins, int player_wins, String winnerText) {
System.out.println("Update stats " + m_ComputerWins);
m_ComputerWins.setText(Integer.toString(computer_wins));
m_PlayerWins.setText(Integer.toString(player_wins));
m_WinnerTextView.setText(winnerText);
}
When updateStats is called m_ComputerWins is null and the program crashes. m_ComputerWins is initialized inside the onCreateView method of the fragment which seems not to be called.
Can anyone please help ?
Take global variables in your fragment class for computer_wins, player_wins, winnerText and init them inside updateStats method.
then inside onViewCreated() method, set values like
m_ComputerWins.setText(Integer.toString(computer_wins));
m_PlayerWins.setText(Integer.toString(player_wins));
m_WinnerTextView.setText(winnerText);
You never know when the fragments is created (which calls onCreate()), because fragment's life cycle differs from your activity's. In your code, you are trying to update fragment from activity. But in the way you have written your code, it is almost guaranteed that your fragment is not created yet.
Search for how to pass parameters to fragment or update fragment to find out the correct way of updating fragment from activity. You can find several related question, e.g. this one.
FragmentTransaction only runs on the next event loop. If you want to immediately run a fragment transaction, use fragmentTransaction.commitNowAllowingStateLoss();.
Please note that this won't actually work correctly after process death, which is why you should use the fragment.setArguments(Bundle method to pass initial argument values to a fragment.

onAttachFragment not being called when showing a dialog from fragment

I'm showing a BottomSheetDialogFragment from a Fragment. The idea is to get the callback of BottomSheetDialogFragment inside that Fragment instead of activity so I was hoping to receive the fragment inside
override fun onAttachFragment(childFragment: Fragment?) {
super.onAttachFragment(childFragment)
callback = childFragment as? Callback
}
This method is not being called. I tried using fragmentManager and childFragmentManager when showing the dialog to see if I can get onAttachFragment called but no luck.
AccountBottomSheetDialog dialog = AccountBottomSheetDialog.Companion.newFragment();
dialog.show(getChildFragmentManager(), AccountBottomSheetDialog.Companion.getTAG());
AccountBottomSheetDialog dialog = AccountBottomSheetDialog.Companion.newFragment();
dialog.show(getFragmentManager(), AccountBottomSheetDialog.Companion.getTAG());
Does anyone know how to get this called?
Thanks.
I think the reason it's not calling onAttachFragment is because DialogFragment control its life cycle and other method overrides differently than the normal ones, see documentation.
If you simply want to have a callback to the parent fragment, you can either override the onAttach method inside the DialogFragment and use the context parameter as the callback (cast it), or have a public method inside your DialogFragment that sets the callback, which you would call after creating that fragment.
onAttach method:
override fun onAttach(context: Context?) {
super.onAttach(context)
callback = context as? Callback
}
public callback setter method:
//parent fragment: after initializing it
childFragment.setCallback(this#ParentFragment)//or pass in other callbacks
//child fragment:
fun setCallback(callback: Callback) {
this.callback = callback
}
Maybe it a little bit late but now in 2019 with support library androidx.appcompat:appcompat:1.1.0 if using variant with child fragment manager then onAttachFragment called as I wait.
Here is correct fragment of code on Kotlin
ChildInfoDialog.getInstance(it.data).show(childFragmentManager, ChildInfoDialog.TAG)
Cannot answer your question directly but here is the workaround you could take:
Pass your parent fragment as newFragment's parameter
Set it on your child fragment as target fragment with any request code (setTargetFragment)
Use it in your code as getTargetFragment() and cast it to any interface you like (the interface your parent fragment implements of course).
PS.: for the above to work, fragment manager must be the same for parent and child.

Is there a difference between getContext of Fragment and getContext of container passed to onCreateView of Fragment?

public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
Say if we call
fragment.getContext() and container.getContext will we get same result, the host activity?
Also does view.getContext() always returns Activity context or can it return some other kind of context?
Activity class in android extends Context class. So basically an activity is a context. But a context may not be an activity.
fragment.getContext() will return context of the container it is attached to. So yes container.getContext() will have the same results as of fragment.getContext() ie., the host activity.
When instance of the ViewGroup is created, the inflater pass context of that activity to it. Means container.getContext() will return that same context.
fragment.getActivity() will return an activity it is attached with which is again a context. When the fragment is detached from the activity, it returns null. And when attached it also returns same as getContext()
And about your last question, view.getContext() returns context in which it is running. View doesnt extend Context class, infact when they are created, they require context object as parameter. So when you are creating a View inside a activty, you need to pass Context. And when you you call view.getContext(), you will get the same context back which you passed when you created it.

How can I know that onCreateView has been called from an outer class?

I'm really curious about how to determine (from an outer class) if a fragment's onCreateView() has already been called. I have searched for similar questions but found none.
For instance, is fragment.isAdded() a good indicator?
My first thought was simply fragment.getView() != null, but I'm not 100% sure it would be reliable as it seems, and I'm also slightly reluctant to use it (for no particular reason, I just tend to avoid nullity checks). I would be happy to find a workaround. Suggestions I had:
isAdded()
Return true if the fragment is currently added to its activity.
This line is quite ambiguous in my opinion; added is not attached, but neither created. It might refer to FragmentTransaction.add() (which is semantically wrong because you can have <fragment>s stuck in your layout without having to call add or replace).
Still, FragmentTransaction.add() documentation gives no info nor makes you think added -> created. I'd say no.
isVisible()
Return true if the fragment is currently visible to the user. This means it: (1) has been added, (2) has its view attached to the window, and (3) is not hidden.
Looks good, in the sense that isVisible() -> isCreated, but the third option makes it isCreated != isVisible. I just think of fragments inside a view pager: not all are visible, but the fragments near the currently visible fragment are added, created and alive, you can call methods on them. But for them, isVisible() == false. This is kind of too strict.
isInLayout()
Return true if the layout is included as part of an activity view
hierarchy via the < fragment> tag. This will always be true when
fragments are created through the < fragment> tag, except in the case
where an old fragment is restored from a previous state and it does
not appear in the layout of the current state.
I don't think this applies here.
getView() != null
Returns
The fragment's root view, or null if it has no layout.
This still looks the one and only solution. I'd just like a confirmation about that.
Implement a callback
..to be called onCreateView() or, better, onViewCreated(). But:
I don't need to call something as soon as the fragment is created (why would you need that?), I need something to check at a given time;
One should define the opposite, say, onViewNotAvailableAnymore(), to make the check meaningful at all times;
I don't see how this would be different, or better, than getView != null.
Does Fragment.isAdded() imply that onCreateView has been called?
NO!! NO!! pause NOOOOOOO00000000000!!!!!
SIr
Fragment.isAdded() is a notification that your Fragment has been added to your Activity, end of story.
The add() method in FragmentTransaction has 3 different methods, all adds Fragment to an Activity ,and, two goes further to create your Fragments View and attach it to a Parent ViewGroup by the aid of LayoutInflater provided your first parameter is not 0 (id != 0)
To check if onCreateView() has been called you need to override onViewCreated().
getView() will always return null unless onCreateView() is done
your solution is check Fragment.isVisible()
FYI: There is nothing wrong that i see with the documentation. Its pretty clear sir.
Hope i am lucid Sir
Assuming
you're not interested in whether the Fragment is visible
you want to know only if the onCreateView(...) method has been called by the Android framework
you need to use existing Fragment API methods to find out
then use
getView() != null
provided that you inflate the layout and return the View inside onCreateView(...) .
A non-Fragment-API approach is to add a callback in onCreate(...), which you then call in onCreateView() or later (in lifecycle) method.
It can be done using interface. make an interface OnCreateViewListerner
public interface OnViewCreatedListener
{
void onCreateCalled();
}
create a static object of OnViewCreatedListener in your Fragment and initialize it within in your Outer class and Outer class implement this interface like
public class CustomClass implements OnViewCreatedListener{
#Override
public void onCreateCalled() {
}
public void initializeInterface()
{
FragmentTest.listerner = this;
}
.....
}
then override your onCreateView() method and write this
public class FragmentTest extends Fragment{
public static OnViewCreatedListener listerner;
View mView;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// if (container == null)
if(listerner!= null)
{
listerner.onCreateCalled();
}
mView = inflater.inflate(R.layout.fragment_product, container, false);
return mView;
}
}
Hope this will help you.
Please consider this approach as I did same like this:
Define an Interface with in your Fragment say:
public Interface IDoSomething{
void intimateAfterOnCreateView();
}
Now, call this method with in onStart() of a fragment as according to life cycle this method will be called after the onCreateView().
Outside from this fragment just implement IDoSomething and you will get an overrided method(intimateAfterOnCreateView()) automatically.
Now this method's execution will show that onCreateView() has been called.
I just want to share my knowledge, may be it helps.
If isAdded() on a Fragment returns true, it doesn't mean that the onCreateView() has been called. In fact, isAdded returns true even during the onAttach callback, that is called before the onCreate().
I would go with extending the Fragment class and adding a public method that you can use to reach from outer of your custom Fragment Class.
When the onCreateView() is called, you can set a boolean value to true and according to your architecture, you can set it to false back again when it's in onPause() or onStop() or onDestroyView() or onDestroy() or onDetach(), up to you.
I don't think the methods you mentioned in your question will provide you exactly what you need.
How can I know that onCreateView has been called from an outer class?
You need to create interface inside your fragment and implement it in the container activity (let's say MainActivity).
1. First create an interface inside your fragment:
// Container activity must implement this interface
public interface OnCreateViewCalledListener {
void OnCreateViewCalled(String someData);
}
2. Next implement the interface inside your container activity (lets say it is MainActivity) and call it's method:
public class MainActivity extends AppCompatActivity implements
YourFragment.OnCreateViewCalledListener {
...
#Override
public void OnCreateViewCalled(String someData) {
Toast.makeText(MainActivity.this, "OnCreateView was called and passed " + someData)
}
3. Then you need to check if MainActivity implements interface callbacks (this step is crucial to make it work properly):
//Using onAttach method to check that activity has implemented callbacks
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Make sure that container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnCreateViewCalledListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnCreateViewCalledListener");
}
}
4. And finally you need to trigger the callback inside onCreateView:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mCallback.OnCreateViewCalled("Some useful data");
...
}
That's it!
EDIT:
To let other class know that onCreateView was called, please use the onCreateViewCalled() callback inside the MainActivity (e.g. use another interface to trigger callback in other class).
Also it is not mandatory to pass data into the OnCreateViewCalled()
Does Fragment.isAdded() imply that onCreateView has been called?
YES
isAdded() Return true if the fragment is currently added to its activity. (Implicitly onCreateView() has been called).

What does getActivity() mean?

What does getActivity() mean? I saw in somewhere, they wrote MainActivity.this.startActionMode(mActionModeCallback) instead of getActivity(). could someone explain what this two lines mean?
someView.setOnLongClickListener(new View.OnLongClickListener() {
// Called when the user long-clicks on someView
public boolean onLongClick(View view) {
if (mActionMode != null) {
return false;
}
// Start the CAB using the ActionMode.Callback defined above
mActionMode = getActivity().startActionMode(mActionModeCallback);
view.setSelected(true);
return true;
}
});
Two likely definitions:
getActivity() in a Fragment returns the Activity the Fragment is currently associated with. (see http://developer.android.com/reference/android/app/Fragment.html#getActivity()).
getActivity() is user-defined.
getActivity() is used for fragment. For activity, wherever you can use this, you can replace the this in fragment in similar cases with getActivity().
getActivity()- Return the Activity this fragment is currently associated with.
I to had a similar doubt what I got to know was getActivity() returns the Activity to which the fragment is associated.
The getActivity() method is used generally in static fragment as the associated activity will not be static and non static member cannot be used in static member.

Categories

Resources