Android Fragment onClick button Methode


90

Ich versuche, die Methode in meinem onClick (View v) XML aufzurufen, funktioniert aber nicht mit Fragment. Dies ist der Fehler.

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'

Java-Code:

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" />

1
Bitte posten Sie mehr von der Stapelverfolgung und dem relevanten Code
Emmanuel

2
Wie hier unter developer.android.com/guide/topics/ui/controls/… angegeben , muss "die Aktivität, die das Layout hostet, dann die entsprechende Methode implementieren." In Ihrem Fall haben Sie die entsprechende Methode in Ihrem Fragment implementiert. Aus diesem Grund wird eine IllegalStateException ausgelöst, da die entsprechende Methode in der Aktivität nicht gefunden werden konnte. Vielleicht können Sie einen Trick anwenden, wie in diesem Beitrag gezeigt stackoverflow.com/a/6271637/2515815
hcelaloner

Die Methode insertIntoDb (View) muss sich in main.MainActivity und nicht in der Fragment-Klasse befinden.
Betorcs


Antworten:


184

Ihre Aktivität muss haben

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

nicht Fragment.

Wenn Sie das oben genannte nicht in Aktivität wollen. Initialisieren Sie die Schaltfläche im Fragment und setzen Sie den Listener auf den gleichen Wert.

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

Dann

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

Dies führt dazu, dass das Fragment in meinem Projekt abstürzt, das API 21 verwendet. Gibt es alternative Korrekturen / Vorschläge?
Darth Coder

@DarthCoder glaube ich nicht. Sie müssen den Code präsentieren. Es kann nicht mit diesem Code hier verwandt sein
Raghunandan

@ Sanu nichts mit Lutscher vor Lutscher zu tun. poste eine andere Frage mit Details ..
Raghunandan

Das Klickereignis wird niemals ausgelöst, wenn ich wie oben angegeben antworte.
Ted

3
Ich bin ein bisschen überrascht von den obigen Kommentaren. Nichts für ungut, aber nur zu sagen "es funktioniert nicht", ohne Details zu veröffentlichen, ist sehr unprofessionell ... Und für mich ist "es funktioniert einfach".
Zzheng

15

Eine andere Möglichkeit besteht darin, dass Ihr Fragment View.OnClickListener implementiert und onClick (View v) in Ihrem Fragment überschreibt. Wenn Ihr Fragment mit der Aktivität sprechen soll, fügen Sie einfach eine Schnittstelle mit den gewünschten Methoden hinzu, und lassen Sie die Aktivität die Schnittstelle implementieren und ihre Methode (n) überschreiben.

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


3
Ich mache das die ganze Zeit, finde es aber immer noch hässlich
Alexander Farber

Anfangs habe ich es auch getan, aber jetzt ist es fast aus zweiter Hand geworden. Außerdem hat es in komplexeren Apps geholfen, aber für etwas Grundlegendes scheint es übertrieben zu sein.
cjayem13

@ cjayem13, kannst du bitte näher erläutern, warum es ein Overkill ist?
eRaisedToX

9

Dies ist kein Problem, dies ist ein Design von Android. Siehe hier :

Sie sollten jedes Fragment als modulare und wiederverwendbare Aktivitätskomponente entwerfen. Das heißt, da jedes Fragment sein eigenes Layout und sein eigenes Verhalten mit eigenen Lebenszyklus-Rückrufen definiert, können Sie ein Fragment in mehrere Aktivitäten einbeziehen. Daher sollten Sie für die Wiederverwendung entwerfen und vermeiden, ein Fragment direkt von einem anderen Fragment aus zu manipulieren.

Eine mögliche Problemumgehung wäre, in Ihrer Hauptaktivität Folgendes zu tun:

Fragment someFragment;    

...onCreate etc instantiating your fragments

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

und dann in Ihrer Fragmentklasse:

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

4

Die anderen haben bereits gesagt, dass Methoden in onClick in Aktivitäten und nicht in Fragmenten gesucht werden. Dennoch, wenn Sie es wirklich wollen, es ist möglich.

Grundsätzlich hat jede Ansicht ein Tag (wahrscheinlich null). Wir setzen das Tag der Stammansicht auf das Fragment, das diese Ansicht aufgeblasen hat. Anschließend können Sie ganz einfach die übergeordneten Elemente der Ansicht durchsuchen und das Fragment mit der angeklickten Schaltfläche abrufen. Jetzt ermitteln wir den Methodennamen und verwenden Reflection, um dieselbe Methode aus dem abgerufenen Fragment aufzurufen. Einfach!

in einer Klasse, die erweitert 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
}

Alle Aktivitäten werden von derselben ButtonHandlingActivity abgeleitet:

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

}

Es müssen Methoden für alle xml onClick-Handler definiert werden.

com / example / 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));
    }
}

Und das nächste Abenteuer wird die Verschleierung sein ...

PS

Es ist natürlich an Sie , Ihre Anwendung so zu gestalten, dass die Daten im leben Modell anstatt in Aktivitäten oder Fragmenten (die Controller aus dem MVC , Model-View-Controller - Sicht). Die Ansicht ist das, was Sie über XML definieren, sowie die benutzerdefinierten Ansichtsklassen (wenn Sie sie definieren, verwenden die meisten Benutzer nur das, was bereits vorhanden ist). Eine Faustregel: Wenn einige Daten die Bildschirmumdrehung definitiv überleben müssen, gehören sie zum Modell .


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

Du bist ok !!!!


3
Können Sie erklären, wie dies die Frage beantwortet? Was meinst du mit deinem Satz am Ende?
Teepeemm

2

Für Kotlin-Benutzer:

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
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.