Bagaimana sebenarnya perbedaan atribut android: onClick XML berbeda dengan setOnClickListener?

416

Dari yang saya baca Anda dapat menetapkan onClickpenangan ke tombol dalam dua cara.

Menggunakan android:onClickatribut XML tempat Anda hanya menggunakan nama metode publik dengan tanda tangan void name(View v)atau dengan menggunakan setOnClickListenermetode tempat Anda melewatkan objek yang mengimplementasikan OnClickListenerantarmuka. Yang terakhir sering membutuhkan kelas anonim yang secara pribadi saya tidak suka (selera pribadi) atau mendefinisikan kelas internal yang mengimplementasikan OnClickListener.

Dengan menggunakan atribut XML Anda hanya perlu mendefinisikan metode, bukan kelas jadi saya bertanya-tanya apakah hal yang sama dapat dilakukan melalui kode dan tidak dalam tata letak XML.

emitrax
sumber
4
Saya membaca masalah Anda dan saya pikir Anda terjebak di tempat yang sama seperti saya. Saya menemukan video yang sangat bagus yang sangat membantu saya dalam menyelesaikan masalah saya. Temukan videonya di tautan berikut: youtube.com/watch?v=MtmHURWKCmg&feature=youtu.be Saya harap ini akan membantu Anda juga :)
user2166292
9
Bagi mereka yang ingin menghemat waktu menonton video yang diposting di komentar di atas, itu hanya menunjukkan bagaimana dua tombol dapat memiliki metode yang sama untuk onClickatributnya di file tata letak. Ini dilakukan berkat parameter View v. Anda cukup memeriksa if (v == findViewById(R.id.button1)) dll.
CodyBugstein
13
@Imray Saya pikir akan lebih baik untuk digunakan v.getId() == R.id.button1, karena Anda tidak harus menemukan kontrol yang sebenarnya dan melakukan perbandingan. Dan Anda dapat menggunakan banyak switchbukannya seandainya.
Sami Kuhmonen
3
Tutorial ini akan banyak membantu Anda. klik di sini
c49
Menggunakan xml android: onClick menyebabkan kerusakan.

Jawaban:

604

Tidak, itu tidak mungkin melalui kode. Android hanya mengimplementasikan OnClickListeneruntuk Anda ketika Anda mendefinisikan android:onClick="someMethod"atribut.

Kedua cuplikan kode itu sama, hanya diterapkan dengan dua cara berbeda.

Implementasi Kode

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
}

Di atas adalah implementasi kode dari sebuah OnClickListener. Dan ini adalah implementasi XML.

Implementasi 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 -->

Di latar belakang, Android tidak melakukan apa pun selain kode Java, memanggil metode Anda pada acara klik.

Perhatikan bahwa dengan XML di atas, Android akan mencari onClickmetode myFancyMethod()hanya di Aktivitas saat ini. Ini penting untuk diingat jika Anda menggunakan fragmen, karena meskipun Anda menambahkan XML di atas menggunakan fragmen, Android tidak akan mencari onClickmetode dalam .javafile fragmen yang digunakan untuk menambahkan XML.

Hal penting lain yang saya perhatikan. Anda mengatakan Anda tidak suka metode anonim . Anda bermaksud mengatakan Anda tidak suka kelas anonim .

Oktavianus A. Damiean
sumber
4
Saya bukan guru Java tapi ya, maksud saya kelas anonim. Terima kasih untuk balasan Anda. Sangat jelas.
emitrax
118
Perhatikan bahwa jika Anda menggunakan XML onclick, Anda harus meletakkan metode onclick ( myFancyMethod()) di Activity saat ini. Ini penting jika Anda menggunakan fragmen, karena cara terprogram mengatur pendengar onclick mungkin akan memiliki metode yang menangani klik di onCreateView () ... fragmen yang tidak akan ditemukan jika dirujuk dari XML.
Peter Ajtai
12
Ya, metodenya harus bersifat publik.
Octavian A. Damiean
12
Hal yang menarik adalah bahwa melakukannya dalam kode memang memungkinkan seseorang untuk melindungi akses metode dengan membuat metode pribadi, sedangkan melakukannya dengan cara xml mengarah ke pemaparan metode.
bgse
5
Fakta bahwa fungsi (dalam pendekatan XML) harus dalam Aktivitas penting tidak hanya ketika Anda mempertimbangkan fragmen, tetapi juga tampilan khusus (berisi tombol). Ketika Anda memiliki tampilan kustom yang Anda gunakan kembali dalam beberapa aktivitas, tetapi Anda ingin menggunakan metode onClick yang sama untuk semua kasus, metode XML bukan yang paling nyaman. Anda harus meletakkan ini diClickMethod (dengan tubuh yang sama) di setiap aktivitas yang menggunakan tampilan kustom Anda.
Bartek Lipinski
87

