Comment gérer les clics de bouton à l’aide du XML onClick dans Fragments

Pré-Honeycomb (Android 3), chaque activité a été enregistrée pour gérer les clics de bouton via la balise onClick dans un XML de mise en page:

 android:onClick="myClickMethod" 

Dans cette méthode, vous pouvez utiliser view.getId() et une instruction switch pour faire la logique du bouton.

Avec l’introduction de Honeycomb, je divise ces activités en fragments qui peuvent être réutilisés dans de nombreuses activités. La plupart du comportement des boutons est indépendant de l’activité, et j’aimerais que le code réside dans le fichier Fragments sans utiliser l’ancienne méthode (antérieure à 1.6) pour enregistrer OnClickListener pour chaque bouton.

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

Le problème est que lorsque ma mise en page est gonflée, c’est toujours l’activité d’hébergement qui reçoit les clics, pas les fragments individuels. Y a-t-il une bonne approche à l’un ou l’autre

  • Enregistrez le fragment pour recevoir les clics sur le bouton?
  • Transmettez les événements de clic de l’activité au fragment auquel ils appartiennent?

Vous pourriez juste faire ceci:

Activité:

 Fragment someFragment; //...onCreate etc instantiating your fragments public void myClickMethod(View v) { someFragment.myClickMethod(v); } 

Fragment:

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

En réponse à @Ameen qui souhaitait moins de couplage, les fragments sont réutilisables

Interface:

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

Activité:

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

Fragment:

 public class SomeFragment implements XmlClickable { //...onCreateView, etc. @Override public void myClickMethod(View v) { switch(v.getId()){ // Just like you were doing } } 

Je préfère utiliser la solution suivante pour gérer les événements onClick. Cela fonctionne également pour l’activité et les 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; } } } 

Le problème, à mon avis, est que la vue rest l’activité, pas le fragment. Le fragment n’a aucune vue indépendante et est associé à la vue des activités parentes. C’est pourquoi l’événement se termine dans l’activité, pas le fragment. C’est malheureux, mais je pense que vous aurez besoin de code pour que cela fonctionne.

Ce que j’ai fait pendant les conversions est simplement d’append un écouteur de clic qui appelle l’ancien gestionnaire d’événement.

par exemple:

 final Button loginButton = (Button) view.findViewById(R.id.loginButton); loginButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { onLoginClicked(v); } }); 

J’ai récemment résolu ce problème sans avoir à append une méthode à l’activité de contexte ou à implémenter OnClickListener. Je ne suis pas sûr que ce soit une solution “valide” non plus, mais cela fonctionne.

Basé sur: https://developer.android.com/tools/data-binding/guide.html#binding_events

Cela peut être fait avec des liaisons de données: ajoutez simplement votre instance de fragment en tant que variable, puis vous pouvez lier toute méthode avec onClick.

         

Et le code de liaison de fragment serait …

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

ButterKnife est probablement la meilleure solution pour le problème de fouillis. Il utilise des processeurs d’annotation pour générer le code dit “méthode ancienne”.

Mais la méthode onClick peut toujours être utilisée, avec un gonfleur personnalisé.

Comment utiliser

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

la mise en oeuvre

 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", Ssortingng.class, AtsortingbuteSet.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(Ssortingng name, Context context, AtsortingbuteSet 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.obtainStyledAtsortingbutes(attrs, sWantedAttrs); Ssortingng methodName = a.getSsortingng(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 Ssortingng mMethodName; private Method mMethod; public FragmentClickListener(Object fragment, Ssortingng 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); } } } } 

Je préfère opter pour la gestion des clics dans le code plutôt que d’utiliser l’atsortingbut onClick en XML lorsque vous travaillez avec des fragments.

Cela devient encore plus facile lors de la migration de vos activités vers des fragments. Vous pouvez simplement appeler le gestionnaire de clic (précédemment défini sur android:onClick in XML) directement depuis chaque bloc de 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: ... } } } 

Quand il s’agit de gérer les clics dans les fragments, cela me semble plus simple android:onClick .

Ceci est une autre façon:

1.Créez un fragment de base comme ceci:

 public abstract class BaseFragment extends Fragment implements OnClickListener 

2.Utiliser

 public class FragmentA extends BaseFragment 

au lieu de

 public class FragmentA extends Fragment 

3.Dans votre activité:

 public class MainActivity extends ActionBarActivity implements OnClickListener 

et

 BaseFragment fragment = new FragmentA; public void onClick(View v){ fragment.onClick(v); } 

J’espère que cela aide.

Vous pouvez définir un rappel comme atsortingbut de votre disposition XML. L’article Atsortingbuts XML personnalisés pour vos widgets Android personnalisés vous montrera comment procéder pour un widget personnalisé. Le crédit va à Kevin Dion 🙂

Je cherche à savoir si je peux append des atsortingbuts de style à la classe Fragment de base.

L’idée de base est d’avoir la même fonctionnalité que View implémente lors du traitement du rappel onClick.

Dans mon cas d’utilisation, j’ai 50 bizarres ImageViews dont je devais disposer dans une seule méthode onClick. Ma solution consiste à parcourir les vues à l’intérieur du fragment et à définir le même écouteur onclick sur chacun:

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

Ajoutant à la réponse de Blundell,
Si vous avez plus de fragments, avec beaucoup de onClicks:

Activité:

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

Dans votre fragment:

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

Si vous vous enregistrez dans xml en utilisant Android: Onclick = “”, le rappel sera donné à l’activité respectée sous le contexte de laquelle votre fragment appartient (getActivity ()). Si une telle méthode est introuvable dans l’activité, le système lancera une exception.

Vous pouvez envisager d’utiliser EventBus pour les événements découplés. Vous pouvez écouter les événements très facilement. Vous pouvez également vous assurer que l’événement est reçu sur le thread ui (au lieu d’appeler runOnUiThread .. pour vous-même pour chaque abonnement à un événement)

https://github.com/greenrobot/EventBus

de Github:

Bus d’événement optimisé pour Android qui simplifie la communication entre les activités, les fragments, les threads, les services, etc. Moins de code, meilleure qualité

Comme je vois les réponses, ils sont en quelque sorte vieux. Récemment, Google a introduit DataBinding qui est beaucoup plus facile à manipuler sur onClick ou à assigner dans votre XML.

Voici un bon exemple que vous pouvez voir comment gérer ceci:

            

Il y a aussi un très bon tutoriel sur DataBinding que vous pouvez trouver ici .

J’aimerais append à la réponse d’ Adjorn Linkz.

Si vous avez besoin de plusieurs gestionnaires, vous pouvez simplement utiliser des références lambda

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

L’astuce ici est que la signature de cette méthode de handler la signature View.OnClickListener.onClick . De cette façon, vous n’aurez plus besoin de l’interface View.OnClickListener .

De plus, vous n’aurez pas besoin d’instructions de commutateur.

Malheureusement, cette méthode est limitée aux interfaces nécessitant une seule méthode, ou une lambda.

Cela a fonctionné pour moi: (studio 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 

Meilleure solution à mon humble avis:

en fragment:

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

puis dans onViewStateRestored de Fragment:

 addClick(R.id.myButton); 

Votre activité reçoit le rappel comme vous avez dû utiliser:

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

Si vous souhaitez que votre fragment reçoive un rappel, procédez comme suit:

 mViewPagerCloth.setOnClickListener(this); 

et implémenter l’interface onClickListener sur Fragment