Как обрабатывать нажатие кнопки с помощью XML onClick внутри фрагментов

Pre-Honeycomb (Android 3), каждое действие было зарегистрировано для обработки нажатий кнопок с помощью тега onClick в XML макета:

android:onClick="myClickMethod"

В этом методе вы можете использовать view.getId() и оператор switch для выполнения логики кнопки.

С введением Honeycomb я разбиваю эти действия на фрагменты, которые можно повторно использовать во многих различных действиях. Большая часть поведения кнопок не зависит от активности, и я хотел бы, чтобы код находился внутри файла Fragments без, используя старый (до 1.6) метод регистрации OnClickListener для каждой кнопки.

final Button button = (Button) findViewById(R.id.button_id);
button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        // Perform action on click
    }
});

Проблема в том, что когда мой макет раздувается, нажатия кнопок по-прежнему получает действие хоста, а не отдельные фрагменты. Есть ли хороший подход к тому или иному

  • Зарегистрировать фрагмент, чтобы получать нажатия кнопок?
  • Передать события щелчка из Activity во фрагмент, которому они принадлежат?

person smith324    schedule 22.05.2011    source источник
comment
Разве вы не можете обработать регистрацию слушателей в onCreate фрагмента?   -  person CL22    schedule 23.05.2011
comment
@jodes Да, но я не хочу использовать setOnClickListener и findViewById для каждой кнопки, поэтому для упрощения был добавлен onClick.   -  person smith324    schedule 23.05.2011
comment
Глядя на принятый ответ, я думаю, что использование setOnClickListener более слабо связано, чем использование подхода XML onClick. Если действие должно «пересылать» каждый щелчок на правый фрагмент, это означает, что код придется изменять каждый раз, когда фрагмент добавляется. Использование интерфейса для отделения от базового класса фрагмента здесь не помогает. Если фрагмент регистрируется с правильной кнопкой, действие остается полностью независимым, что лучше стиля IMO. См. Также ответ Адоржана Принца.   -  person Adriaan Koster    schedule 10.12.2014
comment
@ smith324 должен согласиться с Адрианом в этом вопросе. Попробуйте ответить Адоряну и посмотрите, не станет ли после этого лучше жизнь.   -  person user1567453    schedule 24.09.2017


Ответы (19)


Вы могли просто сделать это:

Деятельность:

Fragment someFragment;    

//...onCreate etc instantiating your fragments

public void myClickMethod(View v) {
    someFragment.myClickMethod(v);
}

Фрагмент:

public void myClickMethod(View v) {
    switch(v.getId()) {
        // Just like you were doing
    }
}    

В ответ на @Ameen, который хотел меньше связи, чтобы фрагменты можно было повторно использовать

Интерфейс:

public interface XmlClickable {
    void myClickMethod(View v);
}

Деятельность:

XmlClickable someFragment;    

//...onCreate, etc. instantiating your fragments casting to your interface.
public void myClickMethod(View v) {
    someFragment.myClickMethod(v);
}

Фрагмент:

