Obteniendo actividad del contexto en Android


184

Este me tiene perplejo.

Necesito llamar a un método de actividad desde una clase de diseño personalizada. El problema con esto es que no sé cómo acceder a la actividad desde el diseño.

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

PerfilActividad

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 puede ver arriba, estoy instanciando el profileView programáticamente y pasando el ActivityContext con él. 2 preguntas:

  1. ¿Estoy pasando el contexto correcto a Profileview?
  2. ¿Cómo obtengo la actividad que contiene el contexto?

Respuestas:


473

Desde su Activity, simplemente pase thiscomo Contextpara su diseño:

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

Luego tendrá un Contextdiseño, pero sabrá que es realmente suyo Activityy puede lanzarlo para que tenga lo que necesita:

Activity activity = (Activity) context;

53
No se puede garantizar que el contexto con el que está trabajando sea un Contexto de actividad o un Contexto de aplicación. Intente pasar un contexto de aplicación a DialogView, observe cómo se bloquea y verá la diferencia.
Sky Kelsey

66
Boris, la pregunta pregunta si hay una manera de obtener una Actividad desde un Contexto. Esto no es posible. Por supuesto que puedes lanzar, pero ese es un último recurso. Si desea tratar el contexto como una actividad, no lo rechace a una actividad. Facilita el código y es menos propenso a errores más tarde cuando otra persona mantiene su código.
Sky Kelsey

66
Tenga en cuenta que 'getApplicationContext ()' en lugar de 'esto' no funcionará.
dwbrito

1
@ BorisStrandjev No entiendo bien tu comentario. De todos modos, dije que después de probar su ejemplo, pero en lugar de 'esto', usé getApplicationContext () y la aplicación trató de transmitir la aplicación en sí misma, por lo tanto, dio un error de transmisión, en lugar de la actividad. Después de cambiar a 'esto', como respondiste, funcionó.
dwbrito

1
Las respuestas más elevadas en su enlace sugieren desafiar la pregunta si es maloliente. Esta pregunta ciertamente es maloliente. El OP primero declaró: "Necesito llamar a un método de actividad desde una clase de diseño personalizada". que es completamente alcanzable con el uso apropiado de interfaces. Luego dice: "El problema con esto es que no sé cómo acceder a la actividad desde el diseño". lo cual es una pista significativa hacia un malentendido. La gente trata de hacer lo incorrecto todo el tiempo en la programación y no debemos hacer la vista gorda.
Sam

39

Esto es algo que he utilizado con éxito para convertir Contexta Activityfuncionar dentro de la interfaz de usuario en fragmentos o vistas personalizadas. Descomprimirá ContextWrapper recursivamente o devolverá nulo si falla.

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

Esta es la respuesta correcta. Los otros no tienen en cuenta la jerarquía de ContentWrapper.
Snicolas

Esta es la verdadera respuesta :)
lygstate

1
@lygstate: ¿Qué nivel de API objetivo está utilizando en su aplicación? Cual es el error Esto solo funciona dentro de la IU (actividades, fragmentos, etc.), no en los Servicios.
Theo

31
  1. No
  2. No puedes

Hay dos contextos diferentes en Android. Una para su aplicación (llamémosla GRANDE) y otra para cada vista (llamémosla contexto de actividad).

Un diseño lineal es una vista, por lo que debe llamar al contexto de actividad. Para llamarlo desde una actividad, simplemente llame "esto". ¿Tan fácil no es así?

Cuando usas

this.getApplicationContext();

Llama al contexto GRANDE, el que describe su aplicación y no puede administrar su vista.

Un gran problema con Android es que un contexto no puede llamar a su actividad. Es un gran problema evitar esto cuando alguien comienza con el desarrollo de Android. Debe encontrar una mejor manera de codificar su clase (o reemplazar "Contexto contextual" por "Actividad de actividad" y convertirlo en "Contexto" cuando sea necesario).

Saludos.


