Llamar al método de actividad desde el adaptador

119

¿Es posible llamar al método que se define en Activitydesde ListAdapter?

(Quiero hacer una Buttonen la list'sfila y cuando se hace clic en este botón, se debería realizar el procedimiento, que se define en la actividad correspondiente. Traté de conjunto onClickListeneren mi ListAdapter, pero no sé cómo llamar a este método, ¿cuál es su camino. ..)

cuando lo usé Activity.this.method(), obtengo el siguiente error:

No enclosing instance of the type Activity is accessible in scope

Alguna idea ?

usuario1602687
fuente
no puede llamar a activity.this en alguna otra clase a menos que sea una clase interna para esa actividad. siga la solución @Eldhose M Babu para su caso
Archie.bpgc

Respuestas:

306

Sí tu puedes.

En el adaptador, agregue un nuevo campo:

private Context mContext;

En el adaptador Constructor agregue el siguiente código:

public AdapterName(......, Context context) {
  //your code.
  this.mContext = context;
}

En el getView (...) de Adapter:

Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
    if (mContext instanceof YourActivityName) {
      ((YourActivityName)mContext).yourDesiredMethod();
    }
  }
});

reemplace con sus propios nombres de clase donde vea su código, su actividad, etc.

Si necesita usar este mismo adaptador para más de una actividad, entonces:

Crea una interfaz

public interface IMethodCaller {
    void yourDesiredMethod();
}

Implemente esta interfaz en las actividades que necesite para tener esta funcionalidad de llamada al método.

Luego, en Adapter getView (), llame como:

Button btn = (Button) convertView.findViewById(yourButtonId);
btn.setOnClickListener(new Button.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mContext instanceof IMethodCaller) {
            ((IMethodCaller) mContext).yourDesiredMethod();
        }
    }
});

Estás listo. Si necesita usar este adaptador para actividades que no requieren este mecanismo de llamada, el código no se ejecutará (si la verificación falla).

Eldhose M Babu
fuente
29
Como esta es una pregunta popular, le sugiero que NO USE esta solución. Debe evitar la conversión de clases aquí, ya que esto podría generar excepciones en el tiempo de ejecución.
Igor Filippov
3
Eldhose hermano, no tengo nada contra ti, pero la respuesta a continuación es la mejor práctica. Incluso puedes leer en los comentarios. No debe enviar mContext a su actividad, ya que evita la reutilización del código. Ahora, este adaptador solo se puede usar dentro de la actividad a la que ha enviado su variable mContext, donde si tiene un oyente definido, puede reutilizar el mismo adaptador en otra actividad. Créame, he pasado suficiente tiempo en la industria y solo estoy tratando de ayudarlo, por favor no lo tome como algo personal.
Varundroid
2
Esta solución es una de las mejores que he encontrado en SO. Así que muchas gracias Sr. Babu. Llamar a los métodos de Activity de esta manera, le da a toda la aplicación un aumento de rendimiento del 500% -> No necesito recargar la base de datos en el adaptador, al que necesito acceder. No me importan los "años en la industria", estoy buscando soluciones estables y que funcionen y estoy bastante seguro de que no encontraré una mejor manera de acceder. Quizás podría configurar el DB como singleton, pero esta no es la forma en que quiero "desestructurar" mi proyecto.
Martin Pfeffer
2
@RAM si necesita llamar al mismo método en más de una actividad, necesita crear una interfaz con ese nombre de método. Luego, implemente esa interfaz en todas las actividades que necesita para que se aproveche la llamada al método. Luego, en el oyente de clics, verifique la instancia de su interfaz en lugar de usar el nombre de la actividad.
Eldhose M Babu
1
Excelente respuesta. Pulgar hacia arriba
Alok Rajasukumaran
126

Puedes hacerlo de esta manera:

Declarar interfaz:

public interface MyInterface{
    public void foo();
}

Deje que su actividad la complemente:

public class MyActivity extends Activity implements MyInterface{
    public void foo(){
        //do stuff
    }
}

Luego, pase su actividad a ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

    public MyAdapter(MyInterface listener){
        this.listener = listener;
    }
}

Y en algún lugar del adaptador, cuando necesite llamar a ese método de actividad:

listener.foo();
Igor Filippov
fuente
5
Primero pensé en el método anterior como lo haría con los fragmentos, ¡pero esta es la mejor solución!
brux
1
¿Cuál es mi InterfaceListener? ¿Cómo puedo inicializarlo? public MyAdapter (escucha de MyInterface) {this.listener = escucha; }
Gilberto Ibarra
6
Sin embargo, ¿cómo se pasa la interfaz al adaptador (de la Actividad)
Brandon
1
@Brandon puede pasar su parámetro de constructor en el adaptador.
Igor Filippov
5
@Brandon, simplemente agregue 'esto' para pasar la interfaz al adaptador myAdapter = new MyAdapter (esto);
ajinkya
67

Original:

Entiendo la respuesta actual, pero necesitaba un ejemplo más claro. Aquí hay un ejemplo de lo que usé con un Adapter(RecyclerView.Adapter) y un Activity.

En tu actividad:

Esto implementará lo interfaceque tenemos en nuestro Adapter. En este ejemplo, se llamará cuando el usuario haga clic en un elemento del archivo RecyclerView.

public class MyActivity extends Activity implements AdapterCallback {

    private MyAdapter myAdapter;

    @Override
    public void onMethodCallback() {
       // do something
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        myAdapter = new MyAdapter(this);
    }
}

En su adaptador:

En Activity, iniciamos nuestro Adaptery pasamos esto como argumento al constructor. Esto iniciará nuestro interfacemétodo de devolución de llamada. Puede ver que usamos nuestro método de devolución de llamada para los clics de los usuarios.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {

    private AdapterCallback adapterCallback;

    public MyAdapter(Context context) {
        try {
            adapterCallback = ((AdapterCallback) context);
        } catch (ClassCastException e) {
            throw new ClassCastException("Activity must implement AdapterCallback.", e);
        }
    }

    @Override
    public void onBindViewHolder(MyAdapter.ViewHolder viewHolder, int position) {
        // simple example, call interface here
        // not complete
        viewHolder.itemView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    adapterCallback.onMethodCallback();
                } catch (ClassCastException e) {
                   // do something
                }
            }
        });
    }

    public static interface AdapterCallback {
        void onMethodCallback();
    }
}
Jared Burrows
fuente
6
gracias por esto, es exactamente lo que estaba buscando. votado a favor
Nactus
esto funcionó para mí! imo, el código de trabajo más completo hasta ahora!
publicknowledge
2
Gracias por esta solución
Steve
4
esta respuesta debería tener más votos a favor, es una solución limpia y perfecta.
Opiatefuchs
Hola @Jared: ¿puedes corregir la misma muestra con la EventBusque sugieres?
Tohid
15

Básico y sencillo.

En su adaptador, simplemente use esto.

((YourParentClass) context).functionToRun();

Ck Maurya
fuente
4
no es una buena práctica porque está restringiendo su clase para que funcione solo con el tipo YourParentClass en lugar de cualquier clase, pero funciona si no le importa reutilizarla, si también cambia el nombre de una clase, el código se rompe ...
Gustavo Baiocchi Costa
7

Una forma más es:

Escriba un método en su adaptador, digamos public void callBack () {}.

Ahora, mientras crea un objeto para el adaptador en la actividad, anule este método. Se llamará al método de anulación cuando llame al método en el adaptador.

Myadapter adapter = new Myadapter() {
  @Override
  public void callBack() {
    // dosomething
  }
};
Prashanth Debbadwar
fuente
5

Para Kotlin:

En su adaptador, simplemente llame

(context as Your_Activity_Name).yourMethod()
Numair
fuente
0
if (parent.getContext() instanceof yourActivity) {
  //execute code
}

esta condición le permitirá ejecutar algo si la Actividad que tiene la GroupViewsolicitud de vistas del getView()método de su adapteresyourActivity

NOTA: parentes que GroupView

Abd Salam Baker
fuente
este GroupView solicita vistas del adaptador cuando llama al getView()método ... así que obtenemos el contexto de ese GroupView y lo examinamos a través de la condición anterior
Abd Salam Baker
0

En Kotlin ahora hay una forma más limpia mediante el uso de funciones lambda, sin necesidad de interfaces:

class MyAdapter(val adapterOnClick: (Any) -> Unit) {
    fun setItem(item: Any) {
        myButton.setOnClickListener { adapterOnClick(item) }
    }
}

class MyActivity {
    override fun onCreate(savedInstanceState: Bundle?) {
        var myAdapter = MyAdapter { item -> doOnClick(item) }
    }


    fun doOnClick(item: Any) {

    }
}
espada
fuente