Метод активності виклику від адаптера


119

Чи можна викликати метод , який визначений в Activityс ListAdapter?

(Я хочу зробити Buttonв list'sрядку і при натисканні на цю кнопку, він повинен виконати метод, який визначений у відповідній активності. Я спробував встановити 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();
}

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

Потім в Adapter 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
Старший брате, я не маю нічого проти вас, але відповідь нижче - найкраща практика. Можна навіть читати в коментарях. Ви не повинні додавати mContext до своєї діяльності, оскільки ви уникаєте повторного використання коду. Тепер цей адаптер можна використовувати лише в межах діяльності, на яку ви додали змінну mContext, де, якщо у вас визначений слухач, ви можете використовувати той же адаптер в іншій діяльності. Повірте мені, що я витратив достатньо часу на промисловість, і я просто намагаюся допомогти вам тут, будь ласка, не сприймайте це особисто.
Варундроїд

2
Це рішення - одне з найкращих, що я коли-небудь знаходив на SO. Тож дуже дякую, пане Бабу. Виклик методів Активності цим методом дає всій програмі підвищення на 500% підвищення продуктивності -> Мені не потрібно перезавантажувати базу даних в адаптер, до якого мені потрібно отримати доступ. Мене не хвилюють "роки в промисловості", я шукаю стабільних та працюючих рішень, і я впевнений, що не знайду кращого способу отримати доступ. Можливо, я міг би створити БД як одиночний, але це не спосіб, яким я хочу "неструктурувати" свій проект.
Мартін Пфеффер

2
@RAM, якщо вам потрібно викликати один і той же метод у кількох заходах, вам потрібно створити інтерфейс з цим ім'ям методу. Потім реалізуйте цей інтерфейс у тому, що всі дії, необхідні для виклику методу, будуть використані. Потім у слухачі клацань перевірте примірник свого інтерфейсу, а не використовуйте ім’я діяльності.
Eldhose M Babu

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

І десь у адаптері, коли вам потрібно зателефонувати в такий спосіб діяльності:

listener.foo();

5
Я спершу подумав про метод вище, як і з фрагментами, але це найкраще рішення!
brux

1
який мій InterfaceListener? як можна ініціалізувати це? загальнодоступний MyAdapter (слухач MyInterface) {this.listener = слухач; }
Гілберто Ібарра

6
Як ви передаєте інтерфейс адаптеру, хоча (з діяльності)
Брендон

1
@Brandon ви можете передати це параметр конструктора в адаптері.
Ігор Філіппов

5
@Brandon, просто додайте "це" для передачі інтерфейсу для адаптера myAdapter = новий MyAdapter (це);
ajinkya

67

Оригінал:

Я розумію поточну відповідь, але потребував більш чіткого прикладу. Ось приклад того, що я використовував із Adapter(RecyclerView.Adapter) та an 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
дякую за це, це саме те, що я шукав. до-голосування
нактуси

це працювало для мене! imo, найповніший робочий код досі!
publicknowledge

2
Дякую за таке рішення
Стів

4
ця відповідь повинна мати більше результатів, це чисте та ідеальне рішення.
Opiatefuchs

Привіт @Jared - Ви можете обрати той самий зразок EventBus, який ви запропонували?
Тохід

15

Основні і прості.

У своєму адаптері просто використовуйте це.

((YourParentClass) context).functionToRun();


4
Недобра практика, оскільки ви обмежуєте свій клас працювати лише з типом YourParentClass замість будь-якого класу, але він працює, якщо ви не переймаєтесь його повторним використанням, якщо ви також перейменовуєте клас, перерви в коді ...
Gustavo Baiocchi Costa

7

Ще один спосіб:

Введіть метод у свій адаптер, що дозволяє говорити про недійсний 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()методу ваших adapterISyourActivity

ПРИМІТКА: parentце GroupView


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

0

У Котліні тепер є більш чистий спосіб, використовуючи функції лямбда, не потрібно інтерфейсів:

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.