Método de atividade de chamada do adaptador

119

É possível chamar o método definido em Activityfrom ListAdapter?

(Quero fazer um Buttonin list'srow e quando este botão for clicado deve executar o método, que está definido na Activity correspondente. Tentei setar onClickListenerno meu ListAdaptermas não sei como chamar esse método, qual é o seu caminho. ..)

quando usei Activity.this.method(), recebo o seguinte erro:

No enclosing instance of the type Activity is accessible in scope

Qualquer ideia ?

user1602687
fonte
você não pode chamar activity.this em alguma outra classe, a menos que seja uma classe interna a essa atividade. siga @Eldhose M Babu solução para o seu caso
Archie.bpgc

Respostas:

306

Sim você pode.

No adaptador, adicione um novo campo:

private Context mContext;

No construtor do adaptador, adicione o seguinte código:

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

No getView (...) do adaptador:

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

substitua por seus próprios nomes de classe onde você vê seu código, sua atividade etc.

Se você precisar usar este mesmo adaptador para mais de uma atividade:

Crie uma interface

public interface IMethodCaller {
    void yourDesiredMethod();
}

Implemente essa interface nas atividades de que você precisa para ter essa funcionalidade de chamada de método.

Então, em Adapter getView (), chame 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();
        }
    }
});

Você terminou. Se você precisar usar este adaptador para atividades que não requerem este mecanismo de chamada, o código não será executado (se a verificação falhar).

Eldhose M Babu
fonte
29
Como esta é uma pergunta popular, eu sugiro fortemente NÃO USAR esta solução. Você deve evitar o lançamento de classe aqui, porque isso pode levar a exceções de tempo de execução.
Igor Filippov
3
Eldhose mano, não tenho nada contra você, mas a resposta abaixo é a melhor prática. Você pode até ler nos comentários. Você não deve lançar mContext em sua Activity, pois está evitando a reutilização de código. Agora, este adaptador só pode ser usado dentro da Activity para a qual você lançou sua variável mContext, onde se você tiver um ouvinte definido, poderá reutilizar o mesmo Adapter em outra Activity. Acredite em mim, já passei muito tempo na indústria e estou apenas tentando ajudá-lo aqui, por favor, não leve para o lado pessoal.
Varundroid
2
Esta solução é uma das melhores que já encontrei no SO. Muito obrigado, Sr. Babu. Chamar os métodos da Activity por isso dá a todo o aplicativo um aumento de 500% no desempenho -> Não preciso recarregar o banco de dados no adaptador, que preciso acessar. Não me importo com “anos de indústria”, procuro soluções estáveis ​​e que funcionem e tenho a certeza, não encontrarei melhor forma de obter acesso. Talvez eu pudesse configurar o DB como um singleton, mas não é assim que quero "desestruturar" meu projeto.
Martin Pfeffer
2
@RAM se você precisar chamar o mesmo método em mais de uma atividade, será necessário criar uma interface com esse nome de método. Em seguida, implemente essa interface em todas as atividades de que você precisa que a chamada de método seja disponibilizada. Em seguida, no ouvinte de clique, verifique a instância de sua interface em vez de usar o nome da atividade.
Eldhose M Babu
1
Excelente resposta. Polegares para cima
Alok Rajasukumaran
126

Você pode fazer desta forma:

Interface de declaração:

public interface MyInterface{
    public void foo();
}

Deixe sua atividade implementá-la:

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

Em seguida, passe sua atividade para ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

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

E em algum lugar do adaptador, quando você precisar chamar esse método Activity:

listener.foo();
Igor Filippov
fonte
5
Pensei primeiro no método acima como faria com fragmentos, mas esta é a melhor solução!
brux
1
qual é o meu InterfaceListener? como posso inicializá-lo? público MyAdapter (ouvinte MyInterface) {this.listener = ouvinte; }
Gilberto Ibarra
6
Como você passa a interface para o adaptador (da Atividade)
Brandon
1
@Brandon você pode passar esse parâmetro do construtor no adaptador.
Igor Filippov
5
@Brandon, basta adicionar 'this' para passar a interface para o adaptador myAdapter = new MyAdapter (this);
ajinkya de
67

Original:

Eu entendo a resposta atual, mas precisava de um exemplo mais claro. Aqui está um exemplo do que usei com um Adapter(RecyclerView.Adapter) e um Activity.

Em sua atividade:

Isso implementará o interfaceque temos em nosso Adapter. Neste exemplo, ele será chamado quando o usuário clicar em um item no 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);
    }
}

Em seu adaptador:

No Activity, iniciamos nosso Adaptere passamos isso como um argumento para o construtor. Isso iniciará nosso interfacemétodo de retorno de chamada. Você pode ver que usamos nosso método de retorno de chamada para cliques do usuário.

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
fonte
6
obrigado por isso, é exatamente o que eu estava procurando. votado
Nactus
isso funcionou para mim! imo, código de trabalho mais completo até agora!
publicknowledge de
2
Obrigado por esta solução
Steve
4
esta resposta deveria ter mais votos positivos, é uma solução limpa e perfeita.
Opiatefuchs
Olá, @Jared - Você pode corrigir a mesma amostra EventBussugerida?
Tohid
15

Básico e simples.

Em seu adaptador, basta usar isso.

((YourParentClass) context).functionToRun();

Ck Maurya
fonte
4
não é uma boa prática porque você está restringindo sua classe para trabalhar apenas com o tipo YourParentClass ao invés de qualquer classe, mas funciona se você não se importar em reutilizá-la, se você também renomear uma classe o código quebra ...
Gustavo Baiocchi Costa
7

Mais uma maneira é:

Escreva um método em seu adaptador, digamos public void callBack () {}.

Agora, ao criar um objeto para o adaptador na atividade, substitua este método. O método de substituição será chamado quando você chamar o método no adaptador.

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

Para Kotlin:

Em seu adaptador, basta ligar

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

esta condição irá permitir que você execute algo se a Activity que tem as GroupViewvisualizações solicitantes do getView()método de seu adapterforyourActivity

NOTA: parenté esse GroupView

Abd Salam Baker
fonte
este GroupView solicita visualizações do adaptador ao chamar o getView()método ... então obtemos o contexto desse GroupView e o examinamos por meio da condição acima
Abd Salam Baker
0

Em Kotlin, agora existe uma maneira mais limpa de usar funções lambda, sem necessidade 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) {

    }
}
lâmina
fonte