Android: onClick XML özniteliğinin setOnClickListener'dan tam olarak farkı nedir?


416

Okuduğumdan onClickbir düğmeye iki şekilde bir işleyici atayabilirsiniz .

Kullanımı android:onClicksadece imza ile halka açık yöntemin adını kullanmak XML özelliğini void name(View v)kullanarak veya setOnClickListeneruygulamak bir nesne geçmek yöntemi OnClickListenerarayüzü. İkincisi genellikle kişisel olarak sevmediğim (kişisel zevk) veya uygulayan bir iç sınıfı tanımlayan anonim bir sınıf gerektirir OnClickListener.

XML özniteliğini kullanarak, sadece bir sınıf yerine bir yöntem tanımlamanız gerekir, bu yüzden aynı XML düzeninde değil kod yoluyla yapılabilir merak ediyorum.


4
Sorununu okudum ve sanırım benim gibi aynı yerde sıkışıp kaldın. Sorunumu çözmemde bana çok yardımcı olan çok iyi bir videoyla karşılaştım. Aşağıdaki bağlantıda videoyu bulun: youtube.com/watch?v=MtmHURWKCmg&feature=youtu.be Umarım bu size de yardımcı olur :)
user2166292

9
Yukarıdaki yorumda yayınlanan videoyu izlerken zaman kazanmak isteyenler için, iki düğmenin onClickmizanpaj dosyasındaki niteliği için nasıl aynı yönteme sahip olabileceğini gösterir . Bu parametre sayesinde yapılır View v. Sadece kontrol edin if (v == findViewById(R.id.button1)) vb ..
CodyBugstein

13
@Imray Kullanmanın daha iyi olduğunu düşünürüm v.getId() == R.id.button1, çünkü gerçek kontrolü bulmak ve bir karşılaştırma yapmak zorunda değilsiniz. Ve bir switchsürü ifs yerine kullanabilirsiniz .
Sami Kuhmonen

3
Bu eğitim size çok yardımcı olacaktır. buraya tıklayın
c49

Xml android: onClick kullanmak çökmeye neden olur.

Yanıtlar:


604

Hayır, bu kodla mümkün değil. Özelliği OnClickListenertanımladığınızda Android yalnızca sizin için uygular android:onClick="someMethod".

Bu iki kod parçacığı eşittir, sadece iki farklı şekilde uygulanır.

Kod Uygulaması

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
}

Yukarıda bir OnClickListener. Ve bu XML uygulamasıdır.

XML Uygulaması

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

Arka planda, Android Java kodundan başka bir şey yapmaz ve yönteminizi bir tıklama etkinliğinde çağırır.

Yukarıdaki XML ile Android'in onClickyöntemi myFancyMethod()yalnızca geçerli Etkinlikte arayacağını unutmayın . Parçalar kullanıyorsanız hatırlamak önemlidir, çünkü yukarıdaki XML'i bir parça kullanarak ekleseniz bile, Android , XML eklemek için kullanılan parçanın dosyasında onClickyöntemi .javaaramaz.

Fark ettiğim bir diğer önemli şey. İsimsiz yöntemleri tercih etmediğinizden bahsettiniz . Anonim derslerden hoşlanmadığınızı söylemek istediniz .


4
Ben bir Java gurusu değilim ama evet, anonim bir sınıf demek istedim. Cevabın için teşekkürler. Çok açık.
emitrax

118
XML onclick kullanıyorsanız, onclick yöntemini ( myFancyMethod()) geçerli Etkinliğe koymanız gerektiğini unutmayın . Eğer parçaları kullanıyorsanız dinleyicileri onclick ayar program yolu muhtemelen verecek bir veridiliminin onCreateView tıklamaları taşıma yöntemi (...) elde edeceklerinden, bu önemlidir değil XML sevk eğer bulunabilir.
Peter Ajtai

12
Evet, yöntemin herkese açık olması gerekir.
Octavian A. Damiean

12
İlginç bir şey, bunu kodda yapmak, yöntemi özel yaparak yöntem erişimine izin verirken xml yolunu yapmak yöntemin ortaya çıkmasına yol açmasıdır.
bgse

