android:onClick XML属性はsetOnClickListenerとどの程度正確に異なりますか?


416

私が読んだことonClickから、2つの方法でハンドラーをボタンに割り当てることができます。

android:onClick署名付きのパブリックメソッドの名前だけを使用するXML属性を使用するvoid name(View v)setOnClickListenerOnClickListenerインターフェイスを実装するオブジェクトを渡すメソッドを使用する。後者は、個人的に私が好きではない(個人的な好み)またはを実装する内部クラスを定義する匿名クラスを必要とすることがよくありOnClickListenerます。

XML属性を使用することで、クラスの代わりにメソッドを定義するだけでよいので、XMLレイアウトではなくコードを使用して同じことができるかどうか疑問に思いました。


4
私はあなたの問題を読み、あなたは私のように同じ場所に行き詰まっていると思います。私は自分の問題を解決するのに非常に役立つ非常に良いビデオに出くわしました。次のリンクでビデオを見つけてください:youtube.com/watch ?
v

9
上記のコメントに投稿されたビデオを見て時間を節約したい人のために、2つのボタンがonClickレイアウトファイルの属性に対して同じメソッドを持つことができる方法を単に示しています。これは、パラメータのおかげで行われますView v。あなたは単にif (v == findViewById(R.id.button1))などをチェックします
CodyBugstein '23

13
@Imray v.getId() == R.id.button1実際のコントロールを見つけて比較する必要がないため、を使用する方が良いと思います。そして、あなたswitchはたくさんのifの代わりに使うことができます。
Sami Kuhmonen 14

3
このチュートリアルは非常に役立ちます。ここをクリック
c49

xml android:onClickを使用すると、クラッシュが発生します。

回答:


604

いいえ、それはコードでは不可能です。Android OnClickListenerでは、android:onClick="someMethod"属性を定義するときにが実装されます。

これら2つのコードスニペットは等しく、2つの異なる方法で実装されています。

コードの実装

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
}

上記はのコード実装ですOnClickListener。そして、これはXML実装です。

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

バックグラウンドでは、AndroidはJavaコード以外には何もせず、クリックイベントでメソッドを呼び出します。

上記のXMLでは、Androidは現在のアクティビティでのみonClickメソッドを検索することに注意してくださいmyFancyMethod()。フラグメントを使用して上記のXMLを追加しても、AndroidはonClickメソッドをメソッドで検索しないため、これはフラグメントを使用している場合に覚えておくことが重要です。.java XMLの追加に使用されたフラグメントファイルで。

私が気づいたもう一つの重要なこと。あなたは匿名の方法を好まないと述べました。あなたは匿名のクラスが好きではないと言うつもりでした。


4
私はJavaの第一人者ではありませんが、そうです、匿名クラスを意味しました。お返事をありがとうございます。非常に明確な。
エミッター

118
XML onclickを使用する場合は、onclickメソッド(myFancyMethod())を現在のアクティビティに配置する必要があることに注意してください。あなたが断片を使用している場合、onclickのリスナーを設定するプログラム的な方法は、おそらくそれは考えフラグメントのonCreateView()...でクリック取り扱い方法がありますので、これは重要でありません XMLから参照あれば見つけることを。
ピーターAjtai

12
はい、メソッドは公開する必要があります。
Octavian A. Damiean 2013

12
興味深いことに、それをコードで実行すると、メソッドをプライベートにすることでメソッドへのアクセスを保護できますが、xmlを使用すると、メソッドが公開されます。
bgse 2013

5
関数(XMLアプローチ)がアクティビティ内になければならないという事実は、フラグメントを検討する場合だけでなく、カスタムビュー(ボタンを含む)も重要です。複数のアクティビティで再利用するカスタムビューがあり、すべてのケースで同じonClickメソッドを使用する場合、XMLメソッドは最も便利なものではありません。カスタムビューを使用するすべてのアクティビティに、このonClickMethod(同じボディ)を配置する必要があります。
Bartek Lipinski 2014

87

一番上の答えを見たとき、私の問題はファンシーメソッドにパラメーター(ビューv)を配置しているのではないことに気づきました。

public void myFancyMethod(View v) {}

xmlからアクセスしようとするときは、

android:onClick="myFancyMethod"/>

それが誰かを助けることを願っています。


74

android:onClick APIレベル4以降のため、1.6未満をターゲットにしている場合は使用できません。


33

メソッドを公開するのを忘れたかどうかを確認してください!


1
なぜそれを公開することが義務付けられているのですか?
eRaisedToX 2017年

3
@eRaisedToXかなり明確だと思います。パブリックでない場合、Androidフレームワークから呼び出すことはできません。
m0skit0

28

android:onClick属性を指定すると、ButtonインスタンスがsetOnClickListener内部的に呼び出されます。したがって、違いはまったくありません。

