Android Fragment onClick button Method

91

Я пытаюсь вызвать метод в моем XML onClick (View v), но не работает с фрагментом. Это ошибка.

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:

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
источник
1
Пожалуйста, опубликуйте больше информации о stacktrace и соответствующем коде
Эммануэль
2
Как указано здесь developer.android.com/guide/topics/ui/controls/… , «Действие, на котором размещен макет, должно затем реализовать соответствующий метод». В вашем случае вы реализовали соответствующий метод в своем фрагменте. Из-за этого он генерирует исключение IllegalStateException, поскольку не может найти соответствующий метод в действии. Возможно, вы можете применить трюк, как показано в этом сообщении stackoverflow.com/a/6271637/2515815
hcelaloner
Метод insertIntoDb (View) должен находиться в main.MainActivity, а не в классе Fragment.
betorcs
лучшее решение stackoverflow.com/questions/7570575/…
AndroidGeek

Ответы:

185

Ваша деятельность должна иметь

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

не фрагмент.

Если вы не хотите, чтобы это действовало. инициализировать кнопку во фрагменте и установить такой же слушатель.

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

затем

@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;
}
Рагхунандан
источник
Это приводит к сбою фрагмента в моем проекте, который использует API 21. Какие-либо альтернативные исправления / предложения?
Darth Coder
@DarthCoder, я так не думаю. Вам необходимо предоставить код. Возможно, это не связано с этим кодом, размещенным здесь
Raghunandan
@Sanu не имеет ничего общего с леденцом перед леденцом. отправьте еще один вопрос с подробностями ..
Raghunandan
Событие щелчка никогда не запускается, когда я делаю так, как указано выше.
Ted
4
Я немного удивлен комментариями выше. Без обид, но просто сказать «это не работает», не опубликовав никаких деталей, очень непрофессионально ... А как по мне, «это просто работает».
zzheng
15

Другой вариант может заключаться в том, чтобы ваш фрагмент реализовал View.OnClickListener и переопределил onClick (View v) внутри вашего фрагмента. Если вам нужно, чтобы ваш фрагмент разговаривал с действием, просто добавьте интерфейс с желаемым методом (ами) и пусть действие реализует интерфейс и переопределит его метод (ы).

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
источник
3
Я делаю это все время, но все же считаю, что это некрасиво
Александр Фарбер
Сначала я тоже так делал, но теперь это стало почти подержанным; плюс это действительно помогло в более сложных приложениях, но для чего-то простого кажется излишним.
cjayem13
@ cjayem13, не могли бы вы уточнить, "почему это излишество"?
eRaisedToX
9

Это не проблема, это дизайн Android. Смотрите здесь :

Каждый фрагмент следует проектировать как модульный и многократно используемый компонент деятельности. То есть, поскольку каждый фрагмент определяет свой собственный макет и собственное поведение со своими собственными обратными вызовами жизненного цикла, вы можете включить один фрагмент в несколько действий, поэтому вам следует разрабатывать дизайн для повторного использования и избегать прямого манипулирования одним фрагментом из другого фрагмента.

Возможный обходной путь - сделать что-то вроде этого в вашем MainActivity:

Fragment someFragment;    

...onCreate etc instantiating your fragments

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

а затем в вашем классе фрагментов:

public void myClickMethod(View v){
    switch(v.getid()){
       // Your code here
    }
 } 
CzarMatt
источник
4

Другие уже сказали, что методы в onClick ищутся в действиях, а не во фрагментах. Тем не менее, если вы действительно хотите, то это возможно.

По сути, каждое представление имеет тег (возможно, нулевой). Мы устанавливаем тег корневого представления для фрагмента, который раздувает это представление. Затем можно легко выполнить поиск в родительских представлениях и получить фрагмент, содержащий нажатую кнопку. Теперь мы узнаем имя метода и используем отражение для вызова того же метода из полученного фрагмента. Легко!

в классе, который расширяет 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
}

все действия являются производными от одного и того же 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 ...
    }

}

Он должен определять методы для всех обработчиков xml onClick.

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

А следующим приключением будет обфускация прогарда ...

PS

Конечно, вы должны спроектировать свое приложение так, чтобы данные находились в модели, а не в действиях или фрагментах (которые являются контроллерами с точки зрения MVC , модели-представления-контроллера ). View является то , что можно определить с помощью XML, плюс пользовательские классы представлений (если вы определили их, большинство людей просто использовать то , что уже есть). Эмпирическое правило: если некоторые данные обязательно должны пережить поворот экрана, они принадлежат модели .

18446744073709551615
источник
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;
}

Ты в порядке !!!!

Понг Петрунг
источник
3
Не могли бы вы объяснить, как это отвечает на вопрос? Что вы подразумеваете под своим предложением в конце?
Teepeemm
3

Для пользователей 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
}
Джаянт Дхингра
источник