public class SomeFragment implements XmlClickable {

//...onCreateView, etc.

@Override
public void myClickMethod(View v) {
    switch(v.getId()){
        // Just like you were doing
    }
}    
person Blundell    schedule 07.06.2011
comment
По сути, это то, что я делаю сейчас, но это намного сложнее, когда у вас есть несколько фрагментов, каждый из которых должен получать события щелчка. Меня просто раздражают фрагменты в целом, потому что вокруг них растворились парадигмы. - person smith324; 09.06.2011
comment
Это не становится намного сложнее, так как вы просто передаете щелчок даже всем своим фрагментам, и только тот, у кого есть идентификатор, будет реагировать, но да, они бросили муху в oitment - person Blundell; 09.06.2011
comment
Прекрасный! Это совсем не беспорядочно, даже упрощает чтение кода другим людям (поскольку им не нужно угадывать, где заканчивается фактический вызов). :-) - person karllindmark; 31.01.2012
comment
Я думаю, вам следует назвать того, что находится во фрагменте, как-то myClickMethod_actual или что-то в этом роде. Поскольку может возникнуть двусмысленность в отношении того, какой метод вызывается позже. Я знаю, что на данный момент метод в действии - это всего лишь заглушка, но вы можете добавить к нему что-нибудь позже. - person Dheeraj Bhaskar; 03.02.2013
comment
Я столкнулся с той же проблемой, и хотя я ценю ваш ответ, это не чистый код с точки зрения разработки программного обеспечения. Этот код приводит к тому, что действие тесно связано с фрагментом. Вы должны иметь возможность повторно использовать один и тот же фрагмент в нескольких действиях, не зная подробностей реализации фрагментов. - person Ameen; 08.02.2013
comment
@Ameen все, что вам нужно сделать, это позволить вашему фрагменту реализовать интерфейс XmlClickable - person Blundell; 21.03.2014
comment
Должен быть switch (v.getId ()) {, а не switch (v.getid ()) { - person eb80; 25.06.2014
comment
Если вы используете метод первого прохода, вам не нужно делать метод во фрагменте общедоступным. - person Steve Waring; 20.07.2014
comment
Вместо определения своего собственного интерфейса вы можете использовать уже существующий OnClickListener, как упомянул Euporie. - person fr00tyl00p; 07.09.2014
comment
Думаю, это действительно правильный ответ. Нижеследующий ответ (со слишком большим количеством голосов) не решает проблему PO ... он хотел запустить событие щелчка с XML, а не с JAVA! - person S.Thiongane; 05.11.2014
comment
Вы также можете использовать существующий интерфейс View.OnClickListener для своего фрагмента! - person jrmgx; 20.12.2014
comment
Я почти плакал, когда читал это, это ТАКОЕ БОЙЛЕРПЛИТА ... Приведенный ниже ответ от @AdorjanPrincz - это то, что нужно. - person Wjdavis5; 02.01.2015
comment
создание экземпляра трансляции ваших фрагментов в интерфейс. как ? - person Milad Faridnia; 15.08.2016
comment
@milad XmlClickable someFragment = (XmlClickable) getFragmentManager().findFragment.... - person Blundell; 16.08.2016
comment
@DarthCoder Что значит не сработало? Произошла ошибка или приложение не реагировало на onclick? вы зарегистрировали прослушиватель onclick для элемента ui? - person Amruta-Pani; 29.04.2017
comment
@Blundell Это очень плохая практика. Даже XmlClickable - это тесно связанная архитектура. Каждый должен прокрутить вниз до другого ответа (500+ голосов, и это избавит вас от головной боли) - person user1567453; 24.09.2017
comment
@ user1567453, пока я отвечал на этот вопрос в 2011 году, вы читали вопрос? Специально просили не регистрировать прослушиватель кликов - person Blundell; 24.09.2017
comment
Это действительно так. Когда фрагмент добавляется в действие, все его представления добавляются в иерархию представлений действия. вот почему только действия могут обрабатывать такие события просмотра, как клики. В качестве подпредставления фрагмент действительно должен быть тупым. Если вам нужен код многократного использования, просто вызовите другой класс для обработки щелчка и делайте это везде, где идет фрагмент. PS: для чистого кода просто не используйте привязку XML, сделайте это в коде :) вы можете установить прослушиватель кликов при создании представления во фрагменте. - person Matei Suica; 01.03.2018

Я предпочитаю использовать следующее решение для обработки событий onClick. Это также работает для Activity и Fragments.

public class StartFragment extends Fragment implements OnClickListener{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {

        View v = inflater.inflate(R.layout.fragment_start, container, false);

        Button b = (Button) v.findViewById(R.id.StartButton);
        b.setOnClickListener(this);
        return v;
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.StartButton:

            ...

            break;
        }
    }
}
person Adorjan Princz    schedule 28.01.2013
comment
Я бы сделал это вместо принятого ответа, но я использую android:onClick для кнопок, размещенных в строках списка, а затем я просто делаю getPositionForView(v), чтобы получить позицию. Я не знаю, как мне это использовать. - person prostynick; 20.03.2013
comment
В onCreateView я просматриваю все дочерние элементы ViewGroup v и устанавливаю onclicklistener для всех обнаруженных мной экземпляров Button. Это намного лучше, чем ручная установка слушателя для всех кнопок. - person A Person; 21.03.2013
comment
Проголосовал. Это делает фрагменты многоразовыми. Иначе зачем использовать фрагменты? - person boreas; 29.05.2013
comment
@NathanOsman предоставил, что это решение, которое я бы рекомендовал и использовал, но вопрос просит не регистрировать прослушиватель щелчка - person Blundell; 11.06.2013
comment
Это приемлемое решение, но меня довольно озадачивает, почему вы не можете добиться такого же результата при регистрации onClick в макете фрагмента xml. - person apocalypz; 24.09.2013
comment
Я не предпочитаю регистрировать обработчики событий в файле xml, потому что существует множество событий, не имеющих атрибутов xml, поэтому, если вы хотите зарегистрировать все события в одном месте, единственное возможное решение - зарегистрировать их в коде. . - person Adorjan Princz; 26.09.2013
comment
Жаль, что у Android нет цепочки респондентов, такой как класс UIResponder в iOS, однако для фрагментов это решение настолько близко, насколько возможно. - person Leon Storey; 17.10.2013
comment
Разве это не та же техника, которую предлагали Программирование Windows в 1987 году? Не беспокоиться. Google быстро движется и ориентирован на продуктивность разработчиков. Я уверен, что скоро обработка событий станет такой же хорошей, как Visual Basic 1991-го года. - person Edward Brey; 18.10.2013
comment
Вы использовали импорт ведьм для OnClickListener? Intellij предлагает мне android.view.View.OnClickListener, и он не работает: / (onClick никогда не запускается) - person Lucas Jota; 29.11.2013
comment
@LucasJota, это android.view.View.OnClickListener. По какой-то причине я должен сказать implements View.OnClickListener, так что это может быть вашей проблемой. - person Kevin McCarpenter; 10.03.2014
comment
работает хорошо, но событие click не запускается из XML ... что является проблемой PO. Спасибо хоть - person S.Thiongane; 05.11.2014
comment
@NathanOsman Я думаю, что вопрос был связан с xml onClick, поэтому принятые ответы предоставляют точное решение. - person Naveed Ahmad; 19.11.2014
comment
Решение такого рода теперь предлагается в официальной документации в Руководстве по Android. ›Пользовательский интерфейс› События ввода ›Слушатели событий, хотя и с примером для действия. - person John Bentley; 12.12.2014
comment
Я использую API 21, и это решение не работает ... есть предложения? Благодарность - person Demian Flavius; 10.03.2015
comment
То же самое здесь, я использую API 21, и мое приложение вылетает в момент загрузки фрагмента, если я буду следовать этому подходу (я согласен, что это выглядит намного элегантнее и менее загромождено, чем принятый ответ). Любые предложения, как сделать эту работу, пожалуйста? - person Darth Coder; 10.03.2015
comment
@AdorjanPrincz, ваше решение выглядит очень чистым и точным, но, похоже, оно не работает для меня. Не могли бы вы также взглянуть на мою проблему: stackoverflow.com/questions/29021441/unable-to-get-onclick-method-in-fragment-to-work-using-existing-solutions - person Darth Coder; 13.03.2015
comment
@EdwardBrey ответ не претендовал на оригинальность. - person Jon Cairns; 08.06.2015
comment
@joonty Согласен. Мой комментарий был ударом в адрес Google, а не ответом SO. - person Edward Brey; 08.06.2015
comment
Это не работает на уровне API 20, onClick никогда не запускался, почему так много голосов за? - person zdd; 09.06.2015
comment
все заключено в один и тот же компонент, мне это нравится! - person QuarK; 09.06.2015
comment
реализует OnClickListener .... какой из них мы должны импортировать, чтобы эта реализация работала? - person A. Adam; 19.07.2015
comment
В случае, если кто-то попадает сюда и все еще сбит с толку, да, это не работает (по крайней мере, я уверен, что не смог этого сделать, и я последовал за ним до T, и этот метод работает в действиях). Чтобы найти фрагменты, перейдите ниже и посмотрите на метод, за который ответил Брилл Паппин, который не получил одобрения, и который работает отлично. - person StackAttack; 01.12.2017
comment
Это сработало для меня на Android Studio 4.x. Я бы рекомендовал сделать небольшую очистку, чтобы избежать переключения: поместите свою логику в метод в классе вашего фрагмента, затем используйте лямбда вместо анонимного объекта в качестве слушателя, например, view.findViewById(.R.id.donut).setOnClickListener(v -> showDonutOrder(v)); - person Jordan Deyton; 18.05.2021

