Método do botão onClick do fragmento do Android

91

Estou tentando invocar o método no meu onClick (View v) XML, mas não funciona com Fragment. Esse é o erro.

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" />
user3160725
fonte
1
Por favor, poste mais sobre o stacktrace e o código relevante
Emmanuel
2
Conforme declarado aqui developer.android.com/guide/topics/ui/controls/… , "A atividade que hospeda o layout deve então implementar o método correspondente.". No seu caso, você implementou o método correspondente em seu fragmento. Por causa disso, ele lança IllegalStateException porque não conseguiu encontrar o método correspondente na atividade. Talvez você possa aplicar um truque conforme mostrado nesta postagem stackoverflow.com/a/6271637/2515815
hcelaloner
O método insertIntoDb (View) deve estar em main.MainActivity e não na classe Fragment.
betorcs
melhor solução stackoverflow.com/questions/7570575/…
AndroidGeek

Respostas:

185

Sua atividade deve ter

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

não fragmento.

Se você não quer o acima em atividade. inicializar o botão no fragmento e definir o ouvinte para o mesmo.

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

Então

@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
fonte
Isso faz com que o fragmento trave em meu projeto que usa API 21. Alguma correção / sugestão alternativa?
Darth Coder
@DarthCoder acho que não. Você precisa apresentar o código. Ele pode não estar relacionado a este código postado aqui
Raghunandan
@Sanu nada a ver com pirulito pré-pirulito. poste outra pergunta com detalhes ..
Raghunandan
O evento de clique nunca é acionado quando eu faço conforme a resposta acima.
Ted de
3
Estou um pouco surpreso com os comentários acima. Sem ofensa, mas apenas dizer "não funciona" sem postar nenhum detalhe é muito pouco profissional ... E quanto a mim, "simplesmente funciona".
zzheng
15

Outra opção pode ser fazer com que seu fragmento implemente View.OnClickListener e substitua onClick (View v) em seu fragmento. Se você precisar que seu fragmento converse com a atividade, basta adicionar uma interface com os métodos desejados e fazer com que a atividade implemente a interface e substitua seu (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
fonte
3
Eu faço isso o tempo todo, mas ainda acho que é feio
Alexander Farber
Eu também fiz no começo, mas agora é quase uma segunda mão; além disso, na verdade ajudou em aplicativos mais complexos, mas para algo básico parece ser um exagero.
cjayem13
@ cjayem13, você pode explicar "por que isso é um exagero"?
eRaisedToX
9

Isso não é um problema, este é um design do Android. Veja aqui :

Você deve projetar cada fragmento como um componente de atividade modular e reutilizável. Ou seja, como cada fragmento define seu próprio layout e seu próprio comportamento com seus próprios retornos de chamada de ciclo de vida, você pode incluir um fragmento em várias atividades, portanto, deve projetar para reutilização e evitar manipular diretamente um fragmento de outro fragmento.

Uma possível solução alternativa seria fazer algo assim em sua MainActivity:

Fragment someFragment;    

...onCreate etc instantiating your fragments

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

e então em sua classe Fragment:

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

Os outros já disseram que os métodos em onClick são pesquisados ​​em atividades, não em fragmentos. No entanto, se você realmente quiser, é possível.

Basicamente, cada visualização possui uma tag (provavelmente nula). Definimos a marca da visão raiz para o fragmento que inflou essa visão. Então, é fácil pesquisar os pais da vista e recuperar o fragmento que contém o botão clicado. Agora, descobrimos o nome do método e usamos reflexão para chamar o mesmo método do fragmento recuperado. Fácil!

em uma classe que estende 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 as atividades são derivadas do mesmo 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 ...
    }

}

Ele tem que definir métodos para todos os manipuladores 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));
    }
}

E a próxima aventura será a ofuscação progressiva ...

PS

Obviamente, depende de você projetar seu aplicativo de forma que os dados fiquem no modelo em vez de em atividades ou fragmentos (que são controladores do ponto de vista MVC , modelo-visão-controlador ). A visão é o que você define via xml, mais as classes de visão customizadas (se você as definir, a maioria das pessoas apenas reutiliza o que já existe). Uma regra prática: se alguns dados definitivamente devem sobreviver à virada da tela, eles pertencem ao Modelo .

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

Você está bem !!!!

Pong Petrung
fonte
3
Você poderia explicar como isso responde à pergunta? O que você quer dizer com sua frase no final?
Teepeemm
2

Para usuários 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
fonte