Aktivität aus dem Kontext in Android erhalten

182

Dieser hat mich verblüfft.

Ich muss eine Aktivitätsmethode aus einer benutzerdefinierten Layoutklasse heraus aufrufen. Das Problem dabei ist, dass ich nicht weiß, wie ich über das Layout auf die Aktivität zugreifen kann.

Profilansicht

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

Wie Sie oben sehen können, instanziiere ich die profileView programmgesteuert und übergebe damit den activityContext. 2 Fragen:

  1. Übergebe ich den richtigen Kontext in die Profilansicht?
  2. Wie erhalte ich die enthaltende Aktivität aus dem Kontext?
OBERTON
quelle

Antworten:

471

Geben Sie von Ihrem Activityeinfach thisdas Contextfür Ihr Layout ein:

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

Danach haben Sie ein ContextLayout, aber Sie werden wissen, dass es tatsächlich Ihr Layout ist, Activityund Sie können es so umwandeln, dass Sie das haben, was Sie brauchen:

Activity activity = (Activity) context;
Boris Strandjev
quelle
53
Es kann nicht garantiert werden, dass der Kontext, mit dem Sie arbeiten, ein Aktivitätskontext oder ein Anwendungskontext ist. Versuchen Sie, einen Anwendungskontext an eine Dialogansicht zu übergeben, beobachten Sie, wie er abstürzt, und Sie werden den Unterschied feststellen.
Sky Kelsey
6
Boris, die Frage fragt, ob es eine Möglichkeit gibt, eine Aktivität aus einem Kontext zu erhalten. Das ist nicht möglich. Natürlich können Sie besetzen, aber das ist ein letzter Ausweg. Wenn Sie den Kontext als Aktivität behandeln möchten, werden Sie nicht auf eine Aktivität herabgestuft. Dies vereinfacht den Code und ist später weniger anfällig für Fehler, wenn eine andere Person Ihren Code verwaltet.
Sky Kelsey
6
Beachten Sie, dass 'getApplicationContext ()' anstelle von 'this' nicht funktioniert.
Dwbrito
1
@ BorisStrandjev Ich habe Ihren Kommentar nicht ganz verstanden. Wie auch immer, ich sagte, dass ich nach dem Ausprobieren Ihres Beispiels getApplicationContext () anstelle von 'this' verwendet habe und die Anwendung versucht hat, die App selbst zu konvertieren, wodurch anstelle der Aktivität ein Casting-Fehler ausgegeben wurde. Nachdem Sie zu 'this' gewechselt haben, hat es funktioniert, wie Sie geantwortet haben.
Dwbrito
1
Die am höchsten bewerteten Antworten auf Ihren Link schlagen beide vor, die Frage herauszufordern, wenn sie stinkt. Diese Frage stinkt sicherlich. Das OP erklärte zunächst: "Ich muss eine Aktivitätsmethode aus einer benutzerdefinierten Layoutklasse heraus aufrufen." Dies ist bei entsprechender Verwendung von Schnittstellen vollständig erreichbar. Dann sagt er: "Das Problem dabei ist, dass ich nicht weiß, wie ich über das Layout auf die Aktivität zugreifen soll." Das ist ein bedeutender Hinweis auf ein Missverständnis. Die Leute versuchen ständig, beim Programmieren das Falsche zu tun, und wir sollten nicht die Augen verschließen.
Sam
39

Dies ist etwas, in das ich erfolgreich konvertiert habe Context, Activitywenn ich innerhalb der Benutzeroberfläche in Fragmenten oder benutzerdefinierten Ansichten arbeite. Es entpackt ContextWrapper rekursiv oder gibt null zurück, wenn es fehlschlägt.

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;
}
Das Ö
quelle
Das ist die richtige Antwort. Die anderen berücksichtigen die ContentWrapper-Hierarchie nicht.
Snicolas
Dies ist die wahre Antwort :)
Lygstate
1
@lygstate: Welche Ziel-API-Ebene verwenden Sie in Ihrer App? Was ist der Fehler? Dies funktioniert nur innerhalb der Benutzeroberfläche (Aktivitäten, Fragmente usw.), nicht in Diensten.
Theo
31
  1. Nein
  2. Das kannst du nicht

In Android gibt es zwei verschiedene Kontexte. Eine für Ihre Anwendung (nennen wir sie die GROSSE) und eine für jede Ansicht (nennen wir sie den Aktivitätskontext).

Ein lineares Layout ist eine Ansicht, daher müssen Sie den Aktivitätskontext aufrufen. Um es von einer Aktivität aus aufzurufen, rufen Sie einfach "this" auf. So einfach ist es nicht?

Wenn Sie verwenden

this.getApplicationContext();

Sie rufen den BIG-Kontext auf, der Ihre Anwendung beschreibt und Ihre Ansicht nicht verwalten kann.

Ein großes Problem mit Android ist, dass ein Kontext Ihre Aktivität nicht aufrufen kann. Das ist eine große Sache, um dies zu vermeiden, wenn jemand mit der Android-Entwicklung beginnt. Sie müssen einen besseren Weg finden, um Ihre Klasse zu codieren (oder "Kontextkontext" durch "Aktivitätsaktivität" ersetzen und bei Bedarf in "Kontext" umwandeln).

Grüße.


