212

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?

mAndroid
  • 5,087
  • 6
  • 25
  • 33

9 Answers9

340

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;
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Gomino
  • 12,127
  • 4
  • 40
  • 49
  • 18
    while? why while? – Jakob Eriksson May 05 '16 at 17:41
  • 19
    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. – Gomino May 05 '16 at 18:19
  • 1
    Very good ! I replaced ((Activity) getContext()) with getActivity() and it works fine.... Thanks – Christian Mar 08 '17 at 20:50
  • 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. – Tohid Aug 03 '17 at 20:17
  • @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. – Bitcoin Cash - ADA enthusiast Jan 13 '19 at 21:39
  • @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 :) – Gomino Jan 15 '19 at 16:01
  • A good article about where and how context can be used: https://possiblemobile.com/2013/06/context/?utm_source=Android Weekly&utm_campaign=78ad4cb95e-Android_Weekly_64&utm_medium=email&utm_term=0_4eb677ad19-78ad4cb95e-328260729 – StepanM Jun 10 '19 at 11:47
  • 2
    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` ;) – Mieszko Jun 30 '20 at 22:54
174

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
}
pigeonhands
  • 3,066
  • 15
  • 26
dira
  • 30,304
  • 14
  • 54
  • 69
  • 39
    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. – Dzhuneyt Sep 20 '13 at 15:19
  • 1
    @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? – AlikElzin-kilaka Mar 23 '14 at 14:02
  • 1
    @kilaka Widgets, Fragments, RemoteViews, LayoutInflaters are all cases where you may create a view that is not tied to an Activity. – Dzhuneyt Mar 24 '14 at 08:07
  • 4
    @WordPressDeveloper - When you create a view in a fragment, it's context is still the activity. Fragments can reside only in Activities. – AlikElzin-kilaka Mar 24 '14 at 10:59
  • @AlikElzin-kilaka 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. – Jug6ernaut Jul 03 '15 at 19:33
  • 26
    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`. – alex Jul 10 '15 at 04:19
  • 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. – 최봉재 Apr 17 '17 at 06:05
  • Gomino's answer is definitely a better answer, as there are many cases when `getContext()` is *not* an activity. – David Berry Jul 27 '17 at 22:23
  • @alex `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? – ono Nov 01 '17 at 04:24
  • 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://www.stackoverflow.com/a/51077569/787399 – Abhinav Saxena Jun 29 '18 at 08:15
45

UPDATED

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

thanks to @Westy92

Usage:

context.getActivity()
Vlad
  • 7,997
  • 3
  • 56
  • 43
  • 2
    thanks, really appreciate it for this nice scan activity on kotlin – mochadwi Mar 19 '20 at 09:09
  • 6
    You can also do: `tailrec fun Context?.getActivity(): Activity? = this as? Activity ?: (this as? ContextWrapper)?.baseContext?.getActivity()` – Westy92 Sep 16 '20 at 16:22
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;
    }
}
brownieGirl
  • 101
  • 1
  • 2
  • 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://stackoverflow.com/a/51077569/787399 – Abhinav Saxena Jun 29 '18 at 08:13
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.

Adam Burley
  • 5,551
  • 4
  • 51
  • 72
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
}
Mike6679
  • 5,547
  • 19
  • 63
  • 108
-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
    }
}
Fedir Tsapana
  • 1,283
  • 16
  • 19
-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();
}
Sebas LG
  • 1,715
  • 1
  • 18
  • 31
  • 8
    **"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? – Simple Fellow May 08 '18 at 18:12
  • @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. – Sebas LG Oct 19 '19 at 11:53
-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; } ... ... }

  • 2
    Hi, and welcome to Stack Overflow. please explain your answer more than just the code sample; look at other answers for example. – Itamar Mushkin Feb 25 '20 at 09:19