Getting activity from context in android

AndroidAndroid LayoutAndroid ActivityViewHierarchy

Android Problem Overview


This one has me stumped.

I need to call an activity method from within a custom layout class. The problem with this is that I don't know how to access the activity from within the layout.

ProfileView

public class ProfileView extends LinearLayout
{
    TextView profileTitleTextView;
    ImageView profileScreenImageButton;
    boolean isEmpty;
    ProfileData data;
    String name;

    public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
    {
        super(context, attrs);
        ......
        ......
    }

    //Heres where things get complicated
    public void onClick(View v)
    {
        //Need to get the parent activity and call its method.
        ProfileActivity x = (ProfileActivity) context;
        x.activityMethod();
    }
}

##ProfileActivity##

public class ProfileActivityActivity extends Activity
{
    //In here I am creating multiple ProfileViews and adding them to the activity dynamically.
 
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.profile_activity_main);
    }

    public void addProfilesToThisView()
    {
        ProfileData tempPd = new tempPd(.....)
        Context actvitiyContext = this.getApplicationContext();
        //Profile view needs context, null, name and a profileData
        ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
        profileLayout.addView(pv);
    }
}

As you can see above, I am instantiating the profileView programatically and passing in the activityContext with it. 2 questions:

  1. Am i passing the correct context into the Profileview?
  2. How do I get the containing activity from the context?

Android Solutions


Solution 1 - Android

From your Activity, just pass in this as the Context for your layout:

ProfileView pv = new ProfileView(this, null, temp, tempPd);

Afterwards you will have a Context in the layout, but you will know it is actually your Activity and you can cast it so that you have what you need:

Activity activity = (Activity) context;

Solution 2 - Android

This is something that I have used successfully to convert Context to Activity when operating within the UI in fragments or custom views. It will unpack ContextWrapper recursively or return null if it fails.

public Activity getActivity(Context context)
{
	if (context == null)
	{
		return null;
	}
	else if (context instanceof ContextWrapper)
	{
		if (context instanceof Activity)
		{
			return (Activity) context;
		}
		else
		{
			return getActivity(((ContextWrapper) context).getBaseContext());
		}
	}

	return null;
}

Solution 3 - Android

  1. No
  2. You can't

There are two different contexts in Android. One for your application (Let's call it the BIG one) and one for each view (let's call it the activity context).

A linearLayout is a view, so you have to call the activity context. To call it from an activity, simply call "this". So easy isn't it?

When you use

this.getApplicationContext();

You call the BIG context, the one that describes your application and cannot manage your view.

A big problem with Android is that a context cannot call your activity. That's a big deal to avoid this when someone begins with the Android development. You have to find a better way to code your class (or replace "Context context" by "Activity activity" and cast it to "Context" when needed).

Regards.


Just to update my answer. The easiest way to get your Activity context is to define a static instance in your Activity. For example

public class DummyActivity extends Activity
{
	public static DummyActivity instance = null;

	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
	
		// Do some operations here
	}
	
	@Override
	public void onResume()
	{
		super.onResume();
		instance = this;
	}
	
	@Override
	public void onPause()
	{
		super.onPause();
		instance = null;
	}
}

And then, in your Task, Dialog, View, you could use that kind of code to get your Activity context:

if (DummyActivity.instance != null)
{
	// Do your operations with DummyActivity.instance
}

Solution 4 - Android

>If you like to call an activity method from within a custom layout class(non-Activity Class).You should create a delegate using interface.

It is untested and i coded it right . but i am conveying a way to achieve what you want.

First of all create and Interface

interface TaskCompleteListener<T> {
   public void onProfileClicked(T result);
}



public class ProfileView extends LinearLayout
{
    private TaskCompleteListener<String> callback;
    TextView profileTitleTextView;
    ImageView profileScreenImageButton;
    boolean isEmpty;
    ProfileData data;
    String name;

    public ProfileView(Context context, AttributeSet attrs, String name, final ProfileData profileData)
    {
        super(context, attrs);
        ......
        ......
    }
    public setCallBack( TaskCompleteListener<String> cb) 
    {
      this.callback = cb;
    }
    //Heres where things get complicated
    public void onClick(View v)
    {
        callback.onProfileClicked("Pass your result or any type");
    }
}

