Chính xác thì thuộc tính android: onClick XML khác với setOnClickListener như thế nào?


416

Từ đó tôi đã đọc, bạn có thể gán một onClicktrình xử lý cho một nút theo hai cách.

Sử dụng android:onClickthuộc tính XML trong đó bạn chỉ cần sử dụng tên của một phương thức công khai có chữ ký void name(View v)hoặc bằng cách sử dụng setOnClickListenerphương thức mà bạn vượt qua một đối tượng thực hiện OnClickListenergiao diện. Loại thứ hai thường yêu cầu một lớp ẩn danh mà cá nhân tôi không thích (sở thích cá nhân) hoặc xác định một lớp bên trong thực hiện OnClickListener.

Bằng cách sử dụng thuộc tính XML, bạn chỉ cần xác định một phương thức thay vì một lớp, vì vậy tôi đã tự hỏi liệu điều tương tự có thể được thực hiện thông qua mã chứ không phải trong bố cục XML.


4
Tôi đọc vấn đề của bạn và tôi nghĩ rằng bạn đang bị mắc kẹt ở cùng một nơi giống như tôi. Tôi đã xem một video rất hay giúp tôi rất nhiều trong việc giải quyết vấn đề của mình. Tìm video trên liên kết sau: youtube.com/watch?v=MtmHURWKCmg&feature=youtu.be Tôi hy vọng điều này cũng sẽ giúp bạn :)
user2166292 13/03/13

9
Đối với những người muốn tiết kiệm thời gian xem video được đăng trong nhận xét ở trên, nó chỉ đơn giản chỉ ra cách hai nút có thể có cùng một phương thức cho onClickthuộc tính của nó trong tệp bố cục. Điều này được thực hiện nhờ vào tham số View v. Bạn chỉ cần kiểm tra if (v == findViewById(R.id.button1)) vv ..
CodyBugstein

13
@Imray Tôi nghĩ sẽ tốt hơn khi sử dụng v.getId() == R.id.button1, vì bạn không phải tìm kiểm soát thực tế và so sánh. Và bạn có thể sử dụng switchthay vì rất nhiều ifs.
Sami Kuhmonen

3
Hướng dẫn này sẽ giúp bạn rất nhiều. bấm vào đây
c49

Sử dụng xml android: onClick gây ra sự cố.

Câu trả lời:


604

Không, điều đó là không thể thông qua mã. Android chỉ thực hiện OnClickListenercho bạn khi bạn xác định android:onClick="someMethod"thuộc tính.

Hai đoạn mã đó bằng nhau, chỉ được thực hiện theo hai cách khác nhau.

Thực hiện mã

Button btn = (Button) findViewById(R.id.mybutton);

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        myFancyMethod(v);
    }
});

// some more code

public void myFancyMethod(View v) {
    // does something very interesting
}

Trên đây là một mã thực hiện của một OnClickListener. Và đây là triển khai XML.

Triển khai XML

<?xml version="1.0" encoding="utf-8"?>
<!-- layout elements -->
<Button android:id="@+id/mybutton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Click me!"
    android:onClick="myFancyMethod" />
<!-- even more layout elements -->

Trong nền, Android không làm gì khác ngoài mã Java, gọi phương thức của bạn trong một sự kiện nhấp chuột.

Lưu ý rằng với XML ở trên, Android sẽ chỉ tìm onClickphương thức myFancyMethod()trong Hoạt động hiện tại. Điều này rất quan trọng để nhớ nếu bạn đang sử dụng các đoạn, vì ngay cả khi bạn thêm XML ở trên bằng cách sử dụng một đoạn, Android sẽ không tìm kiếm onClickphương thức trong .javatệp của đoạn được sử dụng để thêm XML.

Một điều quan trọng tôi nhận thấy. Bạn đã đề cập rằng bạn không thích các phương pháp ẩn danh . Bạn muốn nói rằng bạn không thích các lớp ẩn danh .


4
Tôi không phải là một chuyên gia về Java nhưng vâng, ý tôi là lớp ẩn danh. Cảm ơn vì đã trả lời. Rất rõ ràng.
emitrax

118
Lưu ý rằng nếu bạn sử dụng onclick XML, bạn phải đặt phương thức onclick ( myFancyMethod()) trong Hoạt động hiện tại. Điều này rất quan trọng nếu bạn đang sử dụng các đoạn, vì cách cài đặt chương trình của người nghe onclick có thể sẽ có các phương thức xử lý các nhấp chuột trong một đoạn của onCreateView () ... nơi mà nó sẽ không được tìm thấy nếu được đề cập từ XML.
Peter Ajtai

