【问题标题】:Getting activity from context in android从android中的上下文获取活动
【发布时间】:2012-04-11 02:12:24
【问题描述】:

这个让我难过。

我需要从自定义布局类中调用活动方法。这样做的问题是我不知道如何从布局中访问活动。

个人资料视图

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. 如何从上下文中获取包含的活动?

【问题讨论】:

    标签: android android-layout android-activity view hierarchy


    【解决方案1】:

    Theo 的solutionKotlin android 速记扩展版

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

    上面解释的用法here

    【讨论】:

      【解决方案2】:

      对于 kotlin 用户 -

      val activity = context as Activity
      

      【讨论】:

        【解决方案3】:

        创建一个扩展函数。并使用您的上下文调用此扩展函数,例如 context.getActivity()。

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

        【讨论】:

          【解决方案4】:

          一些实时数据回调怎么样,

          class ProfileView{
              private val _profileViewClicked = MutableLiveData<ProfileView>()
              val profileViewClicked: LiveData<ProfileView> = _profileViewClicked
          }
          
          class ProfileActivity{
          
            override fun onCreateView(...){
          
              profileViewClicked.observe(viewLifecycleOwner, Observer { 
                 activityMethod()
              })
            }
          
          }
          
          

          【讨论】:

            【解决方案5】:

            在 Kotlin 中:

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

            【讨论】:

              【解决方案6】:

              这个方法应该有用..!

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

              我希望这会有所帮助.. 编码愉快!

              【讨论】:

              • 检查你传入的上下文不为空。这很可能是问题所在。
              【解决方案7】:

              这是我在片段或自定义视图中的 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;
              }
              

              【讨论】:

              • 这是正确的答案。其他的不考虑 ContentWrapper 层次结构。
              • 这是真正的答案:)
              • @lygstate:您在应用中使用的目标 API 级别是什么?错误是什么?这仅适用于 UI(活动、片段等),不适用于服务。
              【解决方案8】:

              永远不要对视图使用 getApplicationContext()

              它应该始终是活动的上下文,因为视图附加到活动。此外,您可能有一个自定义主题集,并且在使用应用程序的上下文时,所有主题都将丢失。阅读更多关于不同版本的上下文here

              【讨论】:

                【解决方案9】:

                我使用了转换活动

                Activity activity = (Activity) context;
                

                【讨论】:

                • 有不同种类的上下文。活动和应用程序可以有上下文。这仅在上下文是活动时才有效。
                【解决方案10】:

                从您的Activity,只需将this 作为您的布局的Context 传递:

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

                之后你会在布局中有一个Context,但你会知道它实际上是你的Activity,你可以将它转换成你需要的东西:

                Activity activity = (Activity) context;
                

                【讨论】:

                • 您不能保证您正在使用的上下文是活动上下文或应用程序上下文。尝试将 Application Context 传递给 DialogView,观察它崩溃,您会看到差异。
                • Boris,问题询问是否有办法从上下文中获取活动。这不可能。当然你可以施放,但这是最后的手段。如果要将 Context 视为 Activity,则不要向下转换为 Activity。它使代码更简单,并且以后当其他人维护您的代码时不太容易出现错误。
                • 请注意,'getApplicationContext()' 而不是 'this' 将不起作用。
                • @BorisStrandjev 我不太明白你的评论。无论如何,我说在尝试了您的示例之后,我使用了 getApplicationContext() 而不是“this”,并且应用程序尝试投射应用程序本身,因此给出了投射错误,而不是活动。切换到“this”后,正如您回答的那样,它起作用了。
                • 您的链接上获得最高评价的答案都建议在问题是否有异味时提出质疑。这个问题当然很臭。 OP 首先声明:“我需要从自定义布局类中调用活动方法。”这完全可以通过适当使用接口来实现。然后他说“这个问题是我不知道如何从布局中访问活动。”这是对误解的重要暗示。人们在编程中总是试图做错事,我们不应该对此视而不见。
                【解决方案11】:

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

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

                ContextThemeWrapper 在最新版本的 AppCompat 和 Android 中被大量使用(感谢布局中的 android:theme 属性),所以我个人永远不会执行此转换。

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

                【讨论】:

                  【解决方案12】:

                  如果您想从自定义布局类(非活动类)中调用活动方法。您应该使用接口创建委托。

                  它未经测试,我编码正确。但我正在传达一种实现您想要的方法。

                  首先创建和接口

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

                  【讨论】:

                  • 这是正确答案,应该标记为正确答案。我知道标记为正确的答案实际上回答了 OP 的问题,但它不应该这样回答问题。事实是,像在视图中那样传递 Activity 并不是一个好习惯。孩子在任何情况下都不应该知道他们的父母,除非通过Context。正如 Nepster 所说,最好的做法是传入一个回调,因此每当发生父级感兴趣的事情时,都会使用相关数据触发回调。
                  【解决方案13】:
                  1. 没有
                  2. 你不能

                  Android 中有两种不同的上下文。一个用于您的应用程序(我们称之为 BIG 一个),一个用于每个视图(我们称之为活动上下文)。

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

                  当你使用

                  this.getApplicationContext();
                  

                  您调用 BIG 上下文,即描述您的应用程序但无法管理您的视图的上下文。

                  Android 的一个大问题是上下文无法调用您的活动。当有人开始进行 Android 开发时,避免这种情况是一件大事。您必须找到一种更好的方法来编写您的类(或将“上下文上下文”替换为“活动活动”并在需要时将其转换为“上下文”)。

                  问候。


                  只是为了更新我的答案。获取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
                  }
                  

                  【讨论】:

                  • +1 用于解释 2 种不同类型的上下文之间非常常见的混淆区域(就像有 2 种不同的 Rs)。谷歌的人需要丰富他们的词汇量。
                  • 顺便说一句,@BorisStrandjev 是正确的:2。是的,你可以。 (不能与工作代码争论)
                  • 2.并不真地。如果上下文是应用程序上下文,那么您的应用就会崩溃。
                  • 静态实例?! @Nepster 对此 imo 有最佳解决方案
                  • 创建对 Activity 的静态引用是造成内存泄漏的最佳方式。
                  【解决方案14】:

                  Activity 是 Context 的一种特殊化,因此,如果您有一个 Context,那么您已经知道要使用哪个 Activity,并且可以简单地将 a 转换为 c;其中 a 是 Activity,c 是 Context。

                  Activity a = (Activity) c;
                  

                  【讨论】:

                  • 这很危险,因为正如单独的评论中提到的,上下文可能并不总是一个活动。
                  • 仅在 (context instanceof Activity){ //typecast} 时进行类型转换
                  猜你喜欢
                  • 2016-11-24
                  • 2014-10-11
                  • 2020-12-24
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-04-12
                  • 2013-11-23
                  • 2019-02-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多