5
Fonksiyonun (XML yaklaşımında) Etkinlik içinde olması, sadece parçaları dikkate aldığınızda değil, aynı zamanda özel düğmeleri de (düğmeyi içeren) dikkate aldığınızda önemlidir. Birden çok etkinlikte yeniden kullandığınız özel bir görünümünüz varsa, ancak tüm durumlar için aynı onClick yöntemini kullanmak istediğinizde, XML yöntemi en uygun yöntem değildir. Özel görünümünüzü kullanan her etkinliğe bunu onClickMethod (aynı gövdeyle) koymanız gerekir.
Bartek Lipinski

87

En iyi cevabı gördüğümde, sorunumun (View v) parametresini fantezi yöntemine koymadığını fark ettim:

public void myFancyMethod(View v) {}

Xml'den erişmeye çalışırken,

android:onClick="myFancyMethod"/>

Umarım birine yardım eder.


74

android:onClick API seviye 4'ten itibaren, bu nedenle <1,6'yı hedefliyorsanız, onu kullanamazsınız.


33

Yöntemi herkese açık hale getirmeyi unutup unutma!


1
neden halka duyurmak zorunludur?
eRaisedToX

3
@eRaisedToX Bence oldukça açık: halka açık değilse Android çerçevesinden çağrılamaz.
m0skit0

28

android:onClickÖznitelik belirtildiğinde , Buttonörneğin setOnClickListenerdahili olarak arama yapılır . Dolayısıyla kesinlikle hiçbir fark yoktur.

Açık bir anlayışa sahip olmak için, XML onClicközelliğinin çerçeve tarafından nasıl ele alındığını görelim .

Bir düzen dosyası şişirildiğinde, içinde belirtilen tüm Görünümler somutlaştırılır. Bu özel durumda, Buttonörnek public Button (Context context, AttributeSet attrs, int defStyle)yapıcı kullanılarak oluşturulur. XML etiketindeki tüm öznitelikler kaynak paketinden okunur ve yapıcıya iletilir AttributeSet.

Buttonsınıf, yapıcı çağrılmasıyla sonuçlanan ve tıklama geri arama işleyicisinin üzerinden ayarlanmasını sağlayan Viewsınıftan devralınır .ViewsetOnClickListener

Attrs.xml dosyasında tanımlanan onClick özniteliği, View.java içinde R.styleable.View_onClick.

İşte kodu, View.javaişin çoğunu setOnClickListenerkendi başına arayarak yapar .

 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;

Gördüğünüz gibi setOnClickListener, kodumuzda yaptığımız gibi geri aramayı kaydetmek için çağrılır. Tek fark, Java ReflectionFaaliyetimizde tanımlanan geri arama yöntemini çağırmak için kullanılmasıdır.

Diğer yanıtlarda belirtilen sorunların nedeni şunlardır:

  • Geri arama yöntemi herkese açık olmalıdır : Java Class getMethodKullanıldığından, yalnızca genel erişim belirleyicisine sahip işlevler aranır. Aksi takdirde IllegalAccessExceptionistisnayı ele almaya hazır olun .
  • OnClick in Fragment ile Düğme kullanılırken, geri çağırma Etkinlik'te tanımlanmalıdır : getContext().getClass().getMethod()çağrı, yöntem aramasını Parçalanma durumunda Etkinlik olan geçerli bağlamla sınırlar. Bu nedenle yöntem, Fragment sınıfında değil, Activity sınıfında aranır.
  • Geri arama yöntemi View parametresini kabul etmelidir : Parametre olarak Java Class getMethodkabul View.classedilen yöntemi arar .

1
Bu benim için eksik parçaydı - Java, getContext () ile başlayan tıklama işleyicisini bulmak için Reflection kullanıyor. Tıklamanın bir parçadan bir Etkinliğe nasıl yayıldığı benim için biraz gizemliydi.
Andrew Queisser

15

Burada çok iyi cevaplar var, ama bir satır eklemek istiyorum:

Gelen android:onclickXML, Android kullanan java yansıması bu işlemek için sahne arkasında.

Ve burada açıklandığı gibi, yansıma her zaman performansı yavaşlatır. (özellikle Dalvik VM'de). Kayıt onClickListenerolmak daha iyi bir yoldur.


5
Uygulamayı ne kadar yavaşlatabilir? :) Yarım milisaniye; değil mi? Düzeni şişirmeye kıyasla, tüy ve balina gibi
Konrad Morawski

14

