android : onClick XML 속성이 setOnClickListener와 정확히 어떻게 다릅니 까?


416

나는 당신이 onClick두 가지 방법으로 버튼에 핸들러를 할당 할 수 있다는 것을 읽었습니다 .

android:onClick서명과 함께 공용 메소드의 이름을 사용하는 XML 속성 void name(View v)을 사용하거나 인터페이스 setOnClickListener를 구현하는 오브젝트를 전달하는 메소드 를 사용하십시오 OnClickListener. 후자는 종종 개인적으로 좋아하지 않는 (개인 취향) 또는을 구현하는 내부 클래스를 정의하는 익명 클래스가 필요합니다 OnClickListener.

XML 속성을 사용하면 클래스 대신 메소드를 정의하면되므로 XML 레이아웃이 아닌 코드를 통해 동일한 작업을 수행 할 수 있는지 궁금합니다.


4
나는 당신의 문제를 읽고 당신이 나와 같은 장소에 붙어 있다고 생각합니다. 문제를 해결하는 데 큰 도움이되는 매우 유용한 동영상을 발견했습니다. 다음 링크에서 비디오를 찾으십시오. youtube.com/watch?v=MtmHURWKCmg&feature=youtu.be 이것이 도움이되기를 바랍니다 :)
user2166292

9
위의 의견에 게시 된 비디오를 시청하는 시간을 절약하고자하는 사람들을 위해 onClick레이아웃 파일 의 속성에 대해 두 개의 버튼이 동일한 방법을 갖는 방법을 보여줍니다 . 이 매개 변수 덕분에 수행됩니다 View v. 당신은 단순히 if (v == findViewById(R.id.button1)) 등을 점검합니다 .
CodyBugstein

13
@Imray v.getId() == R.id.button1실제 컨트롤을 찾고 비교할 필요가 없기 때문에을 사용하는 것이 좋습니다 . 그리고 switch많은 if 대신에 사용할 수 있습니다 .
Sami Kuhmonen

3
이 튜토리얼은 많은 도움이 될 것입니다. 여기를 클릭하십시오
c49

xml android : onClick을 사용하면 충돌이 발생합니다.

답변:


604

아니요, 코드로는 불가능합니다. 속성 OnClickListener을 정의 할 때 Android가 자동으로 구현 android:onClick="someMethod"합니다.

이 두 코드 스 니펫은 동일하며 두 가지 다른 방식으로 구현됩니다.

코드 구현

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 코드 외에 다른 방법으로 click 이벤트에서 메소드를 호출합니다.

위의 XML을 사용하면 Android는 현재 활동에서만 onClick메소드를 찾습니다 myFancyMethod(). 프래그먼트를 사용하여 XML을 추가하더라도 Android는 XML을 추가하는 데 사용되는 프래그먼트 파일 에서 onClick메소드를 찾지 않으므로 .java프래그먼트를 사용하는 경우 기억해야합니다.

또 다른 중요한 점을 발견했습니다. 익명의 방법을 선호하지 않는다고 언급했습니다 . 익명 클래스 를 좋아하지 않는다고 말하려고했습니다 .


4
나는 자바 전문가가 아니지만 예, 익명 클래스를 의미했습니다. 답장을 보내 주셔서 감사합니다. 매우 명확한.
emitrax

118
XML onclick을 사용하는 경우 onclick 메소드 ( myFancyMethod())를 현재 활동에 넣어야합니다 . onclick 리스너를 프로그래밍 방식으로 설정하면 프래그먼트의 onCreateView ()에서 클릭을 처리하는 메소드 가 XML에서 참조되는 경우 찾을 수 없으므로 프래그먼트를 사용하는 경우 중요합니다 .
피터 Ajtai

12
예,이 방법은 공개되어야합니다.
Octavian A. Damiean 2013

12
흥미로운 점은 코드에서 코드를 작성하면 메소드를 개인용으로 작성하여 메소드 액세스를 보호 할 수 있지만 XML 방식으로 수행하면 메소드가 노출된다는 것입니다.
bgse

