How to check if context instance of a Fragment? - android

I'm trying to check if context is a instance of my Fragment when attaching it. It works with activities, how come it doesn't work with fragments?
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
if(context instanceof MailFragment){
}
}
Doesn't work, but it works with context instanceof MainActivity
How do I check if context is instance of Fragment?

You can't check context instanceof Fragment, because Fragment is not a context. but activities are.
And fragments need a context like an activity to attach them to be displayed.
And onAttach() occurs when a fragment attaches to a context.

Related

I get Fragment not attached to a context. What context need to use?

I get the Exception from Firebase Crashlytics
Fatal Exception: java.lang.IllegalStateException: Fragment MyFragment{122418b (05b123e6-aa8d-4de4-8f7e-49c95018234b)} not attached to a context.
at androidx.fragment.app.Fragment.requireContext(Fragment.java:774)
at androidx.fragment.app.Fragment.getResources(Fragment.java:838)
at com.timskiy.pregnancy.fragments.MyFragment$1$1.run(MyFragment.java:156)
at android.os.Handler.handleCallback(Handler.java:907)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:216)
at android.app.ActivityThread.main(ActivityThread.java:7625)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)
Error line from fragment
imageView.setColorFilter(ContextCompat.getColor(getContext(), R.color.blue));
also tried
imageView.setColorFilter(getResources().getColor(R.color.blue));
I use viewPager in Activity and FragmentStatePagerAdapter. What context I need to use in fragment to setColorFilter? Thx
Add this in your fragment:
private Context mContext;
#Override
public void onAttach(Context context) {
super.onAttach(activity);
mContext = context;
}
#Override
public void onDetach() {
super.onDetach();
mContext = null;
}
And in your image view
imageView.setColorFilter(ContextCompat.getColor(mContext, R.color.blue));
You are getting this crash because you are trying to call getContext() from a fragment that has already been detached from the parent Activity.
From the stacktrace it appears that there is a call to MyFragment.java line 156 from a Handler, this leads me to assume that its some background work happening but its getting completed when the fragment has been detached.
A quick fix for this would be to check if the fragment is attached to activity before attempting to execute any line of code that modifies the view.
if (isAttachedToActivity()){
imageView.setColorFilter(ContextCompat.getColor(getContext(), R.color.blue));
}
or
if (isAttachedToActivity()){
imageView.setColorFilter(getResources().getColor(R.color.blue));
}
The isAttachedToActivity() looks like this:
public boolean isAttachedToActivity() {
boolean attached = isVisible() && getActivity() != null;
return attached;
}
Try to use application context to fetch app resources to prevent IllegalStateException (not attached to a context)
// Init global variable with the application context first:
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
if (appContext == null)
appContext = context.getApplicationContext();
}
Then use appContext var anywhere you want to get app resources ex:
imageView.setColorFilter(ContextCompat.getColor(appContext, R.color.blue));
Probably you're trying to reach ViewPager's child Fragmnet's components, when the fragment is not created yet, or it's already destroyed. Could you make your post more detailed?
In your fragment it is safe to use requireContext() / requireActivity() inside onViewCreated instead of getContext() / getActivity().
imageView.setColorFilter(ContextCompat.getColor(requireContext(), R.color.blue));

What kind of Context onAttach gets

Say me pls what kind of context onAttach gets in a Fragment? Activity or application?
#Override
public void onAttach(Context context) {
super.onAttach(context);
}
you get Activity context to which the fragment is attached to.
You can check it by isInstance in Java
The context you get in onAttach is the activity that contains the fragment

onAttach method for being attached to a parent fragment

I have a fragment that can either be attached to an Activity or a parent fragment. This fragment has an interface that must be implemented by anyone it is attached to. For activities, this is quite simple:
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof Activity){
Activity activity =(Activity) context;
try {
mCallback = (OnMyListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() +
" must implement OnMyListener");
}
}
}
However, I am unable to set the mCallback listener for other Fragments that are hosting this particular Fragment.
You can't directly communicate between 2 fragments, it has to go thorough the activity hosting it (and I saw you already implemented the first half).
After the activity received the data from sender fragment, you can send it to the receiver fragment by resolving the receiver fragment's reference first, using:
ReceiverFragment fragment = ( ReceiverFragment) getSupportFragmentManager().findFragmentById(R.id.receiver_fragment_id);
if it's null then you need to instantiate it first and pass the data using fragment.setArguments(Bundle), otherwise you can directly call the member function of the receiver fragment.
Check: https://developer.android.com/training/basics/fragments/communicating.html

Android Fragment onAttach() deprecated