Проблема, я думаю, в том, что представление по-прежнему является активностью, а не фрагментом. Фрагменты не имеют собственного независимого представления и прикреплены к родительскому представлению действий. Вот почему событие попадает в Activity, а не во фрагмент. Это прискорбно, но я думаю, вам понадобится код, чтобы это работало.

Во время преобразований я просто добавлял прослушиватель кликов, который вызывает старый обработчик событий.

например:

final Button loginButton = (Button) view.findViewById(R.id.loginButton);
loginButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(final View v) {
        onLoginClicked(v);
    }
});
person Brill Pappin    schedule 01.11.2011
comment
Спасибо - я использовал это с одной небольшой модификацией: я передаю представление фрагмента (т.е. результат inflater.inflate (R.layout.my_fragment_xml_resource)) в onLoginClicked (), чтобы он мог получить доступ к вложенным представлениям фрагментов, например EditText, через view.findViewById () (если я просто прохожу через представление активности, вызовы view.findViewById (R.id.myfragmentwidget_id) возвращают значение null). - person Michael Nelson; 01.01.2013
comment
Это не работает с API 21 в моем проекте. Есть мысли о том, как использовать этот подход? - person Darth Coder; 10.03.2015
comment
Это довольно простой код, который используется практически в каждом приложении. Вы можете описать, что с вами происходит? - person Brill Pappin; 11.03.2015
comment
Проголосуйте за этот ответ для объяснения проблемы, возникшей из-за того, что макет фрагмента привязан к представлению активности. - person fllo; 27.01.2016