5
XML 접근 방식의 함수가 활동에 있어야한다는 사실은 단편을 고려할 때뿐만 아니라 사용자 정의보기 (버튼 포함)도 중요합니다. 여러 활동에서 재사용 할 수있는 사용자 정의보기가 있지만 모든 경우에 동일한 onClick 메소드를 사용하려는 경우 XML 메소드가 가장 편리한 것은 아닙니다. 사용자 정의보기를 사용하는 모든 활동 에이 onClickMethod (동일한 본문이 있음)를 넣어야합니다.
Bartek Lipinski

87

최고의 답변을 보았을 때 내 문제가 멋진 메소드에 매개 변수 (View v)를 넣지 않았다는 것을 깨달았습니다.

public void myFancyMethod(View v) {}

XML에서 액세스하려고 할 때

android:onClick="myFancyMethod"/>

누군가에게 도움이되기를 바랍니다.


74

android:onClick 는 API 수준 4 이상을위한 것이므로 <1.6을 타겟팅하는 경우 사용할 수 없습니다.


33

메소드를 공개하는 것을 잊었는지 확인하십시오!


1
공개해야하는 이유는 무엇입니까?
eRaisedToX

3
@eRaisedToX 꽤 분명하다고 생각합니다. 공개되지 않으면 Android 프레임 워크에서 호출 할 수 없습니다.
m0skit0

28

android:onClick속성을 지정 하면 Button인스턴스가 setOnClickListener내부적으로 호출 됩니다. 따라서 아무런 차이가 없습니다.

명확한 이해 onClick를 위해 프레임 워크가 XML 속성을 처리 하는 방법을 살펴 보겠습니다 .

배치 파일이 팽창되면 여기에 지정된 모든 뷰가 인스턴스화됩니다. 이 경우 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을 사용하는 동안 콜백은 Activity에서 정의되어야합니다 . getContext().getClass().getMethod()call은 메소드 검색을 현재 컨텍스트 (Fragment의 경우 Activity)로 제한합니다. 따라서 메소드는 Fragment 클래스가 아닌 Activity 클래스 내에서 검색됩니다.
  • 콜백 방법은보기 매개 변수를 받아 들여야한다 가입일 : Java Class getMethod받아들이는 방법에 대한 검색 View.class매개 변수로합니다.

1
Java는 Reflection을 사용하여 getContext ()로 시작하는 클릭 핸들러를 찾습니다. 클릭이 프래그먼트에서 액티비티로 전파되는 방식은 조금 신비했습니다.
Andrew Queisser 2016 년

15

여기에 매우 좋은 답변이 있지만 한 줄을 추가하고 싶습니다.

에서 android:onclickXML에서, 안드로이드는 사용하는 자바 반사를 이 처리하는 장면 뒤에.

그리고 여기에 설명 된 바와 같이, 반사는 항상 성능이 저하. (특히 Dalvik VM에서). 등록 onClickListener하는 것이 더 좋은 방법입니다.


5
앱 속도가 얼마나 느려질 수 있습니까? :) 반 밀리 초; 심지어? 실제로 레이아웃을 팽창시키는 것에 비해 깃털과 고래와 같습니다
Konrad Morawski

14

onClick XML 기능을 사용하려면 해당 메소드에 하나의 매개 변수가 있어야하며이 매개 변수의 유형은 XML 오브젝트와 일치해야합니다.