Ketika saya melihat jawaban teratas, itu membuat saya menyadari bahwa masalah saya tidak menempatkan parameter (Lihat v) pada metode mewah:

public void myFancyMethod(View v) {}

Ketika mencoba mengaksesnya dari xml, orang harus menggunakannya

android:onClick="myFancyMethod"/>

Semoga itu bisa membantu seseorang.

jp093121
sumber
74

android:onClick untuk API level 4 dan seterusnya, jadi jika Anda menargetkan <1,6, maka Anda tidak dapat menggunakannya.

James
sumber
33

Periksa apakah Anda lupa mempublikasikan metode ini!

Ruivo
sumber
1
mengapa wajib mempublikasikannya?
eRaisedToX
3
@ eRaisedToX Saya pikir cukup jelas: jika bukan untuk publik tidak dapat dipanggil dari kerangka Android.
m0skit0
28

Menentukan android:onClickhasil atribut Buttonmisalnya memanggil secara setOnClickListenerinternal. Karenanya sama sekali tidak ada perbedaan.

Untuk memiliki pemahaman yang jelas, mari kita lihat bagaimana onClickatribut XML ditangani oleh framework.

Ketika file tata letak meningkat, semua tampilan yang ditentukan di dalamnya adalah instantiated. Dalam kasus khusus ini, Buttoninstance dibuat menggunakan public Button (Context context, AttributeSet attrs, int defStyle)konstruktor. Semua atribut dalam tag XML dibaca dari bundel sumber daya dan diteruskan AttributeSetke konstruktor.

Buttonkelas diwarisi dari Viewkelas yang menghasilkan Viewkonstruktor dipanggil, yang mengatur pengaturan panggilan balik melalui penelepon setOnClickListener.

Atribut onClick didefinisikan dalam attrs.xml , disebut dalam View.java sebagai R.styleable.View_onClick.

Berikut adalah kode View.javayang berfungsi untuk Anda dengan menelepon setOnClickListenersendiri.

 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;

Seperti yang Anda lihat, setOnClickListenerdipanggil untuk mendaftarkan panggilan balik, seperti yang kita lakukan dalam kode kita. Hanya perbedaan yang digunakannya Java Reflectionuntuk memanggil metode panggilan balik yang ditentukan dalam Aktivitas kami.

Berikut adalah alasan untuk masalah yang disebutkan dalam jawaban lain:

  • Metode panggilan balik harus umum : Karena Java Class getMethoddigunakan, hanya fungsi dengan penentu akses publik yang dicari. Kalau tidak siap untuk menangani IllegalAccessExceptionpengecualian.
  • Saat menggunakan Tombol dengan onClick di Fragment, callback harus didefinisikan dalam Activity : getContext().getClass().getMethod()call membatasi pencarian metode ke konteks saat ini, yaitu Activity dalam kasus Fragment. Oleh karena itu metode dicari dalam kelas Activity dan bukan kelas Fragment.
  • Metode panggilan balik harus menerima parameter Tampilan : Karena Java Class getMethodmencari metode yang menerima View.classparameter.
Manish Mulimani
sumber
1
Itu adalah bagian yang hilang untuk saya - Java menggunakan Reflection untuk menemukan penangan klik dimulai dengan getContext (). Bagiku sedikit misterius bagaimana klik menyebar dari sebuah fragmen ke suatu Kegiatan.
Andrew Queisser
15

Ada jawaban yang sangat baik di sini, tetapi saya ingin menambahkan satu baris:

Di android:onclickdalam XML, Android menggunakan refleksi java di belakang layar untuk menangani ini.

Dan seperti yang dijelaskan di sini, refleksi selalu memperlambat kinerja. (terutama pada Dalvik VM). Mendaftar onClickListeneradalah cara yang lebih baik.

Krupal Shah
sumber
5
Berapa bisa memperlambat aplikasi? :) Setengah milidetik; bahkan tidak? Dibandingkan dengan benar-benar menggelembungkan tata letak itu seperti bulu dan ikan paus
Konrad Morawski
14

