Вызов метода Activity из адаптера


119

Можно ли вызвать метод, определенный в Activity from ListAdapter?

(Я хочу создать Buttonв list'sстроке, и при нажатии этой кнопки он должен выполнить метод, определенный в соответствующем Activity. Я попытался установить onClickListenerв моем, ListAdapterно я не знаю, как вызвать этот метод, каков его путь. ..)

при использовании Activity.this.method()я получаю следующую ошибку:

No enclosing instance of the type Activity is accessible in scope

Любая идея ?


вы не можете вызвать activity.this в каком-либо другом классе, если это не внутренний класс для этого действия. следуйте решению @Eldhose M Babu для вашего дела
Archie.bpgc

Ответы:


306

Да, ты можешь.

В адаптере добавьте новое поле:

private Context mContext;

В конструктор адаптера добавьте следующий код:

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

В getView (...) адаптера:

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

замените на свои собственные имена классов, в которых вы видите свой код, свою активность и т. д.

Если вам нужно использовать один и тот же адаптер для нескольких действий, тогда:

Создать интерфейс

public interface IMethodCaller {
    void yourDesiredMethod();
}

Реализуйте этот интерфейс в действиях, которые необходимы для использования этой функции вызова метода.

Затем в адаптере getView () вызовите так:

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

Вы сделали. Если вам нужно использовать этот адаптер для действий, которые не требуют этого механизма вызова, код не будет выполняться (если проверка не удалась).


29
Поскольку это популярный вопрос, я настоятельно рекомендую НЕ ИСПОЛЬЗОВАТЬ это решение. Здесь следует избегать приведения классов, поскольку это может привести к исключениям во время выполнения.
Игорь Филиппов

3
Eldhose, братан, я ничего не имею против тебя, но ответ ниже - это лучшая практика. Можно даже прочитать в комментариях. Вы не должны приводить mContext к своему Activity, так как избегаете повторного использования кода. Теперь этот адаптер можно использовать только внутри Activity, в которое вы добавили свою переменную mContext, где, если у вас определен слушатель, вы можете повторно использовать тот же адаптер в другом Activity. Поверьте, я провел достаточно времени в индустрии и просто пытаюсь вам помочь, пожалуйста, не принимайте это на свой счет.
Варундроид

2
Это решение - одно из лучших, которые я когда-либо находил на SO. Так что большое спасибо, мистер Бабу. Вызов методов Activity таким образом дает всему приложению прирост производительности на 500% -> Мне не нужно перезагружать базу данных в адаптере, к которому мне нужно получить доступ. Меня не волнуют «годы в индустрии», я ищу стабильные и работающие решения и совершенно уверен, что не найду лучшего способа получить доступ. Возможно, я мог бы настроить БД как синглтон, но это не тот способ, которым я хочу «расформировать» свой проект.
Мартин Пфеффер

2
@RAM, если вам нужно вызвать один и тот же метод более чем в одном действии, вам необходимо создать интерфейс с этим именем метода. Затем реализуйте этот интерфейс во всех действиях, которые вам нужны для вызова метода. Затем в прослушивателе кликов проверьте экземпляр своего интерфейса, а не используйте имя действия.
Эльдхосе М Бабу

1
Замечательный ответ. Недурно
Алок Раджасукумаран

126

Сделать это можно так:

Объявить интерфейс:

public interface MyInterface{
    public void foo();
}

Пусть ваша деятельность реализует это:

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

Затем передайте свою активность в ListAdater:

public MyAdapter extends BaseAdater{
    private MyInterface listener;

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

И где-то в адаптере, когда вам нужно вызвать этот метод Activity:

listener.foo();

5
Сначала я подумал о методе выше, как о фрагментах, но это лучшее решение!
brux

1
какой у меня InterfaceListener? как его инициализировать? общедоступный MyAdapter (слушатель MyInterface) {this.listener = слушатель; }
Жилберто Ибарра

6
Как передать интерфейс адаптеру (из Activity)
Брэндон

1
@Brandon вы можете передать это параметр конструктора в адаптере.
Игорь Филиппов

5
@Brandon, просто добавьте this, чтобы передать интерфейс адаптеру myAdapter = new MyAdapter (this);
ajinkya

67

Оригинал:

Я понимаю текущий ответ, но мне нужен более четкий пример. Вот пример того, что я использовал с Adapter(RecyclerView.Adapter) иActivity .

В вашей деятельности:

Это будет реализовывать то interface, что есть в нашем Adapter. В этом примере он будет вызываться, когда пользователь щелкает элемент в файле 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);
    }
}

В вашем адаптере:

В Activity, мы инициировали наш Adapterи передали это как аргумент конструктору. Это запустит interfaceнаш метод обратного вызова. Вы можете видеть, что мы используем наш метод обратного вызова для пользовательских кликов.

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

6
спасибо за это, это именно то, что я искал. проголосовало за
Nactus

это сработало для меня! imo, самый полный рабочий код на данный момент!
publicknowledge 09

2
Спасибо за это решение
Стив

4
этот ответ должен иметь больше голосов, это чистое и идеальное решение.
Opiatefuchs

Привет, @Jared - Можете ли вы исправить тот же образец, EventBusкоторый вы предложили?
Tohid

15

Простой и простой.

В вашем адаптере просто используйте это.

((YourParentClass) context).functionToRun();


4
не очень хорошая практика, потому что вы ограничиваете свой класс работой только с типом YourParentClass вместо любого класса, но работает, если вы не заботитесь о его повторном использовании, если вы также переименуете класс, код ломается ...
Густаво Байокки Коста,

7

Еще один способ:

Напишите метод в своем адаптере, скажем, public void callBack () {}.

Теперь при создании объекта для адаптера в действии переопределите этот метод. Метод переопределения будет вызван при вызове метода в адаптере.

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

5

Для Котлина:

В вашем адаптере просто позвоните

(context as Your_Activity_Name).yourMethod()

0
if (parent.getContext() instanceof yourActivity) {
  //execute code
}

это условие позволит вам выполнить что-либо, если действие, которое имеет GroupViewзапрашивающие представления из getView()метода вашего adapter,yourActivity

ПРИМЕЧАНИЕ: parentэто GroupView


этот GroupView запрашивает представления у адаптера, когда он вызывает getView()метод ... так что мы получаем контекст этого GroupView и исследуем его с помощью условия выше
Абд Салам Бейкер,

0

В Kotlin теперь есть более чистый способ использования лямбда-функций, без интерфейсов:

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) {

    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.