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?
ARMÓNICO
fuente

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;
Boris Strandjev
fuente
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;
}
Theo
fuente
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
}
Manitoba
fuente
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){}
               });
Zar E Ahmer
fuente
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.

BladeCoder
fuente
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í .

lomza
fuente
3

Y en Kotlin:

tailrec fun Context.activity(): Activity? = when {
  this is Activity -> this
  else -> (this as? ContextWrapper)?.baseContext?.activity()
}
rjrjr
fuente
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;
ACLima
fuente
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;
Samuel Ivan
fuente
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!

Taslim Oseni
fuente
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()
    })
  }

}
Abhinav Atul
fuente