Androidのコンテキストからアクティビティを取得する


184

これは私が困惑しています。

カスタムレイアウトクラス内からアクティビティメソッドを呼び出す必要があります。これの問題は、レイアウト内からアクティビティにアクセスする方法がわからないことです。

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

上記を見るとわかるように、私はプログラムでprofileViewをインスタンス化し、それとともにactivityContextを渡します。2つの質問:

  1. 正しいコンテキストをProfileviewに渡していますか?
  2. コンテキストから包含アクティビティを取得するにはどうすればよいですか?

回答:


473

から、レイアウトのとしてActivity渡してください:thisContext

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

その後Context、レイアウトにはが含まれますが、実際にそれが自分のものであることがわかり、Activity必要なものを配置できるようにキャストできます。

Activity activity = (Activity) context;

53
使用しているコンテキストがアクティビティコンテキストまたはアプリケーションコンテキストであることを保証することはできません。アプリケーションコンテキストをDialogViewに渡してみて、クラッシュすることを確認してください。違いがわかります。
スカイケルシー

6
ボリス、質問はコンテキストからアクティビティを取得する方法があるかどうかを尋ねます。これは不可能です。もちろんキャストできますが、それは最後の手段です。コンテキストをアクティビティとして扱いたい場合は、アクティビティにダウンキャストしないでください。これにより、コードが単純になり、後で他の人がコードを保守しているときにバグが発生しにくくなります。
スカイケルシー

6
「this」ではなく「getApplicationContext()」が機能しないことに注意してください。
dwbrito 2013年

1
@BorisStrandjev私はあなたのコメントを完全に理解していません。とにかく、あなたの例を試した後、「これ」の代わりにgetApplicationContext()を使用し、アプリケーションがアプリ自体をキャストしようとしたため、アクティビティではなくキャストエラーが発生したと言いました。「これ」に切り替えた後、あなたが答えたように、それはうまくいきました。
dwbrito 2013年

1
あなたのリンクで最も高い賛成投票の回答はどちらも、それが臭いなら質問に挑戦することを示唆しています。この質問は確かに臭いです。OPは最初に「カスタムレイアウトクラス内からアクティビティメソッドを呼び出す必要がある」と述べました。これは、インターフェースを適切に使用することで完全に達成できます。次に、「これの問題は、レイアウト内からアクティビティにアクセスする方法がわからないことです。」と彼は言います。これは誤解に対する重要なヒントです。人々はプログラミングにおいて常に間違ったことをしようとするので、私たちはそれに目を向けるべきではありません。
サム・

39

これは、フラグメントまたはカスタムビューで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階層を考慮していません。
Snicolas 2017

これが本当の答えです:)
lygstate 2018

1
@lygstate:アプリでどのターゲットAPIレベルを使用していますか?エラーは何ですか?これはUI(アクティビティ、フラグメントなど)内でのみ機能し、サービスでは機能しません。
Theo

31
  1. 番号
  2. できません

Androidには2つの異なるコンテキストがあります。アプリケーション用に1つ(BIGと呼びましょう)と、ビューごとに1つ(アクティビティコンテキストと呼びましょう)。

linearLayoutはビューなので、アクティビティコンテキストを呼び出す必要があります。アクティビティから呼び出すには、「this」を呼び出します。簡単ですね。

使うとき

this.getApplicationContext();

アプリケーションを記述し、ビューを管理できないBIGコンテキストを呼び出します。

Androidの大きな問題は、コンテキストがアクティビティを呼び出せないことです。これは、誰かがAndroid開発を始めるときにこれを回避するための大きな問題です。クラスをコーディングするより良い方法を見つける必要があります(または、「コンテキストコンテキスト」を「アクティビティアクティビティ」に置き換え、必要に応じて「コンテキスト」にキャストします)。

よろしく。


私の答えを更新するだけです。を取得する最も簡単な方法Activity contextは、でstaticインスタンスを定義することActivityです。例えば

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
+1は、2つの異なるタイプのコンテキスト間の混乱の非常に一般的な領域を説明します(2つの異なるRs があるように)。グーグルの人々は彼らの語彙を豊かにする必要があります。
an00b 2012

3
ところで、@ BorisStrandjevは正しいです:2.はい、できます。(動作中のコードについては議論できません)
an00b 2007

2
2.そうではない。コンテキストがアプリケーションコンテキストの場合、アプリはクラッシュします。
StackOverflowed 2013

静的インスタンス?!@Nepsterがこのimoの最良のソリューションを持っている
Sam

14
アクティビティへの静的参照を作成することは、メモリリークを作成する最良の方法です。
BladeCoder 2015年

8

カスタムレイアウトクラス(非アクティビティクラス)内からアクティビティメソッドを呼び出す場合は、インターフェイスを使用してデリゲートを作成する必要があります。

それはテストされておらず、私はそれを正しくコーディングしました。しかし、私はあなたが望むものを達成する方法を伝えています。

まず、作成とインターフェース

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

そして、これを任意のアクティビティに実装します。

そしてそれを

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

1
これは正解であり、正解としてマークする必要があります。正しいものとしてマークされた答えは、実際にはOPの質問に答えますが、そのような質問に答えるべきではありません。実際には、ビュー内でそのようなアクティビティを渡すことは良い習慣ではありません。子供は、を除いて、いかなる場合でも親について決して知っているべきではありませんContext。Nepsterが述べているように、ベストプラクティスはコールバックを渡すことです。そのため、親が関心を持つ何かが発生すると、コールバックは関連するデータで起動されます。
Darwind

6

コンテキストには、アプリケーション、サービス、アクティビティなどがあります。

通常、アクティビティ内のビューのコンテキストはアクティビティ自体なので、このコンテキストをアクティビティにキャストできると思うかもしれませんが、この場合はコンテキストがContextThemeWrapperになることもあるため、実際には常に実行できるとは限りません。

ContextThemeWrapperは、AppCompatおよびAndroidの最近のバージョンで頻繁に使用されているため(レイアウトのandroid:theme属性のおかげ)、私は個人的にこのキャストを実行することはありません。

つまり、ビューのコンテキストから確実にアクティビティを取得することはできません。Activityをパラメータとして受け取るメソッドを呼び出して、Activityをビューに渡します。


3

ビューでgetApplicationContext()を使用しないでください。

ビューはアクティビティに関連付けられているため、常にアクティビティのコンテキストである必要があります。また、カスタムテーマセットがあり、アプリケーションのコンテキストを使用すると、すべてのテーマが失われます。コンテキストのさまざまなバージョンについて詳しくは、こちらをご覧ください


3

そしてコトリンでは:

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

0

アクティビティはコンテキストの特殊化なので、コンテキストがある場合は、使用する予定のアクティビティがすでにわかっていて、単純にacにキャストできます。ここで、aはアクティビティで、cはコンテキストです。

Activity a = (Activity) c;

7
別のコメントで述べたように、コンテキストは常にアクティビティであるとは限らないため、これは危険です。

4
typecast if(context instanceof Activity){// typecast}
Amit Yadav '26

0

私は変換アクティビティを使用しました

Activity activity = (Activity) context;

2
コンテキストにはさまざまな種類があります。アクティビティとアプリケーションはコンテキストを持つことができます。これは、コンテキストがアクティビティの場合にのみ機能します。
cylov

0

この方法は役に立ちます。

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;

    }

これが役に立てば幸いです。


渡したコンテキストがnullではないことを確認してください。それが問題である可能性が高いです。
Taslim Oseni

0

ライブデータのコールバックはどうですか、

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

class ProfileActivity{

  override fun onCreateView(...){

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

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.