12
Vâng, phương pháp phải được công khai.
Octavian A. Damiean

12
Điều thú vị là việc thực hiện nó trong mã cho phép một người che chắn truy cập phương thức bằng cách đặt phương thức này ở chế độ riêng tư, trong khi thực hiện theo cách xml dẫn đến tiếp xúc với phương thức.
bgse

5
Thực tế là hàm (theo cách tiếp cận XML) phải có trong Activity rất quan trọng không chỉ khi bạn xem xét các đoạn, mà cả các khung nhìn tùy chỉnh (có chứa nút). Khi bạn có chế độ xem tùy chỉnh mà bạn sử dụng lại trong nhiều hoạt động, nhưng bạn muốn sử dụng cùng một phương thức onClick cho mọi trường hợp, phương thức XML không phải là phương thức thuận tiện nhất. Bạn sẽ cần đặt cái này trênClickMethod (có cùng thân) trong mọi hoạt động sử dụng chế độ xem tùy chỉnh của bạn.
Bartek Lipinski

87

Khi tôi thấy câu trả lời hàng đầu, nó khiến tôi nhận ra rằng vấn đề của tôi không phải là đưa tham số (Xem v) vào phương thức ưa thích:

public void myFancyMethod(View v) {}

Khi cố gắng truy cập nó từ xml, người ta nên sử dụng

android:onClick="myFancyMethod"/>

Hy vọng rằng sẽ giúp được ai đó.


74

android:onClick dành cho API cấp 4 trở đi, vì vậy nếu bạn đang nhắm mục tiêu <1.6, thì bạn không thể sử dụng API.


33

Kiểm tra nếu bạn quên đặt phương thức công khai!


1
Tại sao nó bắt buộc phải công khai?
eRaisedToX

3
@eRaisedToX Tôi nghĩ nó khá rõ ràng: nếu nó không công khai thì không thể gọi từ khung Android.
m0skit0

28

Chỉ định android:onClickkết quả thuộc tính trong Buttonví dụ gọi setOnClickListenernội bộ. Do đó hoàn toàn không có sự khác biệt.

Để hiểu rõ hơn, chúng ta hãy xem cách onClickthuộc tính XML được xử lý bởi khung công tác.

Khi tệp bố cục bị thổi phồng, tất cả các Chế độ xem được chỉ định trong đó sẽ được khởi tạo. Trong trường hợp cụ thể này, Buttonthể hiện được tạo bằng cách sử dụng hàm public Button (Context context, AttributeSet attrs, int defStyle)tạo. Tất cả các thuộc tính trong thẻ XML được đọc từ gói tài nguyên và được chuyển AttributeSetcho hàm tạo.

Buttonlớp được kế thừa từ Viewlớp dẫn đến hàm Viewtạo được gọi, trong đó đảm nhiệm việc thiết lập trình xử lý gọi lại nhấp qua setOnClickListener.

Thuộc tính onClick được xác định trong attrs.xml , được gọi trong View.java là R.styleable.View_onClick.

Đây là mã của View.javahầu hết các công việc cho bạn bằng cách gọi setOnClickListenerbằng chính nó.

 case R.styleable.View_onClick:
            if (context.isRestricted()) {
                throw new IllegalStateException("The android:onClick attribute cannot "
                        + "be used within a restricted context");
            }

            final String handlerName = a.getString(attr);
            if (handlerName != null) {
                setOnClickListener(new OnClickListener() {
                    private Method mHandler;

                    public void onClick(View v) {
                        if (mHandler == null) {
                            try {
                                mHandler = getContext().getClass().getMethod(handlerName,
                                        View.class);
                            } catch (NoSuchMethodException e) {
                                int id = getId();
                                String idText = id == NO_ID ? "" : " with id '"
                                        + getContext().getResources().getResourceEntryName(
                                            id) + "'";
                                throw new IllegalStateException("Could not find a method " +
                                        handlerName + "(View) in the activity "
                                        + getContext().getClass() + " for onClick handler"
                                        + " on view " + View.this.getClass() + idText, e);
                            }
                        }

                        try {
                            mHandler.invoke(getContext(), View.this);
                        } catch (IllegalAccessException e) {
                            throw new IllegalStateException("Could not execute non "
                                    + "public method of the activity", e);
                        } catch (InvocationTargetException e) {
                            throw new IllegalStateException("Could not execute "
                                    + "method of the activity", e);
                        }
                    }
                });
            }
            break;

