在安卓中从上下文获取活动信息

217

这个问题让我束手无策。

我需要从自定义布局类中调用一个活动方法。问题在于我不知道如何从布局内部访问活动。

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();
    }
}

个人资料活动

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);
    }
}

正如您在上面看到的,我正在以编程方式实例化profileView,并将activityContext与其一起传递。2个问题:

  1. 我是否将正确的上下文传递到Profileview中?
  2. 如何从上下文中获取包含的Activity?
15个回答

506

在您的Activity中,只需将this作为布局的Context传递即可:

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

之后您将在布局中拥有一个 Context,但您将知道它实际上是您的Activity,您可以将其转换为所需的类型:

Activity activity = (Activity) context;

72
无法保证你正在使用的上下文是Activity Context还是Application Context。尝试向DialogView传递Application Context,就会导致崩溃,从而显现出它们之间的区别。 - Sky Kelsey
9
这段话的意思是:Boris,这个问题问是否有一种方法可以从上下文中获取活动。这是不可能的。当然你可以强制转换,但那是最后的手段。如果你想将上下文视为活动,则不要向下转换为活动。这样代码更简单,并且在另一个人维护代码时,出错的风险更小。 - Sky Kelsey
6
请注意,使用“getApplicationContext()”而不是“this”将无法正常工作。 - dwbrito
1
你链接中得到的最高赞答案都建议质疑问题是否有问题。这个问题确实有问题。OP首先说:“我需要从自定义布局类中调用活动方法。”这完全可以通过适当使用接口来实现。然后他说:“问题在于我不知道如何从布局中访问活动。”这是一个重要的误解提示。人们在编程中经常会做错事情,我们不应该对此视而不见。 - Sam
1
这个答案对我没有用。它甚至不是无用的,因为另一个开发人员已经使用了这种模式提交了代码,由于时间限制,这些代码可能不会很快被撤销。所以...拒绝这个答案 :P - Sam
显示剩余17条评论

58

这是我在片段或自定义视图中在 UI 内操作时成功地将 Context 转换为 Activity 所使用的内容。它会递归地解包 ContextWrapper,如果失败则返回 null。

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;
}

1
这是正确的答案。其他答案没有考虑到ContentWrapper层级结构。 - Snicolas
这是正确的答案:) - lygstate
1
@lygstate:你的应用程序使用哪个目标API级别?错误是什么?这仅适用于UI(活动,片段等),而不适用于服务。 - Theo

34
  1. 不可以
  2. 你无法这么做

在Android中有两个不同的上下文。一个是针对应用程序的(我们称之为BIG),另一个是针对每个视图的(我们称之为活动上下文)。

LinearLayout是一个视图,因此必须调用活动上下文。要从活动中调用它,只需调用“this”。如此简单,不是吗?

当你使用

this.getApplicationContext();

您调用的是描述您的应用程序并且无法管理您的视图的BIG上下文。

Android存在一个大问题,即上下文无法调用您的活动。这对于初学Android开发的人来说是一个很大的问题。您必须找到一种更好的方式来编写您的类(或者将“Context context”替换为“Activity activity”并在需要时进行强制类型转换为“Context”)。

祝好。


仅更新我的答案。获取您的Activity context最简单的方法是在Activity中定义一个static实例。例如:

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;
    }
}

然后,在你的TaskDialogView中,你可以使用以下代码来获取Activity context:

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

4
感谢您解释这两种不同语境中常见的混淆点(就像有两个不同的“R”一样),给您加1个赞。Google的人需要丰富他们的词汇量。 - an00b
3
顺便说一句,@BorisStrandjev是正确的:2. 可以。(不能反驳有效的代码) - an00b
2
不完全是。如果上下文是应用程序上下文,那么您的应用程序将崩溃。 - StackOverflowed
29
创建一个静态引用(Activity)是导致内存泄漏的最佳方式。 - BladeCoder
1
静态活动引用不正确,活动不是单例。 - Miha_x64
显示剩余2条评论

15

在 Kotlin 中:

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

嘿 @rjrjr,在这里 tailrec 的作用是什么? - Rax

9

如果您想从自定义布局类(非Activity类)中调用Activity方法,您应该使用接口创建一个代理。

此方法未经测试,但我正在传达一种实现您想要的方式。

首先创建一个接口:

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");
    }
}

并将其实现到任何Activity中。

并像这样调用它

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

1
这是正确的答案,应该标记为正确答案。我知道标记为正确的答案实际上回答了OP的问题,但不应该那样回答问题。事实是,在视图中像那样传递Activity并不是好的做法。在任何情况下,子项都不应该知道其父项,除非通过“Context”。正如Nepster所说,最佳实践是传递回调,因此每当发生对父项感兴趣的事件时,回调将使用相关数据触发。 - Darwind

7

上下文可能是应用程序、服务、活动等。

通常,活动中视图的上下文是活动本身,因此您可以将此上下文转换为活动,但实际上并不总是这样,因为在这种情况下上下文也可以是ContextThemeWrapper。

ContextThemeWrapper在AppCompat和Android的最新版本中被广泛使用(由于布局中的android:theme属性),因此我个人永远不会执行此转换。

所以简短的答案是:您无法可靠地从视图中的上下文中检索活动。通过调用一个以活动作为参数的方法将活动传递给视图。


4

永远不要在视图中使用getApplicationContext()

它应该始终是活动的上下文,因为视图附加到活动中。此外,您可能设置了自定义主题,当使用应用程序的上下文时,所有主题都将丢失。在这里阅读有关不同版本上下文的更多信息。


2
我使用了转换活动。
Activity activity = (Activity) context;

2
有不同类型的上下文。活动和应用程序可以具有上下文。只有当上下文是活动的时候,这才有效。 - cylov

2
对于 Kotlin 用户 -
val activity = context as Activity

如果上下文是ContextWrapper,这将会崩溃。 - ravindu1024

1

Activity是Context的一个特化,因此,如果您有一个Context,您已经知道要使用哪个Activity,只需将a强制转换为c即可;其中a是一个Activity,c是一个Context。

Activity a = (Activity) c;

8
由于上一个评论中提到的原因,这是危险的,因为上下文可能并不总是指一个活动。 - user153275
6
只有在上下文是Activity类型时才进行类型转换。//进行类型转换 - Amit Yadav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接