Фрагмент Android onClick Метод


90

Я намагаюся викликати метод у своєму 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" />

1
Будь ласка, опублікуйте більше стек-трасу та відповідного коду
Еммануель

2
Як зазначено тут developer.android.com/guide/topics/ui/controls/… , "Діяльність, що розміщує макет, повинна потім реалізувати відповідний метод.". У вашому випадку ви застосували відповідний метод у своєму фрагменті. Через це він видає IllegalStateException, оскільки не може знайти відповідний метод у діяльності. Може бути, ви можете застосувати трюк , як показано на це повідомлення stackoverflow.com/a/6271637/2515815
hcelaloner

Метод insertIntoDb (View) повинен бути в main.MainActivity, а не в класі Fragment.
betorcs


Відповіді:


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. Будь-які альтернативні виправлення / пропозиції?
Дарт Кодер

@DarthCoder, я не думаю. Вам потрібно пред'явити код. Можливо, це не пов’язано з цим кодом, розміщеним тут
Рагхундандан,

@ Сану нічого спільного з льодяником перед льодяником. опублікувати ще одне запитання з подробицями ..
Рагхундан

Подія натискання ніколи не запускається, коли я роблю відповідь вище.
Тед,

3
Я трохи здивований коментарями вище. Без образ, але просто сказати "це не працює", не розміщуючи жодних деталей, дуже непрофесійно ... А як на мене, "це просто працює".
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


3
Я роблю це постійно, але все одно вважаю, що це потворно
Олександр Фарбер

Я теж спочатку, але зараз це стало майже секонд-хендом; плюс це насправді допомогло в більш складних додатках, але для чогось базового це здається надмірним.
cjayem13

@ cjayem13, ти можеш пояснити, "чому це надмірне"?
eRaisedToX

9

Це не проблема, це дизайн Android. Дивіться тут :

Ви повинні спроектувати кожен фрагмент як модульний компонент активності, що використовується багаторазово. Тобто, оскільки кожен фрагмент визначає свій власний макет та свою поведінку зі своїми зворотними викликами життєвого циклу, ви можете включати один фрагмент у декілька дій, тому вам слід спроектувати для повторного використання та уникати безпосереднього маніпулювання одним фрагментом з іншого фрагмента.

Можливим обхідним шляхом було б зробити щось подібне у своїй MainActivity:

Fragment someFragment;    

...onCreate etc instantiating your fragments

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

а потім у класі Fragment:

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

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.

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

І наступною пригодою буде затухання прогуарда ...

PS

Звичайно, від вас залежить, як розробити свою програму таким чином, щоб дані надходили в модель, а не в дії або фрагменти (які є контролерами з точки зору MVC , Model-View-Controller ). View є те , що можна визначити з допомогою XML, плюс призначені для користувача класи уявлень (якщо ви визначили їх, більшість людей просто використовувати то , що вже є). Емпіричне правило: якщо деякі дані неодмінно повинні пережити поворот екрана, вони належать до моделі .


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

2

Для користувачів 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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.