Вызов метода 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

Любая идея ?

user1602687
источник
вы не можете вызвать 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();

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

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

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

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

Myadapter adapter = new Myadapter() {
  @Override
  public void callBack() {
    // dosomething
  }
};
Прашант Деббадвар
источник
5

Для Котлина:

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

(context as Your_Activity_Name).yourMethod()
Numair
источник
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) {

    }
}
лопасть
источник