Obtendo atividade do contexto no android

184

Este aqui me deixou perplexo.

Preciso chamar um método de atividade de dentro de uma classe de layout personalizada. O problema disso é que não sei como acessar a atividade de dentro do layout.

Vista de perfil

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

Como você pode ver acima, estou instanciando o profileView programaticamente e passando o activityContext com ele. 2 perguntas:

  1. Estou passando o contexto correto para o Profileview?
  2. Como obtenho a atividade que contém o contexto?
OVERTONE
fonte

Respostas:

473

No seu Activity, basta passar thiscomo Contextpara o seu layout:

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

Depois, você terá um Contextlayout, mas saberá que é realmente seu Activitye poderá transmiti-lo para ter o que precisa:

Activity activity = (Activity) context;
Boris Strandjev
fonte
53
Você não pode garantir que o contexto com o qual você está trabalhando seja um Contexto de Atividade ou um Contexto de Aplicativo. Tente passar um contexto de aplicativo para um DialogView, observe-o travar e você verá a diferença.
Sky Kelsey
6
Boris, a pergunta pergunta se existe uma maneira de obter uma Atividade de um Contexto. Isso não é possível. Claro que você pode lançar, mas esse é o último recurso. Se você deseja tratar o contexto como uma atividade, não faça o downcast para uma atividade. Isso facilita o código e é menos propenso a erros mais tarde, quando outra pessoa está mantendo seu código.
Sky Kelsey
6
Observe que 'getApplicationContext ()' em vez de 'this' não funcionará.
dwbrito
1
@BorisStrandjev Não entendi direito o seu comentário. Enfim, eu disse que depois de tentar o seu exemplo, mas em vez de 'this', usei getApplicationContext () e o aplicativo tentou lançar o próprio aplicativo, gerando um erro de conversão em vez da atividade. Depois de mudar para 'this', como você respondeu, funcionou.
Dwbrito 22/01
1
As respostas mais votadas no seu link sugerem desafiar a pergunta, se estiver mal cheirosa. Esta questão certamente é fedorenta. O OP primeiro declarou: "Preciso chamar um método de atividade de dentro de uma classe de layout personalizada". o que é completamente possível com o uso apropriado de interfaces. Então ele diz: "O problema é que não sei como acessar a atividade a partir do layout". que é uma dica significativa para um mal-entendido. As pessoas tentam fazer a coisa errada o tempo todo na programação e não devemos fechar os olhos para isso.
Sam
39

Isso é algo que eu usei com êxito para converter Contextao Activityoperar na interface do usuário em fragmentos ou visualizações personalizadas. Ele descompactará o ContextWrapper recursivamente ou retornará nulo se falhar.

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;
}
Theo
fonte
Esta é a resposta certa. Os outros não levam em conta a hierarquia do ContentWrapper.
Snicolas 26/09
Esta é a verdadeira resposta :)
lygstate
1
@lygstate: qual nível de API de destino você está usando no seu aplicativo? Qual é o erro? Isso funciona apenas na interface do usuário (atividades, fragmentos, etc.), não nos Serviços.
Theo
31
  1. Não
  2. Você não pode

Existem dois contextos diferentes no Android. Um para o seu aplicativo (vamos chamá-lo de GRANDE) e um para cada visualização (vamos chamá-lo de contexto da atividade).

Um linearLayout é uma visualização, então você deve chamar o contexto de atividade. Para chamá-lo de uma atividade, basta chamar "this". Tão fácil, não é?

Quando você usa

this.getApplicationContext();

Você chama o contexto BIG, aquele que descreve seu aplicativo e não pode gerenciar sua exibição.

Um grande problema com o Android é que um contexto não pode chamar sua atividade. Isso é importante para evitar isso quando alguém começa com o desenvolvimento do Android. Você precisa encontrar uma maneira melhor de codificar sua classe (ou substituir "Contexto de contexto" por "Atividade de atividade" e convertê-la em "Contexto" quando necessário).

Saudações.


Apenas para atualizar minha resposta. A maneira mais fácil de obter seuActivity context é definir uma staticinstância no seu Activity. Por exemplo

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 então, em sua Task, Dialog, View, você pode usar esse tipo de código para obter o seu Activity context:

if (DummyActivity.instance != null)
{
    // Do your operations with DummyActivity.instance
}
Manitoba
fonte
4
+1 para explicar uma área de confusão muito comum entre os 2 tipos diferentes de contextos (assim como existem 2 diferentes R). O pessoal do Google precisa enriquecer seu vocabulário.
an00b 23/07/12
3
BTW, @BorisStrandjev está correto: 2. Sim, você pode . (não pode argumentar com o código de trabalho)
an00b
2
2. Na verdade não. Se o contexto fosse o contexto Aplicativo, seu aplicativo falharia.
StackOverflowed
instância estática ?! @Nepster tem a melhor solução para este imo
Sam
14
Criar uma referência estática para uma Atividade é a melhor maneira de criar vazamentos de memória.
precisa saber é o seguinte
8

Se você deseja chamar um método de atividade de dentro de uma classe de layout personalizada (não Classe de Atividade). Você deve criar um delegado usando a interface.

Não foi testado e eu o codifiquei corretamente. mas estou transmitindo uma maneira de conseguir o que você deseja.

Antes de tudo, crie e faça interface

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 implemente isso em qualquer atividade.

e chame assim

ProfileView pv = new ProfileView(actvitiyContext, null, temp, tempPd);
pv.setCallBack(new TaskCompleteListener
               {
                   public void onProfileClicked(String resultStringFromProfileView){}
               });
Zar E Ahmer
fonte
1
Esta é a resposta correta e deve ser marcada como a resposta correta. Sei que a resposta marcada como correta realmente responde à pergunta do OP, mas não deveria estar respondendo à pergunta dessa maneira. O fato é que não é uma boa prática passar a atividade dessa maneira em uma visualização. A criança nunca deve saber sobre seus pais em nenhum caso, exceto através do Context. Como afirma o Nepster, a melhor prática é transmitir um retorno de chamada; portanto, sempre que algo interessar aos pais, o retorno de chamada será acionado com os dados relevantes.
Darwind
6

O contexto pode ser um Aplicativo, um Serviço, uma Atividade e muito mais.

Normalmente, o contexto de Views em uma Activity é a própria Activity, então você pode pensar que pode apenas converter esse Contexto em Activity, mas na verdade nem sempre pode fazê-lo, porque o contexto também pode ser um ContextThemeWrapper nesse caso.

O ContextThemeWrapper é muito usado nas versões recentes do AppCompat e Android (graças ao atributo android: theme nos layouts), então eu pessoalmente nunca faria esse elenco.

A resposta é curta: você não pode recuperar com segurança uma Atividade de um Contexto em uma Visualização. Passe a Atividade para a visualização chamando um método que tome a Atividade como parâmetro.

BladeCoder
fonte
3

Nunca use getApplicationContext () com visualizações.

Sempre deve ser o contexto da atividade, pois a visualização é anexada à atividade. Além disso, você pode ter um conjunto de temas personalizado e, ao usar o contexto do aplicativo, todos os temas serão perdidos. Leia mais sobre diferentes versões de contextos aqui .

lomza
fonte
3

E em Kotlin:

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

an Activity é uma especialização de Contexto, portanto, se você tem um Contexto, já sabe qual atividade pretende usar e pode simplesmente converter a em c ; onde a é uma atividade ec é um contexto.

Activity a = (Activity) c;
ACLima
fonte
7
Isso é perigoso porque, como mencionado em um comentário separado, o contexto nem sempre pode ser uma Atividade.
4
typecast somente se (instância de contexto da Activity) {// typecast} #
Amit Yadav
0

Eu usei convert Activity

Activity activity = (Activity) context;
Samuel Ivan
fonte
2
Existem diferentes tipos de contextos. Atividades e aplicativos podem ter contextos. Isso funcionará apenas quando o contexto for de uma atividade.
cylov
0

Este método deve ser útil ..!

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;

    }

Espero que isso ajude .. Feliz codificação!

Taslim Oseni
fonte
Verifique se o contexto que você passou não é nulo. Esse provavelmente é o problema.
Taslim Oseni
0

que tal um retorno de chamada de dados ao vivo,

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

class ProfileActivity{

  override fun onCreateView(...){

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

}
Abhinav Atul
fonte