明確に理解するために、XML onClick属性がフレームワークによってどのように処理されるかを見てみましょう。

レイアウトファイルがインフレートされると、その中で指定されているすべてのビューがインスタンス化されます。この特定のケースでは、Buttonインスタンスはpublic Button (Context context, AttributeSet attrs, int defStyle)コンストラクタを使用して作成されます。XMLタグ内のすべての属性がリソースバンドルから読み取られAttributeSet、コンストラクターに渡されます。

Buttonクラスがから継承さViewで結果クラスViewを経由して、クリックコールバックハンドラを設定するの面倒を取るコンストラクタが呼び出され、setOnClickListener

attrs.xmlで定義されているonClick属性は、View.javaではとして参照されR.styleable.View_onClickます。

これがView.java自分で呼び出すことでほとんどの作業を行うコードですsetOnClickListener

 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;

ご覧のとおりsetOnClickListener、コードで行うように、コールバックを登録するために呼び出されます。唯一の違いはそれが使用することですJava Reflection、アクティビティで定義されたコールバックメソッドを呼び出すためされます。

他の回答で言及されている問題の理由は次のとおりです。

  • コールバックメソッドはパブリックである必要がありますJava Class getMethodが使用されているため、パブリックアクセス指定子を持つ関数のみが検索されます。それ以外の場合は、IllegalAccessException例外を処理する準備をしてください。
  • FragmentのonClickでButtonを使用している間、コールバックはActivitygetContext().getClass().getMethod()callで定義する必要があります。メソッド検索を現在のコンテキスト(Fragmentの場合はActivity)に制限します。したがって、メソッドはFragmentクラスではなく、Activityクラス内で検索されます。
  • コールバックメソッドはViewパラメータを受け入れる必要があります:パラメータとしてJava Class getMethod受け入れるメソッドを検索しView.classます。

1
それは私にとって欠けている部分でした-JavaはReflectionを使用して、getContext()で始まるクリックハンドラーを見つけます。クリックがフラグメントからアクティビティにどのように伝播するかは、少し不思議でした。
Andrew Queisser 2014年

15

ここには非常によく答えがありますが、1行追加します。

ではandroid:onclickXMLで、Androidは使用してJavaのリフレクションをこれに対処するためにシーンの後ろに。

そして、ここで説明したように、反射が常にパフォーマンスが低下します。(特にDalvik VM)。登録onClickListenerはより良い方法です。


5
アプリの速度はどの程度低下しますか?:) 0.5ミリ秒。でもない?実際にレイアウトを膨らませるのと比較すると、羽とクジラのようです
Konrad Morawski

14

onClick XML機能を使用する場合、対応するメソッドには1つのパラメーターが必要であり、そのタイプはXMLオブジェクトと一致する必要があります。