I have updated my app to use the latest support library (version 23.0.0), I've found out that they deprecated the onAttach() function of the Fragment class.
Instead of:
onAttach (Activity activity)
It's now:
onAttach (Context context)
As my app uses the activity passed before deprecation, a possible solution i think is:
#Override
public void onAttach(Context context) {
super.onAttach(context);
activity = getActivity();
}
Would that be the correct way to do it?
UPDATE:
If i run a device with API lower than 23, the new onAttach() is not even being called. I hope that this is not what they intended to do!
UPDATE 2:
Issue has been resolved with the latest updates to the SDK.
I have tested on my API 22 device and onAttach(Context) is being called.
Click here to follow the bug report I've opened a couple of weeks ago and the answers from the guys at Google.
Activity is a context so if you can simply check the context is an Activity and cast it if necessary.
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity a;
if (context instanceof Activity){
a=(Activity) context;
}
}
Update: Some are claiming that the new Context override is never called. I have done some tests and cannot find a scenario where this is true and according to the source code, it should never be true. In all cases I tested, both pre and post SDK23, both the Activity and the Context versions of onAttach were called. If you can find a scenario where this is not the case, I would suggest you create a sample project illustrating the issue and report it to the Android team.
Update 2: I only ever use the Android Support Library fragments as bugs get fixed faster there. It seems the above issue where the overrides do not get called correctly only comes to light if you use the framework fragments.
This is another great change from Google ... The suggested modification: replace onAttach(Activity activity) with onAttach(Context context) crashed my apps on older APIs since onAttach(Context context) will not be called on native fragments.
I am using the native fragments (android.app.Fragment) so I had to do the following to make it work again on older APIs (< 23).
Here is what I did:
#Override
public void onAttach(Context context) {
super.onAttach(context);
// Code here
}
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Code here
}
}
If you use the the framework fragments and the SDK version of the device is lower than 23, OnAttach(Context context) wouldn't be called.
I use support fragments instead, so deprecation is fixed and onAttach(Context context) always gets called.
Currently from the onAttach Fragment code, it is not clear if the Context is the current activity: Source Code
public void onAttach(Context context) {
mCalled = true;
final Activity hostActivity = mHost == null ? null : mHost.getActivity();
if (hostActivity != null) {
mCalled = false;
onAttach(hostActivity);
}
}
If you will take a look at getActivity you will see the same call
/**
* Return the Activity this fragment is currently associated with.
*/
final public Activity getActivity() {
return mHost == null ? null : mHost.getActivity();
}
So If you want to be sure that you are getting the Activity then use getActivity() (in onAttach in your Fragment) but don't forget to check for null because if mHost is null your activity will be null
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity activity = context instanceof Activity ? (Activity) context : null;
}
Although it seems that in most cases it's enough to have onAttach(Context), there are some phones (i.e: Xiaomi Redme Note 2) where it's not being called, thus it causes NullPointerExceptions. So to be on the safe side I suggest to leave the deprecated method as well:
// onAttach(Activity) is necessary in some Xiaomi phones
#SuppressWarnings("deprecation")
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
_onAttach(activity);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
_onAttach(context);
}
private void _onAttach(Context context) {
// do your real stuff here
}
Download newest Support library with the sdk manager and include
compile 'com.android.support:appcompat-v7:23.1.1'
in gradle.app and set compile version to api 23
The answer below is related to this deprecation warning occurring in the Fragments tutorial on the Android developer website and may not be related to the posts above.
I used this code on the tutorial lesson and it did worked.
public void onAttach(Context context){
super.onAttach(context);
Activity activity = getActivity();
I was worried that activity maybe null as what the documentation states.
getActivity
FragmentActivity getActivity ()
Return the FragmentActivity this fragment is currently associated with. May return null if the fragment is associated with a Context instead.
But the onCreate on the main_activity clearly shows that the fragment was loaded and so after this method, calling get activity from the fragment will return the main_activity class.
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
I hope I am correct with this. I am an absolute newbie.
you are probably using android.support.v4.app.Fragment. For this instead of onAttach method, just use getActivity() to get the FragmentActivity with which the fragment is associated with. Else you could use onAttach(Context context) method.
This worked for me when i have userdefined Interface 'TopSectionListener', its object activitycommander:
//This method gets called whenever we attach fragment to the activity
#Override
public void onAttach(Context context) {
super.onAttach(context);
Activity a=getActivity();
try {
if(context instanceof Activity)
this.activitycommander=(TopSectionListener)a;
}catch (ClassCastException e){
throw new ClassCastException(a.toString());}
}

Save Activity instance in a Fragment

Fragment has a method named getActivity() which returns the activity with which the fragment currently is associated.
Is it safe to not use this method, but instead save the Activity instance in the onAttach(Activity) method?
For example, change from:
public class MyFragment extends Fragment {
public void foo() {
((MainActivity) getActivity()).foo();
}
}
to:
public class MyFragment extends Fragment {
private MainActivity activity;
public void onAttach(Activity activity) {
this.activity = (MainActivity) activity;
}
public void foo() {
this.activity.foo();
}
}
Are there any differences between these two approaches? Which is better?
PS. One benefit of the second approach is that you don't have to do type conversion each time you use the activity (like (MainActivity) getActivity()). But I don't know whether it's safe to save the activity instance.
yes it's ok. I do that almost always to avoid calling method getActivity() and casting result every time (for better performance and better code)

Categories

Resources