Недавно я решил эту проблему, не добавляя метод в контекст Activity или не реализовывая OnClickListener. Я не уверен, что это «правильное» решение, но оно работает.

На основе: https://developer.android.com/tools/data-binding/guide.html#binding_events

Это можно сделать с помощью привязки данных: просто добавьте экземпляр фрагмента в качестве переменной, затем вы можете связать любой метод с onClick.

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.testapp.fragments.CustomFragment">

    <data>
        <variable android:name="fragment" android:type="com.example.testapp.fragments.CustomFragment"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_place_black_24dp"
            android:onClick="@{() -> fragment.buttonClicked()}"/>
    </LinearLayout>
</layout>

И код связывания фрагментов будет ...

public class CustomFragment extends Fragment {

    ...

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_person_profile, container, false);
        FragmentCustomBinding binding = DataBindingUtil.bind(view);
        binding.setFragment(this);
        return view;
    }

    ...

}
person Aldo Canepa    schedule 11.09.2015
comment
У меня просто ощущение, что некоторые детали отсутствуют, так как я не могу заставить это решение работать - person Tima; 13.04.2017
comment
Возможно, вам чего-то не хватает в документации по среде сборки: developer.android .com / tools / data-binding /. - person Aldo Canepa; 25.04.2017
comment
@Aldo Я считаю, что для метода onClick в XML у вас должен быть android: onClick = @ {() - ›фрагмент. buttonClicked ()} вместо этого. Также для других вы должны объявить функцию buttonClicked () внутри фрагмента и поместить свою логику внутрь. - person Hayk Nahapetyan; 28.01.2019
comment
в xml должно быть android:name="fragment" android:type="com.example.testapp.fragments.CustomFragment"/> - person COYG; 07.06.2019
comment
Я только что попробовал это, и атрибуты name и type тега variable не должны не иметь префикс android:. Возможно, есть старая версия раскладок Android, которая этого требует? - person JohnnyLambada; 05.02.2020

ButterKnife, вероятно, является лучшим решением проблемы беспорядка. Он использует обработчики аннотаций для генерации так называемого шаблонного кода «старого метода».

Но метод onClick все еще можно использовать с настраиваемым инфлятором.

Как использовать

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup cnt, Bundle state) {
    inflater = FragmentInflatorFactory.inflatorFor(inflater, this);
    return inflater.inflate(R.layout.fragment_main, cnt, false);
}

Реализация

public class FragmentInflatorFactory implements LayoutInflater.Factory {

    private static final int[] sWantedAttrs = { android.R.attr.onClick };

    private static final Method sOnCreateViewMethod;
    static {
        // We could duplicate its functionallity.. or just ignore its a protected method.
        try {
            Method method = LayoutInflater.class.getDeclaredMethod(
                    "onCreateView", String.class, AttributeSet.class);
            method.setAccessible(true);
            sOnCreateViewMethod = method;
        } catch (NoSuchMethodException e) {
            // Public API: Should not happen.
            throw new RuntimeException(e);
        }
    }