OnClick XML özelliğini kullanmak istiyorsanız, karşılık gelen yöntemin türünün XML nesnesiyle eşleşmesi gereken bir parametresi olması gerektiğini unutmayın.

Örneğin, bir düğme yönteminize ad dizesi aracılığıyla bağlanır: android:onClick="MyFancyMethod"ancak yöntem bildirimi şunu göstermelidir: ...MyFancyMethod(View v) {...

Bu özelliği bir menü öğesine eklemeye çalışıyorsanız , XML dosyasında tam olarak aynı sözdizimine sahip olacak, ancak yönteminiz şu şekilde bildirilecektir:...MyFancyMethod(MenuItem mi) {...


6

Tıklama dinleyicilerinizi ayarlamanın başka bir yolu da XML kullanmaktır. Etiketinize android: onClick özelliğini eklemeniz yeterlidir.

Mümkün olduğunca anonim bir Java sınıfı üzerinde “onClick” xml özniteliğini kullanmak iyi bir uygulamadır.

Her şeyden önce, koddaki farka bir göz atalım:

XML Özelliği / onClick özelliği

XML bölümü

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

Java bölümü

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

Anonim Java Sınıfı / setOnClickListener

XML Bölümü

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

Java bölümü

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

XML niteliğini anonim bir Java sınıfı üzerinde kullanmanın yararları şunlardır:

  • Anonim Java sınıfında her zaman öğelerimiz için bir kimlik belirtmemiz gerekir, ancak XML özellik kimliği ile atlanabilir.
  • Anonim Java sınıfında, görünümün içindeki öğeyi (findViewById bölümü) aktif olarak aramamız gerekir, ancak Android XML özelliği ile bunu bizim için yapar.
  • Anonim Java sınıfı, görebildiğimiz gibi en az 5 satır kod gerektirir, ancak XML özelliğiyle 3 satır kod yeterlidir.
  • Anonim Java sınıfı ile “onClick” yöntemimizi adlandırmalıyız, ancak XML özelliğiyle istediğimiz herhangi bir adı ekleyebiliriz, bu da kodumuzun okunabilirliğine önemli ölçüde yardımcı olacaktır.
  • Xml “onClick” özelliği, API düzey 4 sürümü sırasında Google tarafından eklenmiştir. Bu, biraz daha modern bir sözdizimi ve modern sözdiziminin neredeyse her zaman daha iyi olduğu anlamına gelir.

Tabii ki, Xml özniteliğini kullanmak her zaman mümkün değildir, işte neden seçmememizin nedenleri şunlardır:

  • Parçalarla çalışıyorsak. onClick niteliği yalnızca bir etkinliğe eklenebilir, bu nedenle bir parçamız varsa, anonim bir sınıf kullanmamız gerekir.
  • OnClick dinleyicisini ayrı bir sınıfa taşımak istiyorsak (belki çok karmaşıksa ve / veya başvurumuzun farklı bölümlerinde yeniden kullanmak istiyorsak), xml niteliğini kullanmak istemeyiz ya.

XML özniteliklerini kullanmak olarak adlandırılan işlevin her zaman herkese açık olması gerektiğini ve özel olarak bildirildiklerinde bunun istisnaya neden olacağını lütfen unutmayın.
Bestin John

5

Java 8 ile, istediğinizi elde etmek için Yöntem Referansı'nı kullanabilirsiniz .

Bunun onClickbir düğme için olay işleyiciniz olduğunu varsayalım .

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

Ardından, böyle onMyButtonClickedbir setOnClickListener()çağrıda örnek yöntemi başvurusunu iletirsiniz .

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

Bu, anonim bir sınıfı kendiniz açıkça tanımlamaktan kaçınmanıza izin verecektir . Bununla birlikte, Java 8'in Yöntem Referansının aslında sadece sözdizimsel bir şeker olduğunu vurgulamalıyım. Aslında sizin için anonim sınıfın bir örneğini oluşturur (tıpkı lambda ifadesinin yaptığı gibi), bu nedenle olay işleyicinizin kayıtsızlığına geldiğinizde lambda-ifade tarzı olay işleyicisi uygulandığı gibi benzer bir uyarı. Bu makale gerçekten güzel olduğunu açıklıyor.

PS. Android'de Java 8 dil özelliğini gerçekten nasıl kullanabileceğimi merak edenler için, retrolambda kütüphanesinin izniyle .


5

XML özniteliğini kullanarak, sadece bir sınıf yerine bir yöntem tanımlamanız gerekir, bu yüzden aynı XML düzeninde değil kod yoluyla yapılabilir merak ediyorum.

Evet, yapabilirsiniz fragmentveya activityuygulamakView.OnClickListener

ve yeni görünüm nesnelerinizi kodda başlattığınızda mView.setOnClickListener(this);

ve bu, kodunuzun tüm görünüm nesnelerini veya vb. onClick(View v)yöntemini kullanacak şekilde otomatik olarak ayarlar .fragmentactivity

hangi görünümün onClickyöntemi çağırdığını ayırt etmek için yöntemde bir switch ifadesi kullanabilirsiniz v.getId().

Bu cevap "Hayır, kodla mümkün değil" diyen cevaptan farklı


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

Ruivo'nun cevabını destekleyen evet, Android'in XML onclick'inde kullanabilmek için yöntemi "herkese açık" olarak tanımlamanız gerekiyor - API Seviye 8'den (minSdk ...) 16'ya (targetSdk ...) hedefleyen bir uygulama geliştiriyorum.

Metodumu özel olarak ilan ediyordum ve hataya neden oldum, sadece kamu işleri harika olduğunu ilan ettim.


barındırma Etkinliğinin sınıfında bildirilen değişkenlerin, bildirilen geri çağrı kapsamında kullanılamayacağı anlaşılır; Bundle Activity.mBundle, myFancyMethod () içinde kullanılırsa bir IllegalStateException / NullPointerException kurar.
Quasaur

2

Dikkatli olun, android:onClickXML tıklamayı işlemek için uygun bir yol gibi görünse de, setOnClickListeneruygulama eklemekten başka bir şey yapar onClickListener. Gerçekten de, view özelliğini clickabletrue değerine koydu .

Çoğu Android uygulamasında bir sorun olmasa da, telefon yapıcısına göre, düğme her zaman varsayılan olarak clickable = true olarak ayarlıdır, ancak bazı telefon modelindeki diğer kurucuların varsayılan olarak clickable = false olmayan Düğme görünümlerinde olabilir.

Bu nedenle XML'yi ayarlamak yeterli değildir, android:clickable="true"düğmeyi eklemek için her zaman düşünmeniz gerekir ve varsayılanın clickable = true olduğu bir cihazınız varsa ve bu XML özelliğini koymak için bir kez bile unutursanız, fark etmezsiniz. sorun zamanında ama müşterilerinizin elinde olacağı zaman piyasada geribildirim alacaksınız!

Ayrıca, proguard'ın XML niteliklerini ve sınıf yöntemini nasıl gizleyeceğini ve yeniden adlandıracağından asla emin olamayız, bu yüzden bir gün hiçbir zaman hata yapmayacakları için% 100 güvenli değildir.

Bu yüzden asla sorun yaşamamak ve asla düşünmek istemiyorsanız, setOnClickListenerButterKnife gibi kütüphaneleri ek açıklama ile kullanmak daha iyidir@OnClick(R.id.button)


onClickXML özelliği de ayarlar clickable = trueo çağırır çünkü setOnClickListenerüzerinde Viewdahili olarak
Florian Walther

1

Diyelim ki böyle bir tıklama etkinliği eklemek istiyorsunuz 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"/>

Java dosyasında, bu yöntem gibi bir yöntem yazmanız gerekir.

public void register(View view) {
}

0

Bu kodu xml dosyasına yazıyorum ...

<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"/>

Ve bu kodu parçalara yaz ...

public void register(View view) {
}

Bu parça halinde mümkündür.
user2786249

0

Bunu yapmanın en iyi yolu aşağıdaki koddur:

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

Bunun soruyu nasıl cevapladığını göremiyorum - asker anonim bir sınıf oluşturmaktan kaçınmak ve bunun yerine bir sınıf yöntemi kullanmak istiyor.
ajshort

0

SetOnClicklistener () içinde hayatınızı kolaylaştırmak ve Anonim Sınıftan kaçınmak için aşağıdaki gibi bir View.OnClicklistener Arayüzü uygulayın:

ortak sınıf YourClass CommonActivity genişletir View.OnClickListener, uygular ...

bu önler:

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

ve doğrudan şuraya gider:

@Override
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.your_view:
      yourMethod();
      break;
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.