예를 들어, 버튼 은 이름 문자열을 통해 메소드에 연결 android:onClick="MyFancyMethod"되지만 메소드 선언에는 다음이 표시되어야합니다. ...MyFancyMethod(View v) {...

이 기능을 메뉴 항목 에 추가하려고 하면 XML 파일에 동일한 구문이 사용되지만 메소드는 다음과 같이 선언됩니다....MyFancyMethod(MenuItem mi) {...


6

클릭 리스너를 설정하는 또 다른 방법은 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"/>

자바 부분

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

익명의 Java 클래스 / setOnClickListener

XML 부분

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

자바 부분

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

익명 Java 클래스에 비해 XML 속성을 사용하면 다음과 같은 이점이 있습니다.

  • Anonymous Java 클래스를 사용하면 항상 요소의 ID를 지정해야하지만 XML 속성으로 id를 생략 할 수 있습니다.
  • Anonymous Java 클래스를 사용하면 뷰 내부의 요소 (findViewById 부분)를 적극적으로 검색해야하지만 XML 속성을 사용하면 Android 가이를 수행합니다.
  • 우리가 볼 수 있듯이 익명 Java 클래스에는 최소한 5 줄의 코드가 필요하지만 XML 속성으로는 3 줄의 코드로 충분합니다.
  • Anonymous Java 클래스를 사용하면 메소드의 이름을 "onClick"으로 지정해야하지만 XML 속성을 사용하면 원하는 이름을 추가 할 수 있으므로 코드의 가독성에 크게 도움이됩니다.
  • API 레벨 4 릴리스 중에 Google에서 Xml "onClick"속성을 추가했습니다. 이는 조금 더 현대적인 구문이며 현대적인 구문은 거의 항상 더 좋습니다.

물론 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의 Method Reference는 실제로 구문 설탕이라는 것을 강조해야합니다. 실제로 람다 식처럼 익명 클래스의 인스턴스를 만듭니다. 따라서 람다 식 스타일의 이벤트 처리기가 이벤트 처리기 등록을 취소 할 때 적용된 것과 비슷한주의가 필요합니다. 이 기사 는 정말 좋은 설명합니다.

추신. Android에서 Java 8 언어 기능을 실제로 어떻게 사용할 수 있는지 궁금한 사람들에게는 retrolambda 라이브러리 가 제공 합니다.


5

XML 속성을 사용하면 클래스 대신 메소드를 정의하면되므로 XML 레이아웃이 아닌 코드를 통해 동일한 작업을 수행 할 수 있는지 궁금합니다.

예, 당신은 당신을 만들 fragment거나 activity구현할 수 있습니다View.OnClickListener

코드에서 새 뷰 객체를 초기화하면 간단히 할 수 있습니다. mView.setOnClickListener(this);

그러면 코드의 모든보기 객체가 자동으로 설정 onClick(View v)되어 귀하 fragment또는 activity기타가 가지고 있는 메소드 를 사용합니다 .

어떤 뷰가 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에서 사용할 수 있도록 메소드를 "public"으로 선언해야합니다. API 레벨 8 (minSdk ...)에서 16 (targetSdk ...)까지 앱 타겟팅을 개발 중입니다.

내 방법을 개인으로 선언하고 오류가 발생하여 공용으로 작동한다고 선언했습니다.


호스팅 액티비티 클래스에 선언 된 변수는 선언 된 콜백 범위에서 사용할 수 없습니다. 번들 Activity.mBundle은 myFancyMethod ()에서 사용되는 경우 IllegalStateException / NullPointerException을 발생시킵니다.
Quasaur

2

하지만, 조심 android:onClickXML 핸들을 클릭 할 수있는 편리한 방법이 될 것 같다의 setOnClickListener구현을 추가하는 것보다 추가로 뭔가를 onClickListener. 실제로 view 속성 clickable을 true로 설정했습니다.

전화 생성자에 따르면 대부분의 Android 구현에서 문제가되지는 않지만 버튼은 항상 clickable = true로 설정되지만 일부 전화 모델의 다른 생성자는 Button이 아닌 뷰에서 기본 clickable = false를 가질 수 있습니다.

따라서 XML을 설정하는 것만으로는 충분하지 않으므로 항상 추가해야합니다. android:clickable="true" 버튼이 아닌 버튼 하며 기본값이 clickable = true 인 장치가 있고이 XML 속성을 한 번이라도 잊어 버린 경우 눈치 채지 못할 것입니다 런타임에 문제가 있지만 고객의 손에있을 때 시장에 대한 피드백을 얻을 것입니다!

또한 proguard가 XML 속성 및 클래스 메서드를 난독 화하고 이름을 바꾸는 방법에 대해 확신 할 수 없으므로 언젠가는 버그가 발생하지 않도록 100 % 안전하지 않습니다.

따라서 문제가 발생하지 않고 생각하지 않으려면 setOnClickListenerButterKnife와 같은 라이브러리를 주석과 함께 사용하는 것이 좋습니다@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
            }
        });

이것이 어떻게 질문에 대답하는지 알지 못합니다-asker는 익명 클래스를 만드는 것을 피하고 대신 클래스 메소드를 사용하려고합니다.
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.