Android Fragment onClick button Method


91

Tôi đang cố gọi phương thức trong XML onClick (View v) của mình, nhưng không hoạt động với Fragment. Đây là lỗi.

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'

Mã 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
Xin vui lòng gửi nhiều stacktrace và mã liên quan
Emmanuel

2
Như đã nêu trong đây developer.android.com/guide/topics/ui/controls/… , "Activity lưu trữ bố cục sau đó phải triển khai phương thức tương ứng.". Trong trường hợp của bạn, bạn đã triển khai phương pháp tương ứng trong phân đoạn của mình. Do đó, nó ném IllegalStateException vì nó không thể tìm thấy phương thức tương ứng trong hoạt động. Có lẽ, bạn có thể áp dụng một thủ thuật như trong này bài stackoverflow.com/a/6271637/2515815
hcelaloner

Phương thức insertIntoDb (View) phải nằm trong main.MainActivity chứ không phải trong lớp Fragment.
betorcs


Câu trả lời:


185

Hoạt động của bạn phải có

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

không phải Fragment.

Nếu bạn không muốn những điều trên trong hoạt động. khởi tạo nút trong phân đoạn và đặt trình nghe giống nhau.

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

Sau đó

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

Điều này khiến phân đoạn bị lỗi trong dự án của tôi sử dụng API 21. Có bất kỳ bản sửa lỗi / đề xuất thay thế nào không?
Darth Coder

@DarthCoder tôi không nghĩ vậy. Bạn cần xuất trình mã. Nó có thể không liên quan đến mã này được đăng ở đây
Raghunandan

@Sanu không liên quan gì đến kẹo mút trước kẹo que. đăng một câu hỏi khác kèm theo chi tiết ..
Raghunandan

Sự kiện nhấp chuột không bao giờ được kích hoạt khi tôi làm như câu trả lời ở trên.
Ted vào

3
Tôi hơi bất ngờ trước những bình luận trên. Không xúc phạm, nhưng chỉ nói "nó không hoạt động" mà không đăng bất kỳ chi tiết nào là rất thiếu chuyên nghiệp ... Và đối với tôi, "nó chỉ hoạt động".
zzheng

15

Một tùy chọn khác có thể là để phân đoạn của bạn triển khai View.OnClickListener và ghi đè onClick (View v) trong phân đoạn của bạn. Nếu bạn cần phân đoạn của mình nói chuyện với hoạt động, chỉ cần thêm một giao diện với (các) phương thức mong muốn và yêu cầu hoạt động triển khai giao diện và ghi đè (các) phương thức của nó.

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/fragment/communicating.html


3
Tôi làm điều này tất cả các thời gian, nhưng vẫn nghĩ rằng nó xấu xí
Alexander Farber

Lúc đầu tôi cũng làm vậy nhưng bây giờ nó gần như trở thành đồ cũ; cộng với nó thực sự đã giúp ích trong các ứng dụng phức tạp hơn nhưng đối với một số thứ cơ bản, nó dường như là quá mức cần thiết.
cjayem13

@ cjayem13, bạn có thể vui lòng nói rõ hơn "tại sao nó là một quá mức cần thiết"?
eRaisedToX

9

Đây không phải là một vấn đề, đây là một thiết kế của Android. Xem tại đây :

Bạn nên thiết kế mỗi phân đoạn như một thành phần hoạt động mô-đun và có thể tái sử dụng. Đó là, bởi vì mỗi phân đoạn xác định bố cục riêng và hành vi riêng của nó với các lệnh gọi lại vòng đời của riêng nó, bạn có thể bao gồm một phân đoạn trong nhiều hoạt động, vì vậy bạn nên thiết kế để sử dụng lại và tránh thao tác trực tiếp một phân đoạn này từ một phân đoạn khác.

Một giải pháp khả thi là thực hiện điều gì đó như sau trong MainActivity của bạn:

Fragment someFragment;    

...onCreate etc instantiating your fragments

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

và sau đó trong lớp Fragment của bạn:

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

4

Những người khác đã nói rằng các phương thức trong onClick được tìm kiếm trong các hoạt động, không phải các đoạn. Tuy nhiên, nếu bạn thực sự muốn nó, nó có thể.

Về cơ bản, mỗi chế độ xem có một thẻ (có thể là null). Chúng tôi đặt thẻ của chế độ xem gốc thành phân đoạn làm tăng chế độ xem đó. Sau đó, có thể dễ dàng tìm kiếm chế độ xem và truy xuất đoạn có chứa nút đã nhấp. Bây giờ, chúng ta tìm ra tên phương thức và sử dụng sự phản chiếu để gọi phương thức tương tự từ đoạn được truy xuất. Dễ dàng!

trong một lớp mở rộng 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
}

tất cả các hoạt động đều bắt nguồn từ cùng một 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 ...
    }

}

Nó phải xác định các phương thức cho tất cả các trình xử lý xml onClick.

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

Và cuộc phiêu lưu tiếp theo sẽ là sự che đậy ...

PS

Tất nhiên bạn phải thiết kế ứng dụng của mình sao cho dữ liệu nằm trong Mô hình chứ không phải trong Hoạt động hoặc Phân đoạn (là Bộ điều khiển theo quan điểm MVC , Model-View-Controller ). Các Xem là những gì bạn xác định thông qua xml, cộng với các lớp giao diện tùy chỉnh (nếu bạn định nghĩa chúng, hầu hết mọi người chỉ tái sử dụng những gì đã có). Một quy tắc chung: nếu một số dữ liệu chắc chắn phải tồn tại khi lật màn hình, chúng thuộc về Model .


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

Bạn ổn không !!!!


3
Bạn có thể giải thích làm thế nào điều này trả lời câu hỏi? Bạn có ý gì trong câu nói của bạn ở cuối?
Teepeemm

2

Đối với người dùng 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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.