    private final LayoutInflater mInflator;
    private final Object mFragment;

    public FragmentInflatorFactory(LayoutInflater delegate, Object fragment) {
        if (delegate == null || fragment == null) {
            throw new NullPointerException();
        }
        mInflator = delegate;
        mFragment = fragment;
    }

    public static LayoutInflater inflatorFor(LayoutInflater original, Object fragment) {
        LayoutInflater inflator = original.cloneInContext(original.getContext());
        FragmentInflatorFactory factory = new FragmentInflatorFactory(inflator, fragment);
        inflator.setFactory(factory);
        return inflator;
    }

    @Override
    public View onCreateView(String name, Context context, AttributeSet attrs) {
        if ("fragment".equals(name)) {
            // Let the Activity ("private factory") handle it
            return null;
        }

        View view = null;

        if (name.indexOf('.') == -1) {
            try {
                view = (View) sOnCreateViewMethod.invoke(mInflator, name, attrs);
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            } catch (InvocationTargetException e) {
                if (e.getCause() instanceof ClassNotFoundException) {
                    return null;
                }
                throw new RuntimeException(e);
            }
        } else {
            try {
                view = mInflator.createView(name, null, attrs);
            } catch (ClassNotFoundException e) {
                return null;
            }
        }

        TypedArray a = context.obtainStyledAttributes(attrs, sWantedAttrs);
        String methodName = a.getString(0);
        a.recycle();

        if (methodName != null) {
            view.setOnClickListener(new FragmentClickListener(mFragment, methodName));
        }
        return view;
    }

    private static class FragmentClickListener implements OnClickListener {

        private final Object mFragment;
        private final String mMethodName;
        private Method mMethod;

        public FragmentClickListener(Object fragment, String methodName) {
            mFragment = fragment;
            mMethodName = methodName;
        }

        @Override
        public void onClick(View v) {
            if (mMethod == null) {
                Class<?> clazz = mFragment.getClass();
                try {
                    mMethod = clazz.getMethod(mMethodName, View.class);
                } catch (NoSuchMethodException e) {
                    throw new IllegalStateException(
                            "Cannot find public method " + mMethodName + "(View) on "
                                    + clazz + " for onClick");
                }
            }

            try {
                mMethod.invoke(mFragment, v);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new AssertionError(e);
            }
        }
    }
}
person sergio91pt    schedule 19.04.2014

Я бы предпочел обрабатывать щелчки в коде, чем использовать атрибут onClick в XML при работе с фрагментами.

Это становится еще проще при переносе ваших действий во фрагменты. Вы можете просто вызвать обработчик кликов (ранее установленный в android:onClick в XML) непосредственно из каждого case блока.

findViewById(R.id.button_login).setOnClickListener(clickListener);
...

OnClickListener clickListener = new OnClickListener() {
    @Override
    public void onClick(final View v) {
        switch(v.getId()) {
           case R.id.button_login:
              // Which is supposed to be called automatically in your
              // activity, which has now changed to a fragment.
              onLoginClick(v);
              break;

           case R.id.button_logout:
              ...
        }
    }
}

Когда дело доходит до обработки щелчков по фрагментам, мне это кажется проще, чем android:onClick.

person Ronnie    schedule 27.09.2012

Это еще один способ :

1. Создайте BaseFragment следующим образом:

public abstract class BaseFragment extends Fragment implements OnClickListener

2. использовать

public class FragmentA extends BaseFragment 

вместо того

public class FragmentA extends Fragment

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

public class MainActivity extends ActionBarActivity implements OnClickListener

а также

BaseFragment fragment = new FragmentA;

public void onClick(View v){
    fragment.onClick(v);
}

Надеюсь, это поможет.

person Euporie    schedule 23.10.2013
comment
Через 1 год, 1 месяц и 1 день после вашего ответа: есть ли какая-либо причина, кроме не повторения реализации OnClickListener для каждого класса фрагментов, для создания абстрактного BaseFragment? - person Dimitrios K.; 25.11.2014

