How to get hosting Activity from a view?
Asked Answered
P

9

215

I have an Activity with 3 EditTexts and a custom view which acts a specialised keyboard to add information into the EditTexts.

Currently I'm passing the Activity into the view so that I can get the currently focused edit text and update the contents from the custom keyboard.

Is there a way of referencing the parent activity and getting the currently focused EditText without passing the activity into the view?

Pacify answered 26/11, 2011 at 5:58 Comment(1)
The correct answer is by gomino.Vanmeter
D
342

I just pulled that source code from the MediaRouter in the official support library and so far it works fine:

private Activity getActivity() {
    Context context = getContext();
    while (context instanceof ContextWrapper) {
        if (context instanceof Activity) {
            return (Activity)context;
        }
        context = ((ContextWrapper)context).getBaseContext();
    }
    return null;
}
Darryl answered 6/10, 2015 at 15:4 Comment(8)
while? why while?Ayeshaayin
It's just a way to bubble up trough all the base context, till the activity is found, or exit the loop when the root context is found. Cause the root context will have a null baseContext, leading to the end of the loop.Darryl
Very good ! I replaced ((Activity) getContext()) with getActivity() and it works fine.... ThanksGillen
as it's been told, getContext() may not always represent an Activity object if your View is not called from an Activity context. E.g. it doesn't work for custom views.Saiff
@AbhinavSaxena Could you give us an example where this code would fail? Even though the method itself returns null it should never ever get there.Liaoyang
@AbhinavSaxena if you have a view hierarchy that is too deep, then yes, looping through all the base context come with a performance issue. But then you should probably better refactor your views a bit. This solution has the advantage to keep your code base clean (no spaghetti code referencing your application's child class everywhere!) Cheerz :)Darryl
A good article about where and how context can be used: possiblemobile.com/2013/06/context/?utm_source=Android Weekly&utm_campaign=78ad4cb95e-Android_Weekly_64&utm_medium=email&utm_term=0_4eb677ad19-78ad4cb95e-328260729Painless
Someone forgot to add to this code a comment that is in the original source code // Gross way of unwrapping the Activity so we can get the FragmentManager ;)Blackburn
P
175

following methods may help you

  1. Activity host = (Activity) view.getContext(); and
  2. view.isFocused()

You should first check if it is an Activity at minimum before using it:

if (view.getContext() instanceof Activity){
     Activity host = (Activity) view.getContext();
     //do something with host now
}
Prize answered 26/11, 2011 at 13:58 Comment(10)
Just don't forget that getContext() may not always return an Activity object if your View is not called from an Activity context. Make sure to plan this ahead and provide an appropriate fallback.Paletot
@WordPressDeveloper - How can a view be created without an activity? You mean remote view? Are there other cases of views that are created outside an Activity?Belated
@kilaka Widgets, Fragments, RemoteViews, LayoutInflaters are all cases where you may create a view that is not tied to an Activity.Paletot
@WordPressDeveloper - When you create a view in a fragment, it's context is still the activity. Fragments can reside only in Activities.Belated
@Belated Only situation I can think of is when a View is created to be attached directly to the System Window. In this situation an Application Context is used, not Activity Context.Steer
This is a pretty dangerous cast to make. There is a good chance (if you're using appcompat) that the context you've got is wrapped, casting something like a ContextThemeWrapper to Activity will throw a ClassCastException. You'd need a way to unwrap the base context (which would should be an Activity), which in itself is dangerous as there's a native and v7 version of ContextThemeWrapper.Godbey
this answer is very dangerous. i faced ClassCastException in Galaxy S3. i guess after GC. It does not happen in a phone with good specifications.Holierthanthou
Gomino's answer is definitely a better answer, as there are many cases when getContext() is not an activity.Dedradedric
@Godbey There is a good chance (if you're using appcompat) that the context you've got is wrapped, I've never seen this. Can you give an example?Chevron
This one is not the effective answer, as there are chances to get null as returned from the function he provides. My answer is universally applicable, though through some hard work and understanding, hence should be marked as answer : https://mcmap.net/q/125615/-how-to-get-hosting-activity-from-a-viewMammalogy
N
47

UPDATED

tailrec fun Context.getActivity(): Activity? = this as? Activity
    ?: (this as? ContextWrapper)?.baseContext?.getActivity()

thanks to @Westy92

Usage:

context.getActivity()
Naught answered 5/10, 2019 at 15:59 Comment(2)
thanks, really appreciate it for this nice scan activity on kotlinNavigation
You can also do: tailrec fun Context?.getActivity(): Activity? = this as? Activity ?: (this as? ContextWrapper)?.baseContext?.getActivity()Forklift
R
10

I took Gomino's answer and modified it to fit perfectly in myUtils.java so I can use it wherever and whenever I need. Hope someone finds it helpful :)

abstract class myUtils {
    public static Activity getActivity(View view) {
        Context context = view.getContext();
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                return (Activity)context;
            }
            context = ((ContextWrapper)context).getBaseContext();
        }
        return null;
    }
}
Rizika answered 31/1, 2018 at 15:40 Comment(1)
This is not the effective answer, as there are chances to get null as returned from this function. My answer is universally applicable, though through some hard work and understanding : https://mcmap.net/q/125615/-how-to-get-hosting-activity-from-a-viewMammalogy
H
2

Just want to note that, if you know that your view is inside a Fragment, you can just do:

FragmentManager.findFragment(this).getActivity();

Call this from your onAttachedToWindow or onDraw, you can't call it earlier (e.g. onFinishInflate) or you will get an exception.

If you are not sure your view is inside a Fragment then refer to other answers e.g. Gomino, just wanted to throw it out there.

Hungerford answered 22/6, 2021 at 21:27 Comment(0)
M
1

Kotlin answer based on @gomino

fun View?.getActivitySafely():Activity?{

    var context: Context = this!!.context
    while (context is ContextWrapper) {
        if (context is Activity) {
            return context
        }
        context = context.baseContext
    }
    return null
}
Militarist answered 28/3, 2023 at 14:17 Comment(0)
B
-1

Kotlin extension property for View to retrieve parent activity:

val View.activity: Activity?
get() {
    var ctx = context
    while (true) {
        if (!ContextWrapper::class.java.isInstance(ctx)) {
            return null
        }
        if (Activity::class.java.isInstance(ctx)) {
            return ctx as Activity
        }
        ctx = (ctx as ContextWrapper).baseContext
    }
}
Boulder answered 22/4, 2019 at 19:31 Comment(3)
You could replace the two if with when and the isInstance() with !is ContextWrapper or is ActivityNicolais
According to @Darryl the root context will have a null baseContext. So your implementation may throw a ClassCastException in this caseNicolais
This is old solution. Better to use solution by @NaughtBoulder
P
-3

In Android 7+ the view does not have access to the enclosing activity anymore, so view.getContext() can't be cast to an Activity anymore.

Instead, the code below works in Android 7+ and 6:

private static Activity getActivity(final View view) {
    return (Activity) view.findViewById(android.R.id.content).getContext();
}
Petaliferous answered 13/5, 2017 at 15:25 Comment(2)
"In Android 7+ the view does not have access to the enclosing activity anymore, so view.getContext() can't be cast to an Activity" Any reference?Monseigneur
@SimpleFellow as mentioned in other comments, getContext will probably return a ContextThemeWrapper so the View does not have direct access to the Activity anymore. Instead, you have to recursively search through parent contexts until you find the parent Activity or use the method I provided in this answer.Petaliferous
M
-4

@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if(request.getUrl().getHost().startsWith("pay.google.com")) { Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl()); view.getContext().startActivity(intent); return true; } ... ... }

Morrissey answered 25/2, 2020 at 9:10 Comment(1)
Hi, and welcome to Stack Overflow. please explain your answer more than just the code sample; look at other answers for example.Nitza

© 2022 - 2024 — McMap. All rights reserved.