Método del botón onClick de Fragmento de Android

91

Estoy tratando de invocar el método en mi onClick (Ver v) XML, pero no funciona con Fragment. Este es el error.

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'

Código 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" />
usuario3160725
fuente
1
Publique más información sobre stacktrace y código relevante
Emmanuel
2
Como se indica aquí developer.android.com/guide/topics/ui/controls/… , "La actividad que aloja el diseño debe implementar el método correspondiente". En su caso, implementó el método correspondiente en su fragmento. Debido a esto, lanza IllegalStateException porque no pudo encontrar el método correspondiente en la actividad. Tal vez, puede aplicar un truco como se muestra en esta publicación stackoverflow.com/a/6271637/2515815
hcelaloner
El método insertIntoDb (View) debe estar en main.MainActivity y no en la clase Fragment.
betorcs
la mejor solución stackoverflow.com/questions/7570575/…
AndroidGeek

Respuestas:

185

Tu actividad debe tener

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

no Fragmento.

Si no quiere lo anterior en actividad. Inicializar el botón en el fragmento y configurar el oyente al mismo.

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

Luego

@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
fuente
Esto hace que el fragmento se bloquee en mi proyecto que usa API 21. ¿Alguna solución / sugerencia alternativa?
Darth Coder
@DarthCoder, no lo creo. Debes presentar el código. Puede que no esté relacionado con este código publicado aquí
Raghunandan
@Sanu nada que ver con piruleta pre piruleta. publicar otra pregunta con detalles ..
Raghunandan
El evento de clic nunca se activa cuando hago la respuesta anterior.
Ted
3
Estoy un poco sorprendido por los comentarios anteriores. No te ofendas, pero simplemente decir "no funciona" sin publicar ningún detalle es muy poco profesional ... Y en cuanto a mí, "simplemente funciona".
zzheng
15

Otra opción puede ser que su fragmento implemente View.OnClickListener y anule onClick (Ver v) dentro de su fragmento. Si necesita que su fragmento hable con la actividad, simplemente agregue una interfaz con los métodos deseados y haga que la actividad implemente la interfaz y anule su (s) método (s).

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
fuente
3
Hago esto todo el tiempo, pero sigo pensando que es feo
Alexander Farber
Yo también lo hice al principio, pero ahora se ha vuelto casi de segunda mano; Además, en realidad ayudó en aplicaciones más complejas, pero para algo básico parece exagerado.
cjayem13
@ cjayem13, ¿podría explicar "por qué es una exageración"?
eRaisedToX
9

Esto no es un problema, es un diseño de Android. Ver aquí :

Debe diseñar cada fragmento como un componente de actividad modular y reutilizable. Es decir, debido a que cada fragmento define su propio diseño y su propio comportamiento con sus propias devoluciones de llamada del ciclo de vida, puede incluir un fragmento en múltiples actividades, por lo que debe diseñar para su reutilización y evitar manipular directamente un fragmento de otro fragmento.

Una posible solución alternativa sería hacer algo como esto en su MainActivity:

Fragment someFragment;    

...onCreate etc instantiating your fragments

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

y luego en tu clase Fragmento:

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

Los otros ya han dicho que los métodos en onClick se buscan en actividades, no en fragmentos. Sin embargo, si realmente lo desea, es posible.

Básicamente, cada vista tiene una etiqueta (probablemente nula). Establecemos la etiqueta de la vista raíz en el fragmento que infló esa vista. Entonces, es fácil buscar los padres de la vista y recuperar el fragmento que contiene el botón en el que se hizo clic. Ahora, averiguamos el nombre del método y usamos la reflexión para llamar al mismo método desde el fragmento recuperado. ¡Fácil!

en una clase que se extiende 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
}

todas las actividades se derivan de la misma 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 ...
    }

}

Tiene que definir métodos para todos los manejadores xml onClick.

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

Y la próxima aventura será una ofuscación proguard ...

PD

Por supuesto, depende de usted diseñar su aplicación para que los datos vivan en el Modelo en lugar de en Actividades o Fragmentos (que son Controladores desde el punto de vista MVC , Modelo-Vista-Controlador ). La Vista es lo que define a través de xml, más las clases de vista personalizadas (si las define, la mayoría de las personas simplemente reutilizan lo que ya es). Una regla general: si algunos datos definitivamente deben sobrevivir al cambio de pantalla, pertenecen a Model .

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

Estas bien !!!!

Pong Petrung
fuente
3
¿Podría explicar cómo responde esto a la pregunta? ¿Qué quieres decir con tu oración al final?
Teepeemm
2

Para usuarios 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
fuente