В моем случае использования у меня есть 50 с лишним ImageView, которые мне нужно было подключить к одному методу onClick. Мое решение - перебрать представления внутри фрагмента и установить один и тот же прослушиватель onclick для каждого:

    final View.OnClickListener imageOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            chosenImage = ((ImageButton)v).getDrawable();
        }
    };

    ViewGroup root = (ViewGroup) getView().findViewById(R.id.imagesParentView);
    int childViewCount = root.getChildCount();
    for (int i=0; i < childViewCount; i++){
        View image = root.getChildAt(i);
        if (image instanceof ImageButton) {
            ((ImageButton)image).setOnClickListener(imageOnClickListener);
        }
    }
person Chris Knight    schedule 25.01.2016

Насколько я вижу ответы, они какие-то старые. Недавно Google представил намного проще DataBinding для обработки onClick или назначения в вашем xml.

Вот хороший пример, в котором вы можете увидеть, как с этим справиться:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.Handlers"/>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
           android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"
           android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/>
   </LinearLayout>
</layout>

Также есть очень хорошее руководство по DataBinding, которое вы можете найти здесь.

person Amir    schedule 03.02.2016

Вы можете определить обратный вызов как атрибут вашего XML-макета. Статья Пользовательские атрибуты XML для ваших пользовательских виджетов Android покажет вам, как это сделать для пользовательского виджета. Кредит принадлежит Кевину Диону :)

Я изучаю, могу ли я добавить стилизуемые атрибуты к базовому классу Fragment.

Основная идея состоит в том, чтобы иметь те же функции, что и View, при работе с обратным вызовом onClick.

person Nelson Ramirez    schedule 26.11.2011

Добавление к ответу Бланделла:
Если у вас больше фрагментов с большим количеством onClicks:

Деятельность:

Fragment someFragment1 = (Fragment)getFragmentManager().findFragmentByTag("someFragment1 "); 
Fragment someFragment2 = (Fragment)getFragmentManager().findFragmentByTag("someFragment2 "); 
Fragment someFragment3 = (Fragment)getFragmentManager().findFragmentByTag("someFragment3 "); 

...onCreate etc instantiating your fragments

public void myClickMethod(View v){
  if (someFragment1.isVisible()) {
       someFragment1.myClickMethod(v);
  }else if(someFragment2.isVisible()){
       someFragment2.myClickMethod(v);
  }else if(someFragment3.isVisible()){
       someFragment3.myClickMethod(v); 
  }

} 

В вашем фрагменте:

  public void myClickMethod(View v){
     switch(v.getid()){
       // Just like you were doing
     }
  } 
person amalBit    schedule 15.02.2014

Если вы зарегистрируетесь в xml с помощью android: Onclick = "", обратный вызов будет передан уважаемой Activity, в контексте которой принадлежит ваш фрагмент (getActivity ()). Если такой метод не найден в Activity, система выдаст исключение.

person Isham    schedule 19.05.2015
comment
спасибо, никто не объяснил, почему произошел сбой - person ; 19.05.2016

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

https://github.com/greenrobot/EventBus

из Github:

Оптимизированная для Android шина событий, которая упрощает взаимодействие между действиями, фрагментами, потоками, службами и т. Д. Меньше кода, лучше качество

person programmer    schedule 30.07.2015
comment
Не идеальное решение - person blueware; 19.03.2021
comment
@blueware, пожалуйста, уточнить - person programmer; 19.03.2021

Я хотел бы добавить к ответу Аджорна Линка.

Если вам нужно несколько обработчиков, вы можете просто использовать лямбда-ссылки

void onViewCreated(View view, Bundle savedInstanceState)
{
    view.setOnClickListener(this::handler);
}
void handler(View v)
{
    ...
}

Уловка здесь в том, что подпись handler метода совпадает с подписью View.OnClickListener.onClick. Таким образом, вам не понадобится View.OnClickListener интерфейс.

Кроме того, вам не понадобятся операторы switch.

К сожалению, этот метод ограничен только интерфейсами, для которых требуется один метод или лямбда.

person Dragas    schedule 03.05.2017

Хотя я обнаружил несколько хороших ответов, основанных на привязке данных, я не видел, чтобы этот подход в полной мере продвигался вперед - в смысле разрешения фрагментов при одновременном разрешении определений макета без фрагментов в XML.

Предполагая, что привязка данных включена, я могу предложить общее решение; Немного длинно, но определенно работает (с некоторыми оговорками):

Шаг 1. Пользовательская реализация OnClick

Это запустит поиск с учетом фрагментов в контекстах, связанных с выбранным представлением (например, кнопка):


// CustomOnClick.kt