And implement this to any Activity.

and call it like

ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
pv.setCallBack(new TaskCompleteListener
               {
                   public void onProfileClicked(String resultStringFromProfileView){}
               });

Solution 5 - Android

And in Kotlin:

tailrec fun Context.activity(): Activity? = when {
  this is Activity -> this
  else -> (this as? ContextWrapper)?.baseContext?.activity()
}

Solution 6 - Android

Context may be an Application, a Service, an Activity, and more.

Normally the context of Views in an Activity is the Activity itself so you may think you can just cast this Context to Activity but actually you can't always do it, because the context can also be a ContextThemeWrapper in this case.

ContextThemeWrapper is used heavily in the recent versions of AppCompat and Android (thanks to the android:theme attribute in layouts) so I would personally never perform this cast.

So short answer is: you can't reliably retrieve an Activity from a Context in a View. Pass the Activity to the view by calling a method on it which takes the Activity as parameter.

Solution 7 - Android

Never ever use getApplicationContext() with views.

It should always be activity's context, as the view is attached to activity. Also, you may have a custom theme set, and when using application's context, all theming will be lost. Read more about different versions of contexts here.

Solution 8 - Android

I used convert Activity

Activity activity = (Activity) context;

Solution 9 - Android

For kotlin user -

val activity = context as Activity

Solution 10 - Android

an Activity is a specialization of Context so, if you have a Context you already know which activity you intend to use and can simply cast a into c; where a is an Activity and c is a Context.

Activity a = (Activity) c;

Solution 11 - Android

This method should be helpful..!

public Activity getActivityByContext(Context context){

if(context == null){
    return null;
    }

else if((context instanceof ContextWrapper) && (context instanceof Activity)){
        return (Activity) context;
    }

else if(context instanceof ContextWrapper){
        return getActivity(((ContextWrapper) context).getBaseContext());
    }

return null;

    }

I hope this helps.. Merry coding!

Solution 12 - Android

how about some live data callback,

class ProfileView{
    private val _profileViewClicked = MutableLiveData<ProfileView>()
    val profileViewClicked: LiveData<ProfileView> = _profileViewClicked
}

class ProfileActivity{
 
  override fun onCreateView(...){

    profileViewClicked.observe(viewLifecycleOwner, Observer { 
       activityMethod()
    })
  }

}

Solution 13 - Android

Create an extension function. And call this extension function with your context like context.getActivity().

fun Context.getActivity(): AppCompatActivity? {
      var currentContext = this
      while (currentContext is ContextWrapper) {
           if (currentContext is AppCompatActivity) {
                return currentContext
           }
           currentContext = currentContext.baseContext
      }
      return null
}

Solution 14 - Android

Kotlin android shorthand extension version of Theo's solution

private fun Context?.getParentActivity() : AppCompatActivity? = when {
    this is ContextWrapper -> if (this is AppCompatActivity) this else this.baseContext.getParentActivity()
    else -> null
}

Usage of above explained here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOVERTONEView Question on Stackoverflow
Solution 1 - AndroidBoris StrandjevView Answer on Stackoverflow
Solution 2 - AndroidTheoView Answer on Stackoverflow
Solution 3 - AndroidManitobaView Answer on Stackoverflow
Solution 4 - AndroidZar E AhmerView Answer on Stackoverflow
Solution 5 - AndroidrjrjrView Answer on Stackoverflow
Solution 6 - AndroidBladeCoderView Answer on Stackoverflow
Solution 7 - AndroidlomzaView Answer on Stackoverflow
Solution 8 - AndroidSamuel IvanView Answer on Stackoverflow
Solution 9 - AndroidhasanView Answer on Stackoverflow
Solution 10 - AndroidACLimaView Answer on Stackoverflow
Solution 11 - AndroidTaslim OseniView Answer on Stackoverflow
Solution 12 - AndroidAbhinav AtulView Answer on Stackoverflow
Solution 13 - AndroidRajeev ShettyView Answer on Stackoverflow
Solution 14 - AndroidMDTView Answer on Stackoverflow