たとえば、ボタンは名前文字列を介してメソッドにリンクされますandroid:onClick="MyFancyMethod"が、メソッド宣言は次のように表示されます。 ...MyFancyMethod(View v) {...

この機能をメニュー項目に追加しようとすると、XMLファイルでの構文はまったく同じですが、メソッドは次のように宣言されます。...MyFancyMethod(MenuItem mi) {...


6

クリックリスナーを設定するもう1つの方法は、XMLを使用することです。android:onClick属性をタグに追加するだけです。

可能な場合は常に、匿名Javaクラスに対してxml属性「onClick」を使用することをお勧めします。

まず、コードの違いを見てみましょう。

XML属性/ onClick属性

XML部分

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

Java部分

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

匿名Javaクラス/ setOnClickListener

XML部分

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

Java部分

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

匿名のJavaクラスよりもXML属性を使用する利点は次のとおりです。

  • 匿名Javaクラスでは、常に要素のIDを指定する必要がありますが、XML属性では、IDを省略できます。
  • Anonymous Javaクラスでは、ビュー内の要素(findViewById部分)を積極的に検索する必要がありますが、XML属性ではAndroidがそれを行います。
  • ご覧のように、匿名Javaクラスには少なくとも5行のコードが必要ですが、XML属性では3行のコードで十分です。
  • Anonymous Javaクラスでは、メソッドに「onClick」という名前を付ける必要がありますが、XML属性を使用すると、任意の名前を追加できるため、コードが読みやすくなります。
  • Xmlの「onClick」属性は、APIレベル4のリリース中にGoogleによって追加されました。つまり、これは少し最近の構文であり、ほとんどの場合、最近の構文の方が優れています。

もちろん、Xml属性を常に使用できるとは限りません。これを選択しない理由は次のとおりです。

  • フラグメントで作業している場合。onClick属性はアクティビティにのみ追加できるため、フラグメントがある場合は、匿名クラスを使用する必要があります。
  • onClickリスナーを別のクラスに移動したい場合(おそらく非常に複雑で、アプリケーションのさまざまな部分で再利用したい場合)、xml属性は使用しないでください。どちらか。

また、XML属性を使用して呼び出される関数は常にパブリックである必要があり、それらがプライベートとして宣言されている場合は例外が発生することに注意してください。
Bestin John

5

Java 8では、おそらくメソッドリファレンスを使用して必要なことを実現できます。

これがonClickボタンのイベントハンドラであると想定します。

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

次に、このような呼び出しonMyButtonClickedでインスタンスメソッド参照を渡します setOnClickListener()

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

これにより、匿名クラスを自分で明示的に定義することを回避できます。ただし、Java 8のメソッドリファレンスは実際には単なる構文上の砂糖であることを強調しておきます。実際に匿名クラスのインスタンスを作成します(ラムダ式と同じように)ので、イベントハンドラーの登録解除時にラムダ式スタイルのイベントハンドラーが適用されたのと同様の注意が必要です。この記事はそれが本当に素晴らしいことを説明しています。

PS。AndroidでJava 8言語機能を実際にどのように使用できるかについて知りたい場合は、retrolambdaライブラリをご利用ください。


5

XML属性を使用することで、クラスの代わりにメソッドを定義するだけでよいので、XMLレイアウトではなくコードを使用して同じことができるかどうか疑問に思いました。

はい、作成fragmentまたはactivity実装できますView.OnClickListener

コードで新しいビューオブジェクトを初期化すると、次のようになります mView.setOnClickListener(this);

これは自動的に使用するために、コード内のすべてのビューオブジェクトを設定し、onClick(View v)あなたのことをメソッドfragmentactivityなどがあります。

onClickメソッドを呼び出したビューを区別するために、メソッドでswitchステートメントを使用できますv.getId()

この答えは、「コードでは不可能です」とは異なります。


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の回答をサポートします。そうです。AndroidのXML onclickで使用できるようにするには、メソッドを「パブリック」として宣言する必要があります。APIレベル8(minSdk ...)から16(targetSdk ...)までのターゲットアプリを開発しています。

私は自分のメソッドをプライベートとして宣言していましたが、エラーが発生しました。


ホスティングアクティビティのクラスで宣言された変数は、宣言されたコールバックのスコープでは使用できないようです。Bundle Activity.mBundleは、myFancyMethod()で使用された場合、IllegalStateException / NullPointerExceptionをスローします。
Quasaur 2013

2

が、注意してくださいandroid:onClickXMLは、ハンドルクリックに便利な方法であると思われる、setOnClickListener実装が追加よりも、追加の何かをしますonClickListener。実際、viewプロパティclickableをtrueに設定しました。

ほとんどのAndroid実装では問題にならない可能性がありますが、電話コンストラクターによると、ボタンは常にデフォルトでclickable = trueに設定されていますが、一部の電話モデルの他のコンストラクターでは、Button以外のビューでデフォルトのclickable = falseが設定されている場合があります。

したがって、XMLを設定するだけでは十分ではなく、android:clickable="true"ボタン以外を追加するために常に考える必要があります。デフォルトのclickable = trueであるデバイスを使用していて、このXML属性を設定するのを1回でも忘れた場合、気付かないでしょう。実行時に問題が発生しますが、顧客の手に渡ると市場にフィードバックされます。

さらに、proguardがXML属性とクラスメソッドを難読化して名前を変更する方法がわからないため、100%安全ではなく、いつかバグが発生することはありません。

したがって、問題が発生したくなく、それについて考えたことがない場合は、setOnClickListener注釈付きのButterKnifeなどのライブラリを使用することをお勧めします@OnClick(R.id.button)


onClickXMLの属性も設定しclickable = true、それが呼び出すためsetOnClickListenerView内部
フロリアン・ヴァルター

1

このようなクリックイベントを追加したいとします 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ファイルでは、このメソッドのようなメソッドを記述する必要があります。

public void register(View view) {
}

0

私はこのコードを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"/>

そして、このコードを断片的に書いてください...

public void register(View view) {
}

これは断片的に可能です。
user2786249

0

これを行う最善の方法は、次のコードを使用することです。

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

これが質問にどのように答えるかはわかりません-質問者は匿名クラスの作成を避け、代わりにクラスメソッドを使用します。
ajshort

0

生活を楽にし、setOnClicklistener()の匿名クラスを回避するには、以下のようにView.OnClicklistenerインターフェイスを実装します。

パブリッククラスYourClassは、CommonActivityを拡張してView.OnClickListenerを実装します...

これは避けます:

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

に直接行きます:

@Override
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.your_view:
      yourMethod();
      break;
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.