Như bạn có thể thấy, setOnClickListenerđược gọi để đăng ký gọi lại, như chúng tôi làm trong mã của chúng tôi. Chỉ khác là nó sử dụng Java Reflectionđể gọi phương thức gọi lại được xác định trong Hoạt động của chúng tôi.

Dưới đây là lý do cho các vấn đề được đề cập trong các câu trả lời khác:

  • Phương thức gọi lại phải công khai : Vì Java Class getMethodđược sử dụng, nên chỉ các chức năng với công cụ xác định truy cập công cộng được tìm kiếm. Nếu không hãy sẵn sàng để xử lý IllegalAccessExceptionngoại lệ.
  • Trong khi sử dụng Nút với onClick in Fragment, cuộc gọi lại phải được xác định trong Activity : getContext().getClass().getMethod()cuộc gọi giới hạn tìm kiếm phương thức trong bối cảnh hiện tại, đó là Activity trong trường hợp Fragment. Do đó phương thức được tìm kiếm trong lớp Activity chứ không phải lớp Fragment.
  • Phương thức gọi lại nên chấp nhận Xem tham số : Vì Java Class getMethodcác tìm kiếm cho phương thức chấp nhận View.classlàm tham số.

1
Đó là phần còn thiếu đối với tôi - Java sử dụng Reflection để tìm trình xử lý nhấp chuột bắt đầu bằng getContext (). Đó là một chút bí ẩn đối với tôi cách nhấp chuột truyền từ một mảnh sang Hoạt động.
Andrew Queisser

15

Có những câu trả lời rất hay ở đây, nhưng tôi muốn thêm một dòng:

Trong android:onclickXML, Android sử dụng phản chiếu java phía sau cảnh để xử lý việc này.

như được giải thích ở đây, sự phản chiếu luôn làm chậm hiệu suất. (đặc biệt là trên Dalvik VM). Đăng ký onClickListenerlà một cách tốt hơn.


5
Bao nhiêu nó có thể làm chậm ứng dụng? :) Nửa mili giây; thậm chí không? So với việc thực sự thổi phồng bố cục, nó giống như một chiếc lông vũ và một con cá voi
Konrad Morawski

14

Lưu ý rằng nếu bạn muốn sử dụng tính năng onClick XML, phương thức tương ứng sẽ có một tham số, loại có khớp với đối tượng XML.

Ví dụ: một nút sẽ được liên kết với phương thức của bạn thông qua chuỗi tên của nó: android:onClick="MyFancyMethod"nhưng khai báo phương thức sẽ hiển thị: ...MyFancyMethod(View v) {...

Nếu bạn đang cố gắng thêm tính năng này vào một mục menu , nó sẽ có cùng một cú pháp chính xác trong tệp XML nhưng phương thức của bạn sẽ được khai báo là:...MyFancyMethod(MenuItem mi) {...


6

Một cách khác để thiết lập trình nghe nhấp chuột của bạn là sử dụng XML. Chỉ cần thêm thuộc tính android: onClick vào thẻ của bạn.

Đó là một cách thực hành tốt để sử dụng thuộc tính xml trên onClick vào một lớp Java ẩn danh bất cứ khi nào có thể.

Trước hết, hãy xem sự khác biệt trong mã:

Thuộc tính XML / thuộc tính onClick

Phần XML

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/button1" 
    android:onClick="showToast"/>

Phần Java

public void showToast(View v) {
    //Add some logic
}

Lớp Java ẩn danh / setOnClickListener

Phần XML

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

Phần Java

findViewById(R.id.button1).setOnClickListener(
    new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //Add some logic
        }
});

Dưới đây là những lợi ích của việc sử dụng thuộc tính XML trên một lớp Java ẩn danh:

  • Với lớp Java ẩn danh, chúng ta luôn phải chỉ định một id cho các phần tử của mình, nhưng với id thuộc tính XML có thể được bỏ qua.
  • Với lớp Java ẩn danh, chúng ta phải chủ động tìm kiếm phần tử bên trong khung nhìn (phần findViewById), nhưng với thuộc tính XML, Android thực hiện điều đó cho chúng ta.
  • Như lớp Java ẩn danh yêu cầu ít nhất 5 dòng mã, như chúng ta có thể thấy, nhưng với thuộc tính XML, 3 dòng mã là đủ.
  • Với lớp Java ẩn danh, chúng ta phải đặt tên cho phương thức của mình là on on ", nhưng với thuộc tính XML, chúng ta có thể thêm bất kỳ tên nào chúng ta muốn, điều này sẽ giúp ích rất nhiều cho khả năng đọc mã của chúng ta.
  • Thuộc tính Xml trên onClick đã được Google thêm vào trong quá trình phát hành API cấp 4, điều đó có nghĩa là cú pháp hiện đại hơn một chút và cú pháp hiện đại hầu như luôn luôn tốt hơn.

