Fragment Android Méthode du bouton onClick

91

J'essaye d'appeler la méthode dans mon onClick (View v) XML, mais ne fonctionne pas avec Fragment. Telle est l'erreur.

01-17 12:38:36.840: E/AndroidRuntime(4171): java.lang.IllegalStateException: 
Could not find a method insertIntoDb(View) in the activity class main.MainActivity 
for onClick handler on view class android.widget.Button with id 'btn_conferma'

Code Java:

public void insertIntoDb(View v) {
...
} 

XML:

<Button
        android:id="@id/btn_conferma"
        style="?android:attr/borderlessButtonStyle"
        android:layout_width="0.0dip"
        android:layout_height="45dp"
        android:layout_marginLeft="2dp"
        android:layout_weight="1.0"
        android:background="@drawable/bottoni"
        android:gravity="center_horizontal|center_vertical"
        android:onClick="insertIntoDb"
        android:text="SALVA"
        android:textColor="#ffffff"
        android:textSize="16sp" />
utilisateur3160725
la source
1
Veuillez publier plus de stacktrace et du code pertinent
Emmanuel
2
Comme indiqué ici developer.android.com/guide/topics/ui/controls/… , "L'activité hébergeant la mise en page doit alors implémenter la méthode correspondante.". Dans votre cas, vous avez implémenté la méthode correspondante dans votre fragment. Pour cette raison, il lève IllegalStateException car il n'a pas pu trouver la méthode correspondante dans l'activité. Peut-être que vous pouvez appliquer une astuce comme indiqué dans cet article stackoverflow.com/a/6271637/2515815
hcelaloner
La méthode insertIntoDb (View) doit être dans main.MainActivity et non dans la classe Fragment.
entre le
meilleure solution stackoverflow.com/questions/7570575/…
AndroidGeek

Réponses:

185

Votre activité doit avoir

public void insertIntoDb(View v) {
...
} 

pas Fragment.

Si vous ne voulez pas de l'activité ci-dessus. initialiser le bouton dans le fragment et définir l'auditeur sur le même.

<Button
    android:id="@+id/btn_conferma" // + missing

ensuite

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

   View view = inflater.inflate(R.layout.fragment_rssitem_detail,
    container, false);
   Button button = (Button) view.findViewById(R.id.btn_conferma);
   button.setOnClickListener(new OnClickListener()
   {
             @Override
             public void onClick(View v)
             {
                // do something
             } 
   }); 
   return view;
}
Raghunandan
la source
Cela provoque le blocage du fragment dans mon projet qui utilise l'API 21. D'autres correctifs / suggestions?
Darth Coder
@DarthCoder je ne pense pas. Vous devez présenter le code. Il peut ne pas être lié à ce code affiché ici
Raghunandan
@Sanu rien à voir avec lollipop pré sucette. postez une autre question avec des détails ..
Raghunandan
L'événement de clic n'est jamais déclenché lorsque je fais comme la réponse ci-dessus.
Ted
3
Je suis un peu surpris par les commentaires ci-dessus. Aucune offense, mais dire "ça ne marche pas" sans poster de détails n'est pas du tout professionnel ... Et moi, "ça marche".
zzheng
15

Une autre option peut être que votre fragment implémente View.OnClickListener et remplace onClick (View v) dans votre fragment. Si vous avez besoin que votre fragment parle à l'activité, ajoutez simplement une interface avec la ou les méthodes souhaitées et demandez à l'activité d'implémenter l'interface et de remplacer sa ou ses méthodes.

public class FragName extends Fragment implements View.OnClickListener { 
    public FragmentCommunicator fComm;
    public ImageButton res1, res2;
    int c;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_test, container, false);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            fComm = (FragmentCommunicator) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement FragmentCommunicator");
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        res1 = (ImageButton) getActivity().findViewById(R.id.responseButton1);
        res1.setOnClickListener(this);
        res2 = (ImageButton) getActivity().findViewById(R.id.responseButton2);
        res2.setOnClickListener(this);
    }
    public void onClick(final View v) { //check for what button is pressed
            switch (v.getId()) {
                case R.id.responseButton1:
                  c *= fComm.fragmentContactActivity(2);
                  break;
                case R.id.responseButton2:
                  c *= fComm.fragmentContactActivity(4);
                  break;
                default:
                  c *= fComm.fragmentContactActivity(100);
                  break;
    }
    public interface FragmentCommunicator{
        public int fragmentContactActivity(int b);
    }



public class MainActivity extends FragmentActivity implements FragName.FragmentCommunicator{
    int a = 10;
    //variable a is update by fragment. ex. use to change textview or whatever else you'd like.

    public int fragmentContactActivity(int b) {
        //update info on activity here
        a += b;
        return a;
    }
}

http://developer.android.com/training/basics/firstapp/starting-activity.html http://developer.android.com/training/basics/fragments/communicating.html

cjayem13
la source
3
Je fais ça tout le temps, mais je pense toujours que c'est moche
Alexander Farber
Je l'ai fait aussi au début mais maintenant c'est devenu presque de seconde main; De plus, cela a réellement aidé dans des applications plus complexes, mais pour quelque chose de basique, cela semble excessif.
cjayem13
@ cjayem13, pouvez-vous préciser "pourquoi est-ce excessif"?
eRaisedToX
9

Ce n'est pas un problème, c'est une conception d'Android. Voir ici :

Vous devez concevoir chaque fragment comme un composant d'activité modulaire et réutilisable. Autrement dit, étant donné que chaque fragment définit sa propre disposition et son propre comportement avec ses propres rappels de cycle de vie, vous pouvez inclure un fragment dans plusieurs activités, vous devez donc concevoir pour la réutilisation et éviter de manipuler directement un fragment à partir d'un autre fragment.

Une solution de contournement possible serait de faire quelque chose comme ceci dans votre MainActivity:

Fragment someFragment;    

...onCreate etc instantiating your fragments

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

puis dans votre classe Fragment:

public void myClickMethod(View v){
    switch(v.getid()){
       // Your code here
    }
 } 
CzarMatt
la source
4

Les autres ont déjà dit que les méthodes dans onClick sont recherchées dans les activités, pas dans les fragments. Néanmoins, si vous le voulez vraiment, il est possible.

Fondamentalement, chaque vue a une balise (probablement nulle). Nous définissons la balise de la vue racine sur le fragment qui a gonflé cette vue. Ensuite, il est facile de rechercher les parents de vue et de récupérer le fragment contenant le bouton cliqué. Maintenant, nous trouvons le nom de la méthode et utilisons la réflexion pour appeler la même méthode à partir du fragment récupéré. Facile!

dans une classe qui s'étend Fragment:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_id, container, false);

    OnClickFragments.registerTagFragment(rootView, this); // <========== !!!!!

    return rootView;
}

