Ottenere attività dal contesto in Android


184

Questo mi ha lasciato perplesso.

Devo chiamare un metodo di attività all'interno di una classe di layout personalizzata. Il problema è che non so come accedere all'attività dal layout.

profiloVedi

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

Come puoi vedere sopra, sto istanziando il ProfileView a livello di codice e passando con ActivityContext con esso. 2 domande:

  1. Sto passando il contesto corretto in Profileview?
  2. Come ottengo l'attività contenitiva dal contesto?

Risposte:


473

Dal tuo Activity, basta passare thiscome Contextper il tuo layout:

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

Successivamente avrai un Contextnel layout, ma saprai che in realtà è tuo Activitye puoi lanciarlo in modo da avere ciò di cui hai bisogno:

Activity activity = (Activity) context;

53
Non è possibile garantire che il contesto con cui si sta lavorando sia un contesto di attività o un contesto di applicazione. Prova a passare un contesto di applicazione a DialogView, guardalo in crash e vedrai la differenza.
Sky Kelsey

6
Boris, la domanda chiede se esiste un modo per ottenere un'attività da un contesto. Non è possibile. Certo che puoi lanciare, ma questa è l'ultima risorsa. Se si desidera trattare il contesto come un'attività, non eseguire il downcast a un'attività. Rende il codice più semplice ed è meno soggetto a bug in seguito quando un'altra persona mantiene il codice.
Sky Kelsey

6
Notare che 'getApplicationContext ()' invece di 'this' non funzionerà.
dwbrito,

1
@BorisStrandjev Non ho ancora capito bene il tuo commento. Ad ogni modo, ho detto che dopo aver provato il tuo esempio ma invece di 'this' ho usato getApplicationContext () e l'applicazione ha provato a lanciare l'App stessa, dando quindi un errore di cast, anziché l'attività. Dopo essere passato a "questo", come hai risposto, ha funzionato.
dwbrito,

1
Le risposte più votate più alte sul tuo link suggeriscono entrambe di sfidare la domanda se è puzzolente. Questa domanda è sicuramente puzzolente. L'OP ha dichiarato per la prima volta: "Devo chiamare un metodo di attività all'interno di una classe di layout personalizzata". che è completamente realizzabile con l'uso appropriato delle interfacce. Quindi dice "Il problema è che non so come accedere all'attività all'interno del layout". che è un suggerimento significativo per un malinteso. Le persone cercano di fare sempre la cosa sbagliata durante la programmazione e non dovremmo chiudere un occhio.
Sam,

39

Questo è qualcosa che ho usato con successo per convertire Contexta Activityquando si opera all'interno della interfaccia utente in frammenti o visualizzazioni personalizzate. Disimballerà ContextWrapper in modo ricorsivo o restituirà null in caso di errore.

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

Questa è la risposta esatta. Gli altri non tengono conto della gerarchia di ContentWrapper.
Snicolas,

Questa è la vera risposta :)
Lygstate

1
@lygstate: quale livello API di destinazione stai usando nella tua app? Qual è l'errore? Funziona solo all'interno dell'interfaccia utente (attività, frammenti, ecc.), Non in Servizi.
Theo

31
  1. No
  2. Non puoi

Esistono due contesti diversi in Android. Uno per la tua applicazione (chiamiamolo GRANDE) e uno per ogni vista (chiamiamolo contesto di attività).

Un linearLayout è una vista, quindi è necessario chiamare il contesto dell'attività. Per chiamarlo da un'attività, è sufficiente chiamare "questo". Così facile non è vero?

Quando si usa

this.getApplicationContext();

Tu chiami il GRANDE contesto, quello che descrive la tua applicazione e non può gestire la tua vista.

Un grosso problema con Android è che un contesto non può chiamare la tua attività. Questo è un grosso problema da evitare quando qualcuno inizia con lo sviluppo di Android. Devi trovare un modo migliore per codificare la tua classe (o sostituire "Context context" con "Activity activity" e lanciarlo su "Context" quando necessario).

Saluti.