Perhatikan bahwa jika Anda ingin menggunakan fitur XML onClick, metode yang sesuai harus memiliki satu parameter, yang tipenya harus cocok dengan objek XML.

Misalnya, sebuah tombol akan ditautkan ke metode Anda melalui string namanya: android:onClick="MyFancyMethod"tetapi deklarasi metode harus menunjukkan: ...MyFancyMethod(View v) {...

Jika Anda mencoba untuk menambahkan fitur ini ke item menu , itu akan memiliki sintaks yang sama persis dalam file XML tetapi metode Anda akan dinyatakan sebagai:...MyFancyMethod(MenuItem mi) {...

Antoine Lizée
sumber
6

Cara lain untuk mengatur pendengar klik Anda adalah dengan menggunakan XML. Cukup tambahkan atribut android: onClick ke tag Anda.

Merupakan praktik yang baik untuk menggunakan atribut xml "onClick" di atas kelas Java anonim jika memungkinkan.

Pertama-tama, mari kita lihat perbedaan dalam kode:

Atribut XML / AtClick atribut

Bagian XML

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

Porsi Jawa

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

Kelas Java anonim / setOnClickListener

Bagian XML

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

Porsi Jawa

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

Berikut adalah manfaat menggunakan atribut XML di atas kelas Java anonim:

  • Dengan kelas Java Anonim kita selalu harus menentukan id untuk elemen kita, tetapi dengan atribut XML id dapat dihilangkan.
  • Dengan kelas Java Anonim kita harus secara aktif mencari elemen di dalam tampilan (bagian findViewById), tetapi dengan atribut XML Android melakukannya untuk kita.
  • Kelas Java anonim membutuhkan setidaknya 5 baris kode, seperti yang dapat kita lihat, tetapi dengan atribut XML 3 baris kode sudah cukup.
  • Dengan kelas Java Anonim kita harus memberi nama metode kita "onClick", tetapi dengan atribut XML kita dapat menambahkan nama apa pun yang kita inginkan, yang secara dramatis akan membantu keterbacaan kode kita.
  • Atribut "onClick" Xml telah ditambahkan oleh Google selama rilis API level 4, yang berarti sedikit sintaksis modern dan sintaksis modern hampir selalu lebih baik.

Tentu saja, tidak selalu mungkin untuk menggunakan atribut Xml, berikut adalah alasan mengapa kami tidak memilihnya:

  • Jika kami bekerja dengan fragmen. Atribut onClick hanya dapat ditambahkan ke suatu aktivitas, jadi jika kita memiliki sebuah fragmen, kita harus menggunakan kelas anonim.
  • Jika kami ingin memindahkan pendengar onClick ke kelas yang terpisah (mungkin jika sangat rumit dan / atau kami ingin menggunakannya kembali di berbagai bagian aplikasi kami), maka kami tidak ingin menggunakan atribut xml antara.
Bestin John
sumber
Dan harap dicatat bahwa fungsi yang disebut menggunakan atribut XML harus selalu publik dan jika mereka dinyatakan sebagai ayam pribadi, itu akan menghasilkan pengecualian.
Bestin John
5

Dengan Java 8, Anda mungkin bisa menggunakan Referensi Metode untuk mencapai apa yang Anda inginkan.

Asumsikan ini adalah onClickpengendali acara Anda untuk sebuah tombol.

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

Kemudian, Anda memberikan onMyButtonClickedreferensi metode instance dalam setOnClickListener()panggilan seperti ini.

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

Ini akan memungkinkan Anda untuk menghindari secara eksplisit mendefinisikan kelas anonim sendiri. Namun saya harus menekankan bahwa Referensi Metode Java 8 sebenarnya hanya gula sintaksis. Ini sebenarnya membuat turunan kelas anonim untuk Anda (seperti halnya ekspresi lambda) maka kehati-hatian yang sama seperti pengendali event gaya lambda diaplikasikan saat Anda datang ke pencabutan handler acara Anda. Artikel ini menjelaskannya dengan sangat bagus.

PS. Bagi mereka yang ingin tahu bagaimana saya benar-benar dapat menggunakan fitur bahasa Java 8 di Android, itu adalah milik perpustakaan retrolambda .

onelaview
sumber
5

Dengan menggunakan atribut XML Anda hanya perlu mendefinisikan metode, bukan kelas jadi saya bertanya-tanya apakah hal yang sama dapat dilakukan melalui kode dan tidak dalam tata letak XML.

Ya, Anda dapat membuat fragmentatau activitymenerapkanView.OnClickListener

dan ketika Anda menginisialisasi objek tampilan baru Anda dalam kode, Anda cukup melakukannya mView.setOnClickListener(this);

dan ini secara otomatis mengatur semua objek tampilan dalam kode untuk menggunakan onClick(View v)metode yang Anda fragmentatau activitydll miliki.

untuk membedakan tampilan yang disebut onClickmetode, Anda dapat menggunakan pernyataan sakelar padav.getId() metode.

Jawaban ini berbeda dari yang mengatakan "Tidak, itu tidak mungkin melalui kode"

CQM
sumber
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;
    }}