public void onButtonSomething(View v) {
    Log.d("~~~","~~~ MyFragment.onButtonSomething");
    // whatever
}

toutes les activités sont dérivées de la même ButtonHandlingActivity:

public class PageListActivity extends ButtonHandlingActivity

ButtonHandlingActivity.java:

public class ButtonHandlingActivity extends Activity {

    public void onButtonSomething(View v) {
        OnClickFragments.invokeFragmentButtonHandlerNoExc(v);
//or, if you want to handle exceptions:
//      try {
//          OnClickFragments.invokeFragmentButtonHandler(v);
//      } catch ...
    }

}

Il doit définir des méthodes pour tous les gestionnaires onClick xml.

com / exemple / customandroid / OnClickFragments.java:

package com.example.customandroid;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.app.Fragment;
import android.view.View;

public abstract class OnClickFragments {
    public static class FragmentHolder {
        Fragment fragment;
        public FragmentHolder(Fragment fragment) {
            this.fragment = fragment;
        }
    }
    public static Fragment getTagFragment(View view) {
        for (View v = view; v != null; v = (v.getParent() instanceof View) ? (View)v.getParent() : null) {
            Object tag = v.getTag();
            if (tag != null && tag instanceof FragmentHolder) {
                return ((FragmentHolder)tag).fragment;
            }
        }
        return null;
    }
    public static String getCallingMethodName(int callsAbove) {
        Exception e = new Exception();
        e.fillInStackTrace();
        String methodName = e.getStackTrace()[callsAbove+1].getMethodName();
        return methodName;
    }
    public static void invokeFragmentButtonHandler(View v, int callsAbove) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
        String methodName = getCallingMethodName(callsAbove+1);
        Fragment f = OnClickFragments.getTagFragment(v);
        Method m = f.getClass().getMethod(methodName, new Class[] { View.class });
        m.invoke(f, v);
    }
    public static void invokeFragmentButtonHandler(View v) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
        invokeFragmentButtonHandler(v,1);
    }
    public static void invokeFragmentButtonHandlerNoExc(View v) {
        try {
            invokeFragmentButtonHandler(v,1);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    public static void registerTagFragment(View rootView, Fragment fragment) {
        rootView.setTag(new FragmentHolder(fragment));
    }
}

Et la prochaine aventure sera l'obscurcissement progressif ...

PS

C'est bien sûr à vous de concevoir votre application pour que les données vivent dans le modèle plutôt que dans des activités ou des fragments (qui sont des contrôleurs du point de vue MVC , modèle-vue-contrôleur ). La vue est ce que vous définissez via xml, plus les classes de vue personnalisées (si vous les définissez, la plupart des gens réutilisent simplement ce qui est déjà). Une règle de base: si certaines données doivent définitivement survivre au tour de l'écran, elles appartiennent à Model .

18446744073709551615
la source
3
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.writeqrcode_main, container, false);
    // Inflate the layout for this fragment

    txt_name = (TextView) view.findViewById(R.id.name);
    txt_usranme = (TextView) view.findViewById(R.id.surname);
    txt_number = (TextView) view.findViewById(R.id.number);
    txt_province = (TextView) view.findViewById(R.id.province);
    txt_write = (EditText) view.findViewById(R.id.editText_write);
    txt_show1 = (Button) view.findViewById(R.id.buttonShow1);

    txt_show1.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.e("Onclick","Onclick");

            txt_show1.setVisibility(View.INVISIBLE);
            txt_name.setVisibility(View.VISIBLE);
            txt_usranme.setVisibility(View.VISIBLE);
            txt_number.setVisibility(View.VISIBLE);
            txt_province.setVisibility(View.VISIBLE);
        }
    });
    return view;
}

Ça va !!!!

Pong Petrung
la source
3
Pouvez-vous expliquer comment cela répond à la question? Qu'entendez-vous par votre phrase à la fin?
Teepeemm
2

Pour les utilisateurs de Kotlin:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?, 
    savedInstanceState: Bundle?) : View?
{
    // Inflate the layout for this fragment
    var myView = inflater.inflate(R.layout.fragment_home, container, false)
    var btn_test = myView.btn_test as Button
    btn_test.setOnClickListener {
        textView.text = "hunny home fragment"
    }

    return myView
}
Jayant Dhingra
la source