Solo per aggiornare la mia risposta. Il modo più semplice per ottenere il tuo Activity contextè definire staticun'istanza nel tuo Activity. Per esempio

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

E poi, nel vostro Task, Dialog, View, è possibile utilizzare questo tipo di codice per ottenere la vostra Activity context:

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

4
+1 per spiegare un'area di confusione molto comune tra i 2 diversi tipi di contesti (proprio come ci sono 2 Rs diversi ). Le persone di Google devono arricchire il loro vocabolario.
an00b,

3
A proposito, @BorisStrandjev è corretto: 2. Sì, è possibile . (non posso discutere con il codice di lavoro)
an00b

2
2. Non proprio. Se il contesto fosse il contesto dell'applicazione, l'app si arresterebbe in modo anomalo.
StackOverflow

istanza statica ?! @Nepster ha la migliore soluzione per questo imo
Sam

14
La creazione di un riferimento statico a un'attività è il modo migliore per creare perdite di memoria.
BladeCoder il

8

Se ti piace chiamare un metodo di attività all'interno di una classe di layout personalizzata (non-Activity Class). Dovresti creare un delegato usando l'interfaccia.

Non è stato testato e l'ho codificato correttamente. ma sto trasmettendo un modo per ottenere ciò che vuoi.

Innanzitutto crea e interfaccia

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

E implementalo per qualsiasi attività.

e chiamalo come

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

1
Questa è la risposta corretta e deve essere contrassegnata come risposta corretta. So che la risposta contrassegnata come quella corretta risponde effettivamente alla domanda di OP, ma non dovrebbe rispondere alla domanda in quel modo. Il fatto è che non è buona prassi passare l'Attività in quel modo all'interno di una vista. Il bambino non dovrebbe mai conoscere il proprio genitore in nessun caso, tranne che attraverso il Context. Come afferma Nepster, la migliore pratica è passare un callback, quindi ogni volta che accade qualcosa di interessante per il genitore, il callback verrà attivato con i dati pertinenti.
Darwind,

6

Il contesto può essere un'applicazione, un servizio, un'attività e altro.

Normalmente il contesto di Views in un'attività è l'attività stessa, quindi potresti pensare di poter semplicemente trasmettere questo contesto ad attività, ma in realtà non puoi sempre farlo, perché in questo caso il contesto può anche essere un ContextThemeWrapper.

ContextThemeWrapper è ampiamente utilizzato nelle recenti versioni di AppCompat e Android (grazie a Android: attributo del tema nei layout), quindi personalmente non avrei mai eseguito questo cast.

La risposta così breve è: non è possibile recuperare in modo affidabile un'attività da un contesto in una vista. Passa l'attività alla vista chiamando un metodo su di essa che accetta l'attività come parametro.


3

Non usare mai getApplicationContext () con viste.

Dovrebbe essere sempre il contesto dell'attività, poiché la vista è allegata all'attività. Inoltre, potresti avere un set di temi personalizzato e quando si utilizza il contesto dell'applicazione, tutti i temi andranno persi. Leggi di più sulle diverse versioni dei contesti qui .


3

E a Kotlin:

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

0

un'attività è una specializzazione del contesto, quindi, se hai un contesto, sai già quale attività intendi utilizzare e puoi semplicemente trasmettere a in c ; dove a è un'attività e c è un contesto.

Activity a = (Activity) c;

7
Questo è pericoloso perché, come menzionato in un commento separato, il contesto potrebbe non essere sempre un'attività.

4
typecast solo se (contesto dell'istanza di Activity) {// typecast}
Amit Yadav il

0

Ho usato convert Activity

Activity activity = (Activity) context;

2
Esistono diversi tipi di contesti. Le attività e le applicazioni possono avere contesti. Funzionerà solo quando il contesto è di un'attività.
cylov

0

Questo metodo dovrebbe essere utile ..!

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;

    }

Spero che questo aiuti ... Buon codice!


Verifica che il contesto in cui sei passato non sia nullo .. Probabilmente è questo il problema.
Taslim Oseni,

0

che ne dici di un callback di dati live,

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

class ProfileActivity{

  override fun onCreateView(...){

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.