Tất nhiên, không phải lúc nào cũng có thể sử dụng thuộc tính Xml, đây là những lý do tại sao chúng tôi sẽ không chọn nó:

  • Nếu chúng ta đang làm việc với các mảnh vỡ. Thuộc tính onClick chỉ có thể được thêm vào một hoạt động, vì vậy nếu chúng ta có một đoạn, chúng ta sẽ phải sử dụng một lớp ẩn danh.
  • Nếu chúng tôi muốn chuyển trình nghe onClick sang một lớp riêng (có thể nếu nó rất phức tạp và / hoặc chúng tôi muốn sử dụng lại nó trong các phần khác nhau của ứng dụng của chúng tôi), thì chúng tôi sẽ không muốn sử dụng thuộc tính xml hoặc.

Và xin lưu ý rằng hàm được gọi bằng các thuộc tính XML phải luôn được công khai và nếu chúng được khai báo là riêng tư thì nó sẽ dẫn đến ngoại lệ.
Bestin John

5

Với Java 8, có lẽ bạn có thể sử dụng Phương thức tham khảo để đạt được những gì bạn muốn.

Giả sử đây là onClicktrình xử lý sự kiện của bạn cho một nút.

private void onMyButtonClicked(View v) {
    if (v.getId() == R.id.myButton) {
        // Do something when myButton was clicked
    }
}

Sau đó, bạn chuyển onMyButtonClickedtham chiếu phương thức cá thể trong một setOnClickListener()cuộc gọi như thế này.

Button myButton = (Button) findViewById(R.id.myButton);
myButton.setOnClickListener(this::onMyButtonClicked);

Điều này sẽ cho phép bạn tránh việc xác định rõ ràng một lớp ẩn danh một mình. Tuy nhiên, tôi phải nhấn mạnh rằng Tham chiếu phương thức của Java 8 thực sự chỉ là một đường cú pháp. Nó thực sự tạo ra một thể hiện của lớp ẩn danh cho bạn (giống như biểu thức lambda đã làm) do đó thận trọng tương tự như trình xử lý sự kiện kiểu lambda được áp dụng khi bạn không đăng ký trình xử lý sự kiện. Bài viết này giải thích nó thực sự tốt đẹp.

Tái bút Đối với những người tò mò về cách tôi thực sự có thể sử dụng tính năng ngôn ngữ Java 8 trong Android, thì đó là một thư viện của retrolambda .


5

Bằng cách sử dụng thuộc tính XML, bạn chỉ cần xác định một phương thức thay vì một lớp, vì vậy tôi đã tự hỏi liệu điều tương tự có thể được thực hiện thông qua mã chứ không phải trong bố cục XML.

Có, bạn có thể làm cho fragmenthoặcactivity thực hiệnView.OnClickListener

và khi bạn khởi tạo các đối tượng khung nhìn mới của mình bằng mã, bạn chỉ cần làm mView.setOnClickListener(this);

và điều này tự động đặt tất cả các đối tượng xem trong mã để sử dụng onClick(View v)phương thức mà bạn fragmenthoặcactivity vv có.

để phân biệt khung nhìn nào được gọi là onClickphương thức, bạn có thể sử dụng câu lệnh switch trênv.getId() phương thức.

Câu trả lời này khác với câu trả lời "Không, điều đó là không thể thông qua mã"


4
   Add Button in xml and give onclick attribute name that is the name of Method.
   <!--xml --!>
   <Button
  android:id="@+id/btn_register"
  android:layout_margin="1dp"
  android:onClick="addNumber"
  android:text="Add"
  />


    Button btnAdd = (Button) findViewById(R.id.mybutton); btnAdd.setOnClickListener(new View.OnClickListener() {
   @Override
    public void onClick(View v) {
      addNumber(v);
    }
    });

  Private void addNumber(View v){
  //Logic implement 
    switch (v.getId()) {
    case R.id.btnAdd :
        break;
     default:
        break;
    }}