@file:JvmName("CustomOnClick")

package com.example

import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import java.lang.reflect.Method

fun onClick(view: View, methodName: String) {
    resolveOnClickInvocation(view, methodName)?.invoke(view)
}

private data class OnClickInvocation(val obj: Any, val method: Method) {
    fun invoke(view: View) {
        method.invoke(obj, view)
    }
}

private fun resolveOnClickInvocation(view: View, methodName: String): OnClickInvocation? =
    searchContexts(view) { context ->
        var invocation: OnClickInvocation? = null
        if (context is Activity) {
            val activity = context as? FragmentActivity
                    ?: throw IllegalStateException("A non-FragmentActivity is not supported (looking up an onClick handler of $view)")

            invocation = getTopFragment(activity)?.let { fragment ->
                resolveInvocation(fragment, methodName)
            }?: resolveInvocation(context, methodName)
        }
        invocation
    }

private fun getTopFragment(activity: FragmentActivity): Fragment? {
    val fragments = activity.supportFragmentManager.fragments
    return if (fragments.isEmpty()) null else fragments.last()
}

private fun resolveInvocation(target: Any, methodName: String): OnClickInvocation? =
    try {
        val method = target.javaClass.getMethod(methodName, View::class.java)
        OnClickInvocation(target, method)
    } catch (e: NoSuchMethodException) {
        null
    }

private fun <T: Any> searchContexts(view: View, matcher: (context: Context) -> T?): T? {
    var context = view.context
    while (context != null && context is ContextWrapper) {
        val result = matcher(context)
        if (result == null) {
            context = context.baseContext
        } else {
            return result
        }
    }
    return null
}

Примечание: частично основано на исходной реализации Android (см. https://android.googlesource.com/platform/frameworks/base/+/a175a5b/core/java/android/view/View.java#3025)

Шаг 2: Декларативное приложение в файлах макета

Затем в XML, поддерживающем привязку данных:

<layout>
  <data>
     <import type="com.example.CustomOnClick"/>
  </data>

  <Button
    android:onClick='@{(v) -> CustomOnClick.onClick(v, "myClickMethod")}'
  </Button>
</layout>

Предостережения

  • Предполагает «современную» реализацию на основе FragmentActivity
  • Может использоваться только метод поиска «самого верхнего» (т. Е. последнего) фрагмента в стеке (хотя это можно исправить, если необходимо)
person d4vidi    schedule 02.02.2020

Это сработало для меня: (Студия Android)

 @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        View rootView = inflater.inflate(R.layout.update_credential, container, false);
        Button bt_login = (Button) rootView.findViewById(R.id.btnSend);

        bt_login.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                System.out.println("Hi its me");


            }// end onClick
        });

        return rootView;

    }// end onCreateView
person Vinod Joshi    schedule 26.11.2014
comment
Это дублирует ответ @Brill Pappin. - person naXa; 18.12.2014

Лучшее решение ИМХО:

во фрагменте:

protected void addClick(int id) {
    try {
        getView().findViewById(id).setOnClickListener(this);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void onClick(View v) {
    if (v.getId()==R.id.myButton) {
        onMyButtonClick(v);
    }
}

затем во фрагменте onViewStateRestored:

addClick(R.id.myButton);
person user4806368    schedule 19.04.2015

Ваше действие получает обратный вызов, поскольку он должен был использоваться:

mViewPagerCloth.setOnClickListener((YourActivityName)getActivity());

Если вы хотите, чтобы ваш фрагмент получал обратный вызов, сделайте следующее:

mViewPagerCloth.setOnClickListener(this);

и реализовать интерфейс onClickListener на фрагменте

person JAAD    schedule 13.04.2016

Следующее решение может быть лучшим для подражания. макет находится в fragment_my.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="listener"
            type="my_package.MyListener" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <Button
            android:id="@+id/moreTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{() -> listener.onClick()}"
            android:text="@string/login"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

И Фрагмент будет следующим

class MyFragment : Fragment(), MyListener {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
            return FragmentMyBinding.inflate(
                inflater,
                container,
                false
            ).apply {
                lifecycleOwner = viewLifecycleOwner
                listener = this@MyFragment
            }.root
    }

    override fun onClick() {
        TODO("Not yet implemented")
    }

}

interface MyListener{
    fun onClick()
}
person sadat    schedule 01.01.2021