jeet parmar
sumber
3

Mendukung jawaban Ruivo, ya Anda harus mendeklarasikan metode sebagai "publik" untuk dapat digunakan di Android XML onclick - Saya sedang mengembangkan penargetan aplikasi dari API Level 8 (minSdk ...) hingga 16 (targetSdk ...).

Saya menyatakan metode saya sebagai pribadi dan menyebabkan kesalahan, hanya menyatakannya sebagai pekerjaan umum yang hebat.

Waqas Hasan
sumber
tampaknya variabel yang dideklarasikan di kelas Kegiatan hosting tidak dapat digunakan dalam lingkup panggilan balik yang dinyatakan; Bundle Activity.mBundle akan melempar IllegalStateException / NullPointerException jika digunakan di myFancyMethod ().
Quasaur
2

Hati-hati, meskipun android:onClickXML tampaknya merupakan cara yang nyaman untuk menangani klik, setOnClickListenerimplementasinya melakukan sesuatu yang lebih daripada menambahkan onClickListener. Memang, ini membuat properti view clickablemenjadi true.

Meskipun mungkin tidak menjadi masalah pada sebagian besar implementasi Android, menurut konstruktor telepon, tombol selalu default untuk diklik = benar tetapi konstruktor lain pada beberapa model ponsel mungkin memiliki default yang dapat diklik = salah pada tampilan non Tombol.

Jadi pengaturan XML tidak cukup, Anda harus berpikir setiap saat untuk menambahkan android:clickable="true" pada tombol non, dan jika Anda memiliki perangkat di mana defaultnya dapat diklik = benar dan Anda lupa sekali untuk meletakkan atribut XML ini, Anda tidak akan melihat masalah saat runtime tetapi akan mendapatkan umpan balik di pasar saat itu akan berada di tangan pelanggan Anda!

Selain itu, kita tidak pernah bisa yakin tentang bagaimana proguard akan mengaburkan dan mengganti nama atribut XML dan metode kelas, jadi tidak 100% aman bahwa mereka tidak akan pernah memiliki bug suatu hari.

Jadi jika Anda tidak pernah ingin mengalami masalah dan tidak pernah memikirkannya, lebih baik menggunakan setOnClickListeneratau perpustakaan seperti ButterKnife dengan anotasi@OnClick(R.id.button)

Livio
sumber
The onClickatribut XML juga menetapkan clickable = truekarena panggilan setOnClickListenerdi Viewinternal
Florian Walther
1

Misalkan, Anda ingin menambahkan event klik seperti ini 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"/>

Dalam file java, Anda harus menulis metode seperti metode ini.

public void register(View view) {
}
nazrul islam
sumber
0

Saya Tulis kode ini dalam file 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"/>

Dan tulis kode ini dalam fragmen ...

public void register(View view) {
}
pengguna2786249
sumber
Ini dimungkinkan dalam fragmen.
user2786249
0

Cara terbaik untuk melakukan ini adalah dengan kode berikut:

 Button button = (Button)findViewById(R.id.btn_register);
 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do your fancy method
            }
        });
Katarina Dabo
sumber
Saya tidak melihat bagaimana ini menjawab pertanyaan - penanya ingin menghindari membuat kelas anonim, dan alih-alih menggunakan metode kelas.
ajshort
0

Untuk membuat hidup Anda lebih mudah dan menghindari Kelas Anonim di setOnClicklistener (), terapkan Antarmuka View.OnClicklistener seperti di bawah ini:

kelas publik YourClass memperluas CommonActivity mengimplementasikan View.OnClickListener, ...

ini menghindari:

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

dan langsung menuju:

@Override
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.your_view:
      yourMethod();
      break;
  }
}
Vicente Domingos
sumber