3

Hỗ trợ câu trả lời của Ruivo, vâng, bạn phải khai báo phương thức là "công khai" để có thể sử dụng trong onclick XML của Android - Tôi đang phát triển một ứng dụng nhắm mục tiêu từ API cấp 8 (minSdk ...) đến 16 (targetSdk ...).

Tôi đã tuyên bố phương thức của tôi là riêng tư và nó gây ra lỗi, chỉ tuyên bố nó là công trình công cộng tuyệt vời.


có vẻ như các biến được khai báo trong lớp của Activity Activity có thể được sử dụng trong phạm vi của hàm gọi lại được khai báo; Bundle Activity.mBundle sẽ ném IllegalStateException / NullPulumException nếu được sử dụng trong myFancyMethod ().
Quasaur

2

Hãy cẩn thận, mặc dù android:onClickXML có vẻ là một cách thuận tiện để xử lý nhấp chuột, nhưng setOnClickListenerviệc triển khai thực hiện một cái gì đó bổ sung hơn là thêm onClickListener. Thật vậy, nó đặt tài sản xem clickablelà đúng.

Mặc dù nó có thể không phải là vấn đề đối với hầu hết các triển khai Android, nhưng theo nhà xây dựng điện thoại, nút luôn được mặc định là clickable = true nhưng các hàm tạo khác trên một số kiểu điện thoại có thể có mặc định clickable = false trên các chế độ xem không có Nút.

Vì vậy, cài đặt XML là không đủ, bạn phải suy nghĩ mọi lúc để thêm android:clickable="true"vào nút không và nếu bạn có một thiết bị có mặc định là clickable = true và bạn quên ngay cả khi đặt thuộc tính XML này, bạn sẽ không nhận thấy vấn đề trong thời gian chạy nhưng sẽ nhận được phản hồi trên thị trường khi nó sẽ nằm trong tay khách hàng của bạn!

Ngoài ra, chúng tôi không bao giờ có thể chắc chắn về cách proguard sẽ che giấu và đổi tên các thuộc tính XML và phương thức lớp, vì vậy không an toàn 100% rằng một ngày nào đó chúng sẽ không bao giờ có lỗi.

Vì vậy, nếu bạn không bao giờ muốn gặp rắc rối và không bao giờ nghĩ về điều đó, tốt hơn nên sử dụng setOnClickListenerhoặc các thư viện như ButterKnife với chú thích@OnClick(R.id.button)


Các onClickthuộc tính XML cũng đặt clickable = truevì nó gọi setOnClickListenertrên Viewtrong nội bộ
Florian Walther

1

Giả sử, bạn muốn thêm sự kiện nhấp chuột như thế này main.xml

<Button
    android:id="@+id/btn_register"
    android:layout_margin="1dp"
    android:layout_marginLeft="3dp"
    android:layout_marginTop="10dp"
    android:layout_weight="2"
    android:onClick="register"
    android:text="Register"
    android:textColor="#000000"/>

Trong tệp java, bạn phải viết một phương thức như phương thức này.

public void register(View view) {
}

0

Tôi đang viết mã này trong tập tin xml ...

<Button
    android:id="@+id/btn_register"
    android:layout_margin="1dp"
    android:layout_marginLeft="3dp"
    android:layout_marginTop="10dp"
    android:layout_weight="2"
    android:onClick="register"
    android:text="Register"
    android:textColor="#000000"/>

Và viết mã này thành từng mảnh ...

public void register(View view) {
}

Điều này là có thể trong mảnh.
dùng2786249

0

Cách tốt nhất để làm điều này là với đoạn mã sau:

 Button button = (Button)findViewById(R.id.btn_register);
 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do your fancy method
            }
        });

Tôi không thấy cách này trả lời câu hỏi - người hỏi muốn tránh tạo một lớp ẩn danh, và thay vào đó sử dụng một phương thức lớp.
ajshort

0

Để làm cho cuộc sống của bạn dễ dàng hơn và tránh Lớp ẩn danh trong setOnClicklistener (), hãy triển khai Giao diện View.OnClicklistener như dưới đây:

lớp công khai YourClass mở rộng CommonActivity thực hiện View.OnClickListener, ...

Điều này tránh:

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        yourMethod(v);
    }
});

và đi thẳng đến:

@Override
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.your_view:
      yourMethod();
      break;
  }
}
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.