Solo para actualizar mi respuesta. La forma más fácil de obtener su Activity contextes definir una staticinstancia en su Activity. Por ejemplo

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

Y luego, en su Task, Dialog, View, usted podría utilizar ese tipo de código para obtener su Activity context:

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

44
+1 para explicar un área de confusión muy común entre los 2 tipos diferentes de contextos (al igual que hay 2 Rs diferentes ). La gente de Google necesita enriquecer su vocabulario.
an00b

3
Por cierto, @BorisStrandjev es correcto: 2. Sí, puedes . (no se puede discutir con el código de trabajo)
an00b

2
2. En realidad no. Si el contexto fuera el contexto de la Aplicación, entonces su aplicación se bloquearía.
StackOverflowed

instancia estática? @Nepster tiene la mejor solución para esta OMI
Sam

14
Crear una referencia estática a una Actividad es la mejor manera de crear pérdidas de memoria.
BladeCoder

8

Si desea llamar a un método de actividad desde una clase de diseño personalizada (no clase de actividad). Debe crear un delegado utilizando la interfaz.

No ha sido probado y lo codifiqué correctamente. pero estoy transmitiendo una forma de lograr lo que quieres.

Primero de todo crear e interfaz

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 esto en cualquier actividad.

y llámalo como

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

1
Esta es la respuesta correcta y debe marcarse como la respuesta correcta. Sé que la respuesta marcada como la correcta en realidad responde la pregunta de OP, pero no debería responder la pregunta de esa manera. El hecho es que no es una buena práctica pasar la Actividad así dentro de una vista. El niño nunca debe saber sobre sus padres en ningún caso, excepto a través de Context. Como dice Nepster, la mejor práctica es pasar una devolución de llamada, por lo que cada vez que algo sucede de interés para el padre, la devolución de llamada se activará con los datos relevantes.
Darwind

6

El contexto puede ser una Aplicación, un Servicio, una Actividad y más.

Normalmente, el contexto de Vistas en una Actividad es la Actividad en sí misma, por lo que puede pensar que puede convertir este Contexto en Actividad, pero en realidad no siempre puede hacerlo, porque el contexto también puede ser un ContextThemeWrapper en este caso.

ContextThemeWrapper se usa mucho en las versiones recientes de AppCompat y Android (gracias al atributo android: theme en los diseños), por lo que personalmente nunca realizaría este reparto.

Entonces, la respuesta corta es: no puede recuperar de manera confiable una Actividad de un Contexto en una Vista. Pase la Actividad a la vista llamando a un método que tome la Actividad como parámetro.


3

Nunca use getApplicationContext () con vistas.

Siempre debe ser el contexto de la actividad, ya que la vista se adjunta a la actividad. Además, es posible que tenga un conjunto de temas personalizado y, al usar el contexto de la aplicación, se perderá todo el tema. Lea más sobre diferentes versiones de contextos aquí .


3

Y en Kotlin:

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

0

una Actividad es una especialización de Contexto, por lo tanto, si tiene un Contexto ya sabe qué actividad tiene intención de usar y simplemente puede convertir a en c ; donde a es una actividad yc es un contexto.

Activity a = (Activity) c;

77
Esto es peligroso porque, como se menciona en un comentario separado, el contexto puede no ser siempre una Actividad.

44
typecast solo si (instancia de contexto de actividad) {// typecast}
Amit Yadav

0

Solía ​​convertir Actividad

Activity activity = (Activity) context;

2
Hay diferentes tipos de contextos. Las actividades y aplicaciones pueden tener contextos. Esto solo funcionará cuando el contexto sea de una actividad.
cylov

0

Este método debería 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 esto ayude. ¡Feliz codificación!


Verifique que el contexto en el que pasó no sea nulo. Probablemente ese sea el problema.
Taslim Oseni

0

¿qué tal una devolución de llamada de datos en vivo,

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

class ProfileActivity{

  override fun onCreateView(...){

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

}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.