Nur um meine Antwort zu aktualisieren. Der einfachste Weg, um Ihre zu erhalten, Activity contextbesteht darin, eine staticInstanz in Ihrer zu definieren Activity. Beispielsweise

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

Und dann, in der Task, Dialog, View, können Sie diese Art von Code verwenden , um Ihre zu erhalten Activity context:

if (DummyActivity.instance != null)
{
    // Do your operations with DummyActivity.instance
}
Manitoba
quelle
4
+1 zur Erklärung eines sehr häufigen Verwechslungsbereichs zwischen den beiden verschiedenen Arten von Kontexten (genau wie es zwei verschiedene gibt R). Die Google-Leute müssen ihren Wortschatz erweitern.
an00b
3
Übrigens, @BorisStrandjev ist richtig: 2. Ja, das können Sie . (kann nicht mit
Arbeitscode
2
2. Nicht wirklich. Wenn der Kontext der Anwendungskontext wäre, würde Ihre App abstürzen.
StackOverflowed
statische Instanz?! @Nepster hat die beste Lösung für dieses Imo
Sam
14
Das Erstellen eines statischen Verweises auf eine Aktivität ist der beste Weg, um Speicherlecks zu erstellen.
BladeCoder
8

Wenn Sie eine Aktivitätsmethode aus einer benutzerdefinierten Layoutklasse (Nicht-Aktivitätsklasse) heraus aufrufen möchten, sollten Sie einen Delegaten über die Schnittstelle erstellen.

Es ist ungetestet und ich habe es richtig codiert. Aber ich vermittle einen Weg, um das zu erreichen, was Sie wollen.

Zunächst erstellen und Schnittstelle

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

Und implementieren Sie dies für jede Aktivität.

und nenne es wie

ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
pv.setCallBack(new TaskCompleteListener
               {
                   public void onProfileClicked(String resultStringFromProfileView){}
               });
Zar E Ahmer
quelle
1
Dies ist die richtige Antwort und sollte als die richtige Antwort markiert werden. Ich weiß, dass die als richtig markierte Antwort tatsächlich die Frage von OP beantwortet, aber sie sollte die Frage nicht so beantworten. Tatsache ist, dass es keine gute Praxis ist, die Aktivität so in einer Ansicht weiterzugeben. Das Kind sollte auf keinen Fall etwas über seine Eltern wissen, außer durch die Context. Wie Nepster feststellt, besteht die beste Vorgehensweise darin, einen Rückruf weiterzuleiten. Wenn also etwas passiert, das für die Eltern von Interesse ist, wird der Rückruf mit den relevanten Daten ausgelöst.
Darwind
6

Der Kontext kann eine Anwendung, ein Dienst, eine Aktivität und mehr sein.

Normalerweise ist der Kontext von Ansichten in einer Aktivität die Aktivität selbst, sodass Sie vielleicht denken, Sie können diesen Kontext einfach in Aktivität umwandeln, aber tatsächlich können Sie dies nicht immer tun, da der Kontext in diesem Fall auch ein ContextThemeWrapper sein kann.

ContextThemeWrapper wird in den neuesten Versionen von AppCompat und Android häufig verwendet (dank des Attributs android: theme in Layouts), daher würde ich diese Besetzung persönlich niemals durchführen.

Die kurze Antwort lautet: Sie können eine Aktivität nicht zuverlässig aus einem Kontext in einer Ansicht abrufen. Übergeben Sie die Aktivität an die Ansicht, indem Sie eine Methode aufrufen, die die Aktivität als Parameter verwendet.

BladeCoder
quelle
3

Verwenden Sie niemals getApplicationContext () mit Ansichten.

Es sollte immer der Kontext der Aktivität sein, da die Ansicht an die Aktivität angehängt ist. Möglicherweise haben Sie auch ein benutzerdefiniertes Themenset, und wenn Sie den Kontext der Anwendung verwenden, gehen alle Themen verloren. Lesen Sie mehr über verschiedene Versionen von Kontexten hier .

Lomza
quelle
3

Und in Kotlin:

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

Eine Aktivität ist eine Spezialisierung der Kontext so, wenn Sie einen Kontext haben Sie bereits wissen , welche Aktivität Sie verwenden möchten , und können einfach werfen ein in c ; wo eine ist eine Tätigkeit , und c ist ein Context.

Activity a = (Activity) c;
ACLima
quelle
7
Dies ist gefährlich, da der Kontext, wie in einem separaten Kommentar erwähnt, möglicherweise nicht immer eine Aktivität ist.
4
typecast nur wenn (Kontextinstanz der Aktivität) {// typecast}
Amit Yadav
0

Ich habe Konvertierungsaktivität verwendet

Activity activity = (Activity) context;
Samuel Ivan
quelle
2
Es gibt verschiedene Arten von Kontexten. Aktivitäten und Anwendungen können Kontexte haben. Dies funktioniert nur, wenn es sich bei dem Kontext um eine Aktivität handelt.
Cylov
0

Diese Methode sollte hilfreich sein ..!

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;

    }

Ich hoffe das hilft .. Frohe Codierung!

Taslim Oseni
quelle
Stellen Sie sicher, dass der Kontext, den Sie übergeben haben, nicht null ist. Das ist höchstwahrscheinlich das Problem.
Taslim Oseni
0

Wie wäre es mit einem Live-Datenrückruf?

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

class ProfileActivity{

  override fun onCreateView(...){

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

}
Abhinav Atul
quelle