반응형

안드로이드 앱을 개발하면서 가장 많이 사용하게 되시는 메소드 중 하나가 findViewById() 입니다.

안드로이드 앱개발에서 화면 UI는 xml언어를 사용하고 컨트롤을 위한 코드는 자바 or 코틀린 언어를 사용하지요.

그렇기에 xml언어의 마크업을 통해 아주 가독성이 좋고 수월하게 UI를 구현할 수 있습니다.

이렇게 xml에서 <>마크업을 통해 만들어진 View들을 자바에서 제어하기 위해서는 xml에서 만든 뷰들을 찾아와서 참조해야 할 필요가 있습니다. 즉, 자바나 코틀린에서 xml의 뷰들을 찾아와 제어하는 방식으로 안드로이드 앱을 구현합니다.

 

이때 사용하는, 뷰를 찾아오는 기능 메소드가 모두 다 아시는 findViewById()라는 액티비티의 기능메소드 입니다.

화면 UI를 제어하기 위해서는 반드시 필요한 기능 메소드인 것이죠. 액티비티, 프레그먼트, 리사이클러뷰 아답터 등, 모든 뷰를 제어해야 하는 곳에서 사용되어 집니다. 그렇기에 너무나 많이 사용되어 질 수 밖에 없습니다.

 

이제 안드로이드 학습을 시작하시는 초급 개발자분들은 화면이 단순한 앱들을 위주로 실습을 하며 앱을 만들어보는 경우가 많기에 크게 문제점을 느끼지 못하는 경우가 많지만 실무에서 개발되는 앱들에서는 UI가 다소 복잡하고 중첩적으로 되어 있는 경우가 많아서 뷰들을 찾아와서 참조하는 findViewById() 가 무척 번거롭게 느껴집니다. 또한, findViewId()메소드의 가장 치명적인 문제인 성능이슈(속도가 느림-비용이 비싼메소드라고 부름)로 인해 앱 개발자들을 고민이 빠지게 합니다. 리스트뷰에서 ViewHolder가 등장하고 결국 RecyclerView로 넘어간 과정도 바로 이 findViewById()문제가 크게 반영되었다고 할 수 있습니다.

 

이번에 소개할 내용은 바로 이 findViewById() 이슈를 해결하기 위한 방법들 중 가장 많이 알려진 기법들 입니다.

findViewById()메소드를이용한View참조방법이가진문제를해결하기위한기법들[코드의번거로움과메소드의동작속도문제]

1. 
ButterKnife 외부 라이브러리 사용하기 (버터 나이프 라이브러리)

2. ViewBinding 안드로이드 아키텍처 구성요소
3. DataBinding (view binding + data binding) : 뷰결합 + 데이터 결합 기능까지 가진 아키텍처 구성요소

 

이번 예제에서는 위 3가지 중 많은 안드로이드 개발자로부터 애용되어 오던 JakeWharton 의 ButterKnife 라이브러리를 소개하겠습니다.

 

먼저. ButterKnife 라이브러리를 추가합니다.

[ dependency library - 구글 검색 or [File]메뉴의 [Project Structure]메뉴를 통할 라이브러리 적용

*주의!* 어노테이션을 사용하여 뷰들을 바인딩 하기에 [build.gradle]에 반드시 버터나이프 어노테이션프로페서도 추가해야함

- 버터나이프 GitHub 참고 [ https://github.com/JakeWharton/butterknife ]

# build.gradle(Module.app)
...

dependencies {

    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.0'

    //ButterKnife(버터나이프) 라이브러리 추가
    implementation 'com.jakewharton:butterknife:10.2.3'
    //어노테이션을 사용하여 뷰들을 바인딩 하기에 반드시 버터나이프 어노테이션프로페서도 추가해야함
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

 

버터나이프의 여러 사용을 연습해 보기 위해 1)~6)까지 실습을 확장해 나갈겁니다.

실습을 진행하면서 점점 코드가 확장되어 소개되니 해당 번호의 코드 영역을 구별하여 보시기 바랍니다.

 

1) 먼저 간단하게 TextView 1개를 만들고 이를 버터나이프로 참조하여 글씨를 변경해 보는 코드를 알아보겠습니다.

 

1.1) 먼저 activity_main.xml 문서에 TextView 1개를 만들어 보겠습니다. 다음 실습을 편하게 하기위해 LinearLayout으로 배치합니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <!-- 1) 기본 TextView  -->
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="Hello"/>   

</LinearLayout>

 

1.2) MainActivity.java에서 이 TextView가 보여주는 글씨인 "Hello"를 다른 글씨로 변경해 보겠습니다.

- 먼저 액티비티가 보여주는 뷰들을 ButterKnife를 통해 연결하는 작업을 합니다.

- 액티비티의 onCreate()에서 버터나이프가 연결할 뷰들을 가진 타겟인 액티비티를 연결합니다. ButterKnife.bind()

- Butter Knife 는 @어노테이션을 통해 xml에서 만든 뷰들을 자바의 참조변수와 연결합니다. (id를 통해 연결할 뷰 구별)

- 각 코드들에 대한 설명은 코드를 보면서 확인해보시는게 이해가 수월하기에 주석에 썼으니 참고하시면서 보시기 바랍니다.

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.widget.TextView;

import butterknife.BindView;
import butterknife.ButterKnife;

public class MainActivity extends AppCompatActivity {   

    //Butter Knife Librar의 @(어노테이션)을 이용하여 이 액티비티의 뷰들을 연결 - findViewById를 하지 않음
    @BindView(R.id.tv) TextView tv; //id가 tv인 뷰를 참조하는 참조변수 선언

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //ButterKnife가 연결해줄 뷰들을 가진 타겟(Activity)과 연결
        ButterKnife.bind(this);

        //@BindView 어노테이션을 통해 참조된 TextView의 끌씨 변경하기
        tv.setText("Nice to meet you. Butter Knife!");
    }
}

- 코드에서 확인해볼 수 있듯이 액티비티에서는 ButterKnife.bind(this) 를 통해 버터나이프가 연결할 뷰를 가진 타겟을 지정하고

- @BindView(뷰 id) 어노테이션을 통해 findViewById()의 명시적 표기 없이도 옆에 선언한 참조변수와 뷰가 연결됩니다.

- 보시는 것보다 실제 코드를 작성해 보면 얼마나 편한지 확실하게 느끼실 수 있으실 겁니다. 개수가 많아질 수록 더 강하게 느껴집니다.

 

이 ButterKnife의 @어노테이션은 단순히 뷰를 연결하는 @BindView 만 있는 것을 아닙니다.

버터나이프의 @어노테이션 중 가장 활용이 많은 것들 몇가지를 소개하고 실습해보겠습니다.

1) @BindView   - id를 통해 구별된 뷰들을 연결하는 어노테이션
2) @OnClick     - 일반 메소드를 클릭이벤트 콜백메소드로 연결. 롱~클릭, 체크드체인지 등 이벤트 콜백메소드로 연결하는 어노테이션도 있음.
3) @BindString - res폴더에 있는 리소소 값들과 연결하는 어노테이션. @BindInt, @BindColor 등 다른 리소스들도 연결할 수 있음.

 

2) 버튼 클릭이벤트 콜벡메소드를  Butter Knife 로 연결하기

- 버튼을 클릭하였을때 반응하는 콜백메소드를, 개발자가 만든 임의의 일반 메소드가 반응하도록 하는 기법입니다.

 

- 먼저 Button 뷰를 하나 추가하고 id를 "btn"으로 지정합니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <!-- 1) 기본 TextView  -->
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="Hello"/>

    <!-- 2) 버튼 클릭이벤트를 ButterKinfe로 임의의 메소드와 연결하여 콜백처리하기 -->
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"/>

</LinearLayout>

 

- 자바에서 id가 btn인 버튼이 클릭되면 발동하는 콜백메소드를 버터나이프의 @OnClick 으로 지정하여 처리해 보겠습니다.

- 주석을 통해 상세한 설명을 하니 참고바랍니다.

- 롱~클릭 콜백처리도 같은 방식으로 적용할 수 있습니다. 다른 콜백이벤트들도 처리 가능함.

# MainActivity.java
public class MainActivity extends AppCompatActivity {

    //1) 먼저 간단하게 TextView 1개를 만들고 이를 버터나이프로 참조하여 글씨 변경해 보기
    // - 사용초기화 -
    // 액티비티의 onCreate()에서 버터나이프가 연결할 뷰들을 가진 타겟과 연결! ButterKnife.bind()


    //Butter Knife Librar의 @(어노테이션)을 이용하여 이 액티비티의 뷰들을 연결 - findViewById를 하지 않음
    @BindView(R.id.tv) TextView tv; //id가 tv인 뷰를 참조하는 참조변수 선언

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //ButterKnife가 연결해줄 뷰들을 가진 타겟(Activity)과 연결
        ButterKnife.bind(this);

        //@BindView 어노테이션을 통해 참조된 TextView의 끌씨 변경하기
        tv.setText("Nice to meet you. Butter Knife!");
    }

    //2) 버튼 클릭이벤트 콜백리스너를 Butter Knife 로 연결하기
    // - onClick속성에 지정한 메소드가 아님. 그냥 일반 메소드인데 버터나이프의 어노테이션을 통해 클릭이벤트 처리용 콜백메소드로 연결
    @OnClick(R.id.btn)  // id가 btn인 뷰의 OnClick 이벤트 연결
    void clickBtn(){
        Toast.makeText(this, "clicked button", Toast.LENGTH_SHORT).show();
    }

    //2-1) 당연히 onClick외에 다른 이벤트 콜백처리도 모두 가능함
    @OnLongClick(R.id.btn)
    boolean longClickBtn(){
        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
        return true; // 리턴값을 true 로 하지 않으면 long click 이벤트 후에 onClick 이벤트도 처리됨
    }

    //원래 LongClick이벤트처리 콜백메소드는 리턴값이 있는 메소드여야 하지만 버터나이프로 연결할 때는 return을 안해도 발동함.[단, 리턴값은 기본 true]
//    @OnLongClick(R.id.btn)
//    void longClickBtn(){
//        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
//    }

    //@OnCheckedChanged ,@OnItemClick 등 대표적인 콜백메소드 모두 사용가능함

}

 

 

3) 뷰 참조와 클릭이벤트 처리 종합 simple 예제 실습

- @BindView 와 @OnClick 을 모두 연습해 보는 코드를 작성해 보겠습니다.

- EditText 에 글씨를 입력하고 버튼을 누르면 EditText에 써있는 글씨를 얻어와서 TextView에 보여주도록 하겠습니다.

 

먼저, UI작업을 하기 위해 xml 레이아웃파일에 3개의 뷰를 추가하겠습니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <!-- 1) 기본 TextView  -->
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="Hello"/>

    <!-- 2) 버튼 클릭이벤트를 ButterKinfe로 임의의 메소드와 연결하여 콜백처리하기 -->
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"/>

    <!-- 3) EditText의 글씨을 얻어와서 TextView에 보이는 예제실습 [뷰참조와 클릭이벤트 종합 simple 예제]-->
    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="enter text"
        android:inputType="text"
        android:layout_marginTop="24dp"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="입력완료"/>
    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="RESULT"/>

</LinearLayout>

 

이제 버터나이프를 이용하여 뷰들을 제어해 보겠습니다.

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Context;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

import butterknife.BindString;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnCheckedChanged;
import butterknife.OnClick;
import butterknife.OnEditorAction;
import butterknife.OnItemClick;
import butterknife.OnLongClick;

public class MainActivity extends AppCompatActivity {

    // findViewById() 메소드를 이용한 View 참조 방법이 가진 문제를 해결하기 위한 기법들 [ 코드의 번거로움과 메소드의 동작속도 문제 ]
    // 1. ButterKnife 외부 라이브러리 사용하기
    // 2. ViewBinding 안드로이드 아키텍처 구성요소
    // 3. DataBinding (view binding + data binding) : 뷰결합 + 데이터 결합 기능까지 가진 아키텍처 구성요소

    // 이 실습예제에서는 위 3가지 중 많은 안드로이드 개발자로부터 애용되어 오던 JakeWharton 의 ButterKnife 라이브러리 소개
    // ButterKnife 라이브러리 추가 [ dependency library - 구글 검색 or [File]메뉴의 [Project Structure]메뉴를 통한 라이브러리 적용 ]
    //   *주의!* 어노테이션을 사용하여 뷰들을 바인딩 하기에 [build.gradle]에 반드시 버터나이프 어노테이션프로페서도 추가해야함 - 버터나이프 GitHub 참고 [ https://github.com/JakeWharton/butterknife ]

    //1) 먼저 간단하게 TextView 1개를 만들고 이를 버터나이프로 참조하여 글씨 변경해 보기
    // - 사용초기화 -
    // 액티비티의 onCreate()에서 버터나이프가 연결할 뷰들을 가진 타겟과 연결! ButterKnife.bind()


    //Butter Knife Librar의 @(어노테이션)을 이용하여 이 액티비티의 뷰들을 연결 - findViewById를 하지 않음
    @BindView(R.id.tv) TextView tv; //id가 tv인 뷰를 참조하는 참조변수 선언

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //ButterKnife가 연결해줄 뷰들을 가진 타겟(Activity)과 연결
        ButterKnife.bind(this);

        //@BindView 어노테이션을 통해 참조된 TextView의 끌씨 변경하기
        tv.setText("Nice to meet you. Butter Knife!");
    }

    //2) 버튼 클릭이벤트 콜백리스너를 Butter Knife 로 연결하기
    // - onClick속성에 지정한 메소드가 아님. 그냥 일반 메소드인데 버터나이프의 어노테이션을 통해 클릭이벤트 처리용 콜백메소드로 연결
    @OnClick(R.id.btn)  // id가 btn인 뷰의 OnClick 이벤트 연결
    void clickBtn(){
        Toast.makeText(this, "clicked button", Toast.LENGTH_SHORT).show();
    }

    //2-1) 당연히 onClick외에 다른 이벤트 콜백처리도 모두 가능함
    @OnLongClick(R.id.btn)
    boolean longClickBtn(){
        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
        return true; // 리턴값을 true 로 하지 않으면 long click 이벤트 후에 onClick 이벤트도 처리됨
    }

    //원래 LongClick이벤트처리 콜백메소드는 리턴값이 있는 메소여야 하지만 버터나이프로 연결할 때는 return을 안해도 발동함.[단, 리턴값을 기본 true]
//    @OnLongClick(R.id.btn)
//    void longClickBtn(){
//        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
//    }

    //@OnCheckedChanged ,@OnItemClick 등 대표적인 콜백메소드 모두 사용가능함


    //3) 뷰 참조와 클릭이벤트 처리 종합 simple 예제 실습
    @BindView(R.id.et) EditText et;           //id가 et인 뷰를 참조하는 참조변수 선언
    @BindView(R.id.tv2) TextView tv2;         //id가 tv2인 뷰를 참조하는 참조변수 선언

    @OnClick(R.id.btn2) // id가 btn2인 뷰의 OnClick 이벤트 연결
    public void clickBtn2(){
        tv2.setText(et.getText().toString());
        et.setText(""); //EditText의 글씨 지우기
    }

    //3-1) *추가* EditText의 글씨 입력을 위한 SoftKeyboard의 작성 완료버튼(Action DONE)을 눌렀을때 [입력완료]버튼과 같은 동작이 되도록 하기 위해 이벤트 연결
    @OnEditorAction(R.id.et)
    public boolean softKeyboardAction(TextView v, int actionId, KeyEvent event){
        tv2.setText(et.getText().toString());
        et.setText(""); //EditText의 글씨 지우기

        //소프트키보드 안보이도록 하기
        InputMethodManager imm= (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        //1. hideSoftInputFromWindow flag에 0를 주면 어떠한 상황에서든 닫힘
        //2. hideSoftInputFromWindow flag에 HIDE_NOT_ALWAYS를 주면 SHOW_FORCED를 제외하고는 닫힘
        //3. hideSoftInputFromWindow flag에 HIDE_IMPLICIT_ONLY 를 주면, SHOW_IMPLICIT 로 보여진 키보드만 닫힘, 하지만, 보여준 view의 포커스가 변하면 닫히지 않음.
        return false;
    }

}

 

 

4) res폴더의 리소스 데이터들과도 바인딩하여 값을 가져와 사용할 있음.

[ Ex. res>>vlaues>>strings.xml 문서의 문자열 리소스 연결 실습 ]

- 안드로이드는 앱에 사용되는 여러값들을 자바언어에서 만들지 않고 리소스로서 별도의 xml 로 분리하여 만들고 사용합니다.

- 문자열은 strings.xml, 색상은 colors.xml, 치수는 dimens.xml 등..

- 이 값들을 사용할 때 R.string.xxx 와 같이 리소스ID를 통해 자바나 레이아웃 xml 에 적용합니다.

- 하지만 경우에 따라서는 자바언어에서 리소스에 있는 데이터를 value로서 직접 사용해야 하는 경우도 있습니다. 보통 그럴때는 res폴더를 관리하는 폴더관리 객체인 Resources 객체를 얻어와서 [ getResources() ] 해당 리소스ID의 데이터를 가져오는 작업이 필요하지요. 이 작업을 버터나이프의 @BindXXX 어노테이션으로 간단하게 적용할 수 있습니다.

 

- 리소스의 종류만 다를 뿐 적용하는 방법은 동일하기에 단순한 예제로 소개하고자 합니다.

- 문자열 데이터를 res>>vlaues>>strings.xml 문서에 만들고 이를 자바언어에서 String 변수에 연결하여 TextView에 보여주겠습니다.

 

먼저, 문자열 데이터를 res폴더의 values폴더안에 있는 strings.xml에 추가하겠습니다.

# strings.xml
<resources>
    <string name="app_name">Ex97ButterKnifeLibrary</string>

    <!-- MainActivity의 TextView(tv3)에 보여질 글씨를 가진 String 리소스 -->
    <string name="aaa">Good Butter Knife</string>
</resources>

이 문자열 리소스를 보여주기 위한 뷰로 TextView를 하나 추가하고 'tv3' 이라고 id를 부여하겠습니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <!-- 1) 기본 TextView  -->
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="Hello"/>

    <!-- 2) 버튼 클릭이벤트를 ButterKinfe로 임의의 메소드와 연결하여 콜백처리하기 -->
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"/>

    <!-- 3) EditText의 글씨을 얻어와서 TextView에 보이는 예제실습 [뷰참조와 클릭이벤트 종합 simple 예제]-->
    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="enter text"
        android:inputType="text"
        android:layout_marginTop="24dp"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="입력완료"/>
    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="RESULT"/>

    <!-- 4) res폴더의 리소스 데이터들과도 바인딩 가능함 [ Ex. res>>vlaues>>strings.xml 문서의 문자열 리소스 연결 실습 ]-->
    <TextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:padding="8dp"
        android:textColor="#FF111111"/>

</LinearLayout>

id가 'tv3'인 TextView에는 아직 글씨가 없습니다. 그 글씨를 자바에서 설정하겠습니다. 

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Context;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

import butterknife.BindString;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnCheckedChanged;
import butterknife.OnClick;
import butterknife.OnEditorAction;
import butterknife.OnItemClick;
import butterknife.OnLongClick;

public class MainActivity extends AppCompatActivity {

    // findViewById() 메소드를 이용한 View 참조 방법이 가진 문제를 해결하기 위한 기법들 [ 코드의 번거로움과 메소드의 동작속도 문제 ]
    // 1. ButterKnife 외부 라이브러리 사용하기
    // 2. ViewBinding 안드로이드 아키텍처 구성요소
    // 3. DataBinding (view binding + data binding) : 뷰결합 + 데이터 결합 기능까지 가진 아키텍처 구성요소

    // 이 실습예제에서는 위 3가지 중 많은 안드로이드 개발자로부터 애용되어 오던 JakeWharton 의 ButterKnife 라이브러리 소개
    // ButterKnife 라이브러리 추가 [ dependency library - 구글 검색 or [File]메뉴의 [Project Structure]메뉴를 통한 라이브러리 적용 ]
    //   *주의!* 어노테이션을 사용하여 뷰들을 바인딩 하기에 [build.gradle]에 반드시 버터나이프 어노테이션프로페서도 추가해야함 - 버터나이프 GitHub 참고 [ https://github.com/JakeWharton/butterknife ]

    //1) 먼저 간단하게 TextView 1개를 만들고 이를 버터나이프로 참조하여 글씨 변경해 보기
    // - 사용초기화 -
    // 액티비티의 onCreate()에서 버터나이프가 연결할 뷰들을 가진 타겟과 연결! ButterKnife.bind()


    //Butter Knife Librar의 @(어노테이션)을 이용하여 이 액티비티의 뷰들을 연결 - findViewById를 하지 않음
    @BindView(R.id.tv) TextView tv; //id가 tv인 뷰를 참조하는 참조변수 선언

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //ButterKnife가 연결해줄 뷰들을 가진 타겟(Activity)과 연결
        ButterKnife.bind(this);

        //@BindView 어노테이션을 통해 참조된 TextView의 끌씨 변경하기
        tv.setText("Nice to meet you. Butter Knife!");
    }

    //2) 버튼 클릭이벤트 콜백리스너를 Butter Knife 로 연결하기
    // - onClick속성에 지정한 메소드가 아님. 그냥 일반 메소드인데 버터나이프의 어노테이션을 통해 클릭이벤트 처리용 콜백메소드로 연결
    @OnClick(R.id.btn)  // id가 btn인 뷰의 OnClick 이벤트 연결
    void clickBtn(){
        Toast.makeText(this, "clicked button", Toast.LENGTH_SHORT).show();
    }

    //2-1) 당연히 onClick외에 다른 이벤트 콜백처리도 모두 가능함
    @OnLongClick(R.id.btn)
    boolean longClickBtn(){
        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
        return true; // 리턴값을 true 로 하지 않으면 long click 이벤트 후에 onClick 이벤트도 처리됨
    }

    //원래 LongClick이벤트처리 콜백메소드는 리턴값이 있는 메소여야 하지만 버터나이프로 연결할 때는 return을 안해도 발동함.[단, 리턴값을 기본 true]
//    @OnLongClick(R.id.btn)
//    void longClickBtn(){
//        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
//    }

    //@OnCheckedChanged ,@OnItemClick 등 대표적인 콜백메소드 모두 사용가능함


    //3) 뷰 참조와 클릭이벤트 처리 종합 simple 예제 실습
    @BindView(R.id.et) EditText et;           //id가 et인 뷰를 참조하는 참조변수 선언
    @BindView(R.id.tv2) TextView tv2;         //id가 tv2인 뷰를 참조하는 참조변수 선언

    @OnClick(R.id.btn2) // id가 btn2인 뷰의 OnClick 이벤트 연결
    public void clickBtn2(){
        tv2.setText(et.getText().toString());
        et.setText(""); //EditText의 글씨 지우기
    }

    //3-1) *추가* EditText의 글씨 입력을 위한 SoftKeyboard의 작성 완료버튼(Action DONE)을 눌렀을때 [입력완료]버튼과 같은 동작이 되도록 하기 위해 이벤트 연결
    @OnEditorAction(R.id.et)
    public boolean softKeyboardAction(TextView v, int actionId, KeyEvent event){
        tv2.setText(et.getText().toString());
        et.setText(""); //EditText의 글씨 지우기

        //소프트키보드 안보이도록 하기
        InputMethodManager imm= (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        //1. hideSoftInputFromWindow flag에 0를 주면 어떠한 상황에서든 닫힘
        //2. hideSoftInputFromWindow flag에 HIDE_NOT_ALWAYS를 주면 SHOW_FORCED를 제외하고는 닫힘
        //3. hideSoftInputFromWindow flag에 HIDE_IMPLICIT_ONLY 를 주면, SHOW_IMPLICIT 로 보여진 키보드만 닫힘, 하지만, 보여준 view의 포커스가 변하면 닫히지 않음.
        return false;
    }



    //4) res폴더의 리소스 데이터들과도 바인딩하여 값을 가져와 사용할 수 있음. [ Ex. res>>vlaues>>strings.xml 문서의 문자열 리소스 연결 실습 ]
    //액티비티가 화면에 보여지기 시작할 때 TextView(tv3)의 글씨를 String리소스의 값으로 설정
    @BindView(R.id.tv3) TextView tv3;
    @BindString(R.string.aaa) String resStr;  // res>>vlaues>>strings.xml 문서의 문자열 리소스 객체(name속성이 "aaa"인 <string>)를 연결하여 참조하는 String참조변수

    @Override
    protected void onStart() {
        super.onStart();
        //@BindString(R.string.aaa) String resStr;  // 지역변수로는 @Bind 를 사용할 수 없음
        tv3.setText(resStr);
    }

// ### Resource Bind #####################################################################################
// 아래의 리소스 bind를 지원함.//
// @BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString, @BindArray, @BindAnim .....

// @BindString(R.string.title) String title;
// @BindBool(R.bool.isTed) boolean isTed;
// @BindInt(R.integer.myinteger) int myinteger;
// @BindDrawable(R.drawable.graphic) Drawable graphic;
// @BindColor(R.color.red) int red;
// @BindDimen(R.dimen.spacer) Float spacer;
// @BindDimen(R.dimen.intvalue) int intvalue;
// ................
// ########################################################################################################

}

- 다른 리소스 타입들에 대해서는 주석으로 소개하였으니 참고 바랍니다.

 

 

5) 프레그먼트에서 버터나이프 실습 [ MyFragment.java ]

- 실제 앱을 구현해보면 액티비티에서 직접 뷰들을 배치하여 사용하기 보다 화면의 부분별로 별도의 Fragment 로 나누어 UI를 분리해서 작업하는 경우가 더 많습니다. 화면구성이 단순하지 않기에 액티비티에 모든 뷰들을 배치하면 관리도 어렵고 코드도 복잡하기 때문이지요.

- 이번에는 Fragment에서 버터나이프를 이용하여 뷰들을 제어해 보겠습니다.

- 액티비티에 프레그먼트를 추가하는 방법은 자바로 add 하는 방법이 일반적으로 더 많이 사용되지만 실습의 단순화를 위해 액티비티의 xml에 <fragment>로 정적추가 하겠습니다.

- Fragment를 상속하는 MyFragment 클래스를 별도의 .java 로 만들어 적용해 보겠습니다.

 

먼저, MyFragment 에서 보여줄 레이아웃 파일을 만들어 보겠습니다. 간단하게 구성하기 위해 TextView 1개와 Button 1개만 으로 구성하여 버튼을 클릭했을 때 TextView의 글씨를 변경하도록 하겠습니다.

# fragment_my.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:padding="16dp">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="@color/white"
        android:text="This is MyFragment"
        android:textStyle="bold"/>
    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tv"
        android:backgroundTint="@color/teal_200"
        android:text="chnage text"
        android:textAllCaps="false"/>

</RelativeLayout>

이제 위 레이아웃파일을 보여주고 제어하는 MyFragment.java 를 만들어 보겠습니다.

# MyFragment.java
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MyFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //프레그먼트가 보여줄 뷰 만들기 [ res/layout 폴더 안에 있는 fragment_my.xml의 레이아웃 모양으로 뷰객체 생성(부풀리기) ]
        View view= inflater.inflate(R.layout.fragment_my, container, false);

        //ButterKnife에게 연결할 뷰들을 가진 이 프레그먼트객체를 타겟으로 지정하고 프레그먼트가 보여주는 뷰들을 가진 위에서 만든(inflate를 한) 뷰와 연결!
        ButterKnife.bind(this, view); //액티비티에서 할때와 조금 다름.

        return view;
    }

    //프레그먼트의 뷰참조변수들과 버터나이트로 연결하고 클릭이벤트 처리까지 실습 - 액티비티의 실습과 동일함. [id가 같은 뷰들을 연결하여도 서로 다른 target 이기에 각자의 뷰들을 잘 참조함 - 문제 없음]
    @BindView(R.id.tv) TextView tv;

    @OnClick(R.id.btn)
    public void clickBtn(){
        tv.setText("Have a good day.");
    }
}

- 뷰참조가 간결해지고 onClick속성으로 처리가 안되는 프레그먼트에서 @OnClick으로 손쉽게 클릭이벤트처리가 가능한 것을 볼 수 있습니다.

 

- 마지막으로 액티비티 레이아웃파일에 <fragment>를 통해 정적으로 MyFragment를 추가해 주겠습니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <!-- 1) 기본 TextView  -->
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="Hello"/>

    <!-- 2) 버튼 클릭이벤트를 ButterKinfe로 임의의 메소드와 연결하여 콜백처리하기 -->
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"/>

    <!-- 3) EditText의 글씨을 얻어와서 TextView에 보이는 예제실습 [뷰참조와 클릭이벤트 종합 simple 예제]-->
    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="enter text"
        android:inputType="text"
        android:layout_marginTop="24dp"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="입력완료"/>
    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="RESULT"/>

    <!-- 4) res폴더의 리소스 데이터들과도 바인딩 가능함 [ Ex. res>>vlaues>>strings.xml 문서의 문자열 리소스 연결 실습 ]-->
    <TextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:padding="8dp"
        android:textColor="#FF111111"/>


    <!-- 5) Fragment에서 버터나이프 사용하기 예제 실습   -->
    <fragment
        android:id="@+id/frag"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:name="com.mrhi2021.ex97butterknifelibrary.MyFragment"
        tools:layout="@layout/fragment_my"
        android:layout_marginTop="24dp"/>
    <!-- tools:layout="@layout/fragment_my" 화면의 한 부분을 담당하는 MyFragment의 모양을 이곳에서 미리보기해주는 속성 -->


</LinearLayout>

 

 

6) 개발과정에서 가장 많이 사용되는 중에 하나인 RecyclerView 에서 버터나이프 실습 [ ViewHolder 에서의 사용 ]

- 대량의 데이터를 목록형태로 보여주는 UI는 대부분의 앱에서 가장 많은 비중을 차지하는 UI 입니다. 이럴때 사용하는 것이 어뎁터뷰 입니다. 그 중에서도 RecyclerView가 ViewHolder를 이용하여 성능을 많이 개선했지요. 그 ViewHolder에서도 역시 버터나이프의 연결이 사용되어 질 수 있습니다.

 

6.1) 먼저, 리사이클러뷰가 보여줄 목록 아이템 1개의 모양을 설계하는 레이아웃 xml 파일을 만들어 보겠습니다.

간단한 실습을 위해 ImageView1개와 TextView 1개를 가진 CardView로 아이템 1개의 모양을 구성하겠습니다.

# recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="120dp"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:cardCornerRadius="4dp"
    android:elevation="4dp"
    android:layout_margin="8dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/iv"
            android:layout_width="match_parent"
            android:layout_height="120dp"
            android:src="@drawable/newyork"
            android:scaleType="centerCrop"/>
        <TextView
            android:id="@+id/tv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="newyork"
            android:layout_below="@id/iv"
            android:padding="8dp"
            android:textColor="#FF333333"
            android:layout_centerHorizontal="true"/>

    </RelativeLayout>


</com.google.android.material.card.MaterialCardView>

 

6.2) 두번째 작업으로, 위 아이템뷰 1개 모양에 보여질 이미지파일의 res 리소스ID를 저장할 int 변수와 텍스트뷰에 보여질 String 변수 1개를 가진 데이터 클래스를 한개 만들겠습니다. 리사이클러뷰가 보여즐 대량의 데이터(배열 or 리스트)의 요소 1개 클래스입니다.

# ItemVO.java
//리사이클러뷰가 보여줄 Item 뷰 1개안에서 보여줄 데이터를 가진 Value Object(VO) 클래스
public class ItemVO {
    int imgResId; //이미지의 리소스 ID
    String title; //이미지 아래에 있는 제목글씨

    public ItemVO(int imgResId, String title) {
        this.imgResId = imgResId;
        this.title = title;
    }
}

 

6.3) 세번째로, 대량의 데이터 수 만큼 리사이클러뷰에서 보여줄 아이템 항목뷰를 만들어내는 Adapter 클래스를 만들어 보겠습니다. 이번 실습에서 소개하고자 하는 가장 중요한 코드이지요.

# MyAdapter.java
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

import butterknife.BindView;
import butterknife.ButterKnife;

public class MyAdapter extends RecyclerView.Adapter {

    Context context;
    ArrayList<ItemVO> items;

    public MyAdapter(Context context, ArrayList<ItemVO> items) {
        this.context = context;
        this.items = items;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View itemView= LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false);
        return new VH(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        VH vh= (VH) holder;

        ItemVO item= items.get(position);
        vh.iv.setImageResource(item.imgResId); //이미지로더 라이브러리인 Glide 사용을 권장함!
        vh.tv.setText(item.title);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    //아이템 뷰 1개의 자식뷰들 참조값을 저장하고 있는 ViewHolde 클래스 설계 - inner class
    class VH extends RecyclerView.ViewHolder{

        @BindView(R.id.iv) ImageView iv;
        @BindView(R.id.tv) TextView tv;

        public VH(@NonNull View itemView) {
            super(itemView);
            //버터나이트프로 연결! - 타겟은 ViewHolder 객체, 연결할 뷰들을 가진 itemView와 연결하도록 지정
            // - 모든 자식뷰들의 itemView.findViewById() 작업을 한번에 끝!
            ButterKnife.bind(this, itemView);
        }
    }
}

- VH 클래스의 생성자에서 버터나이프를 연결하는 ButterKnife.bind(this, itemView) 로 findViewById()작업을 한번에 끝냈습니다. 아이템뷰의 자식뷰들의 많아질 수록 그 편의성이 극대화 되겠죠.

 

6.4) 이제 액티비티에서 리사이클러뷰를 보여주는 코드를 추가하겠습니다. 액티비티의 레이아웃 파일에 가로방향 스크롤의 리사이클러뷰를 추가하고 액티비티 자바문서에서 ArrayList, Adapter를 만들어 리사이클러뷰에 설정해주도록 하겠습니다. 

이 실습을 마지막으로 버터나이프에 대해 알아보는 실습을 마무리 하겠습니다.

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <!-- 1) 기본 TextView  -->
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="Hello"/>

    <!-- 2) 버튼 클릭이벤트를 ButterKinfe로 임의의 메소드와 연결하여 콜백처리하기 -->
    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"/>

    <!-- 3) EditText의 글씨을 얻어와서 TextView에 보이는 예제실습 [뷰참조와 클릭이벤트 종합 simple 예제]-->
    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="enter text"
        android:inputType="text"
        android:layout_marginTop="24dp"/>
    <Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="입력완료"/>
    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:textColor="#FF333333"
        android:text="RESULT"/>

    <!-- 4) res폴더의 리소스 데이터들과도 바인딩 가능함 [ Ex. res>>vlaues>>strings.xml 문서의 문자열 리소스 연결 실습 ]-->
    <TextView
        android:id="@+id/tv3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:padding="8dp"
        android:textColor="#FF111111"/>


    <!-- 5) Fragment에서 버터나이프 사용하기 예제 실습   -->
    <fragment
        android:id="@+id/frag"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:name="com.mrhi2021.ex97butterknifelibrary.MyFragment"
        tools:layout="@layout/fragment_my"
        android:layout_marginTop="24dp"/>
    <!-- tools:layout="@layout/fragment_my" 화면의 한 부분을 담당하는 MyFragment의 모양을 이곳에서 미리보기해주는 속성 -->


    <!-- 6) 앱 개발과정에서 가장 많이 사용되는 뷰 중에 하나인 RecyclerView 에서 버터나이프 실습 [ ViewHolder 에서의 사용 ] -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:orientation="horizontal"/>

</LinearLayout>

 

위 모든 실습을 모두 작성한 최종 자바 파일

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;

import android.content.Context;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

import butterknife.BindString;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnCheckedChanged;
import butterknife.OnClick;
import butterknife.OnEditorAction;
import butterknife.OnItemClick;
import butterknife.OnLongClick;

public class MainActivity extends AppCompatActivity {

    // findViewById() 메소드를 이용한 View 참조 방법이 가진 문제를 해결하기 위한 기법들 [ 코드의 번거로움과 메소드의 동작속도 문제 ]
    // 1. ButterKnife 외부 라이브러리 사용하기
    // 2. ViewBinding 안드로이드 아키텍처 구성요소
    // 3. DataBinding (view binding + data binding) : 뷰결합 + 데이터 결합 기능까지 가진 아키텍처 구성요소

    // 이 실습예제에서는 위 3가지 중 많은 안드로이드 개발자로부터 애용되어 오던 JakeWharton 의 ButterKnife 라이브러리 소개
    // ButterKnife 라이브러리 추가 [ dependency library - 구글 검색 or [File]메뉴의 [Project Structure]메뉴를 통한 라이브러리 적용 ]
    //   *주의!* 어노테이션을 사용하여 뷰들을 바인딩 하기에 [build.gradle]에 반드시 버터나이프 어노테이션프로페서도 추가해야함 - 버터나이프 GitHub 참고 [ https://github.com/JakeWharton/butterknife ]

    //1) 먼저 간단하게 TextView 1개를 만들고 이를 버터나이프로 참조하여 글씨 변경해 보기
    // - 사용초기화 -
    // 액티비티의 onCreate()에서 버터나이프가 연결할 뷰들을 가진 타겟과 연결! ButterKnife.bind()


    //Butter Knife Librar의 @(어노테이션)을 이용하여 이 액티비티의 뷰들을 연결 - findViewById를 하지 않음
    @BindView(R.id.tv) TextView tv; //id가 tv인 뷰를 참조하는 참조변수 선언

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //ButterKnife가 연결해줄 뷰들을 가진 타겟(Activity)과 연결
        ButterKnife.bind(this);

        //@BindView 어노테이션을 통해 참조된 TextView의 끌씨 변경하기
        tv.setText("Nice to meet you. Butter Knife!");
    }

    //2) 버튼 클릭이벤트 콜백리스너를 Butter Knife 로 연결하기
    // - onClick속성에 지정한 메소드가 아님. 그냥 일반 메소드인데 버터나이프의 어노테이션을 통해 클릭이벤트 처리용 콜백메소드로 연결
    @OnClick(R.id.btn)  // id가 btn인 뷰의 OnClick 이벤트 연결
    void clickBtn(){
        Toast.makeText(this, "clicked button", Toast.LENGTH_SHORT).show();
    }

    //2-1) 당연히 onClick외에 다른 이벤트 콜백처리도 모두 가능함
    @OnLongClick(R.id.btn)
    boolean longClickBtn(){
        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
        return true; // 리턴값을 true 로 하지 않으면 long click 이벤트 후에 onClick 이벤트도 처리됨
    }

    //원래 LongClick이벤트처리 콜백메소드는 리턴값이 있는 메소여야 하지만 버터나이프로 연결할 때는 return을 안해도 발동함.[단, 리턴값을 기본 true]
//    @OnLongClick(R.id.btn)
//    void longClickBtn(){
//        Toast.makeText(this, "long clicked button", Toast.LENGTH_SHORT).show();
//    }

    //@OnCheckedChanged ,@OnItemClick 등 대표적인 콜백메소드 모두 사용가능함


    //3) 뷰 참조와 클릭이벤트 처리 종합 simple 예제 실습
    @BindView(R.id.et) EditText et;           //id가 et인 뷰를 참조하는 참조변수 선언
    @BindView(R.id.tv2) TextView tv2;         //id가 tv2인 뷰를 참조하는 참조변수 선언

    @OnClick(R.id.btn2) // id가 btn2인 뷰의 OnClick 이벤트 연결
    public void clickBtn2(){
        tv2.setText(et.getText().toString());
        et.setText(""); //EditText의 글씨 지우기
    }

    //3-1) *추가* EditText의 글씨 입력을 위한 SoftKeyboard의 작성 완료버튼(Action DONE)을 눌렀을때 [입력완료]버튼과 같은 동작이 되도록 하기 위해 이벤트 연결
    @OnEditorAction(R.id.et)
    public boolean softKeyboardAction(TextView v, int actionId, KeyEvent event){
        tv2.setText(et.getText().toString());
        et.setText(""); //EditText의 글씨 지우기

        //소프트키보드 안보이도록 하기
        InputMethodManager imm= (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        //1. hideSoftInputFromWindow flag에 0를 주면 어떠한 상황에서든 닫힘
        //2. hideSoftInputFromWindow flag에 HIDE_NOT_ALWAYS를 주면 SHOW_FORCED를 제외하고는 닫힘
        //3. hideSoftInputFromWindow flag에 HIDE_IMPLICIT_ONLY 를 주면, SHOW_IMPLICIT 로 보여진 키보드만 닫힘, 하지만, 보여준 view의 포커스가 변하면 닫히지 않음.
        return false;
    }



    //4) res폴더의 리소스 데이터들과도 바인딩하여 값을 가져와 사용할 수 있음. [ Ex. res>>vlaues>>strings.xml 문서의 문자열 리소스 연결 실습 ]
    //액티비티가 화면에 보여지기 시작할 때 TextView(tv3)의 글씨를 String리소스의 값으로 설정
    @BindView(R.id.tv3) TextView tv3;
    @BindString(R.string.aaa) String resStr;  // res>>vlaues>>strings.xml 문서의 문자열 리소스 객체(name속성이 "aaa"인 <string>)를 연결하여 참조하는 String참조변수

    @Override
    protected void onStart() {
        super.onStart();
        //@BindString(R.string.aaa) String resStr;  // 지역변수로는 @Bind 를 사용할 수 없음
        tv3.setText(resStr);
    }

// ### Resource Bind #####################################################################################
// 아래의 리소스 bind를 지원함.//
// @BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString, @BindArray, @BindAnim .....

// @BindString(R.string.title) String title;
// @BindBool(R.bool.isTed) boolean isTed;
// @BindInt(R.integer.myinteger) int myinteger;
// @BindDrawable(R.drawable.graphic) Drawable graphic;
// @BindColor(R.color.red) int red;
// @BindDimen(R.dimen.spacer) Float spacer;
// @BindDimen(R.dimen.intvalue) int intvalue;
// ................
// ########################################################################################################


    //5) 프레그먼트에서 버터나이프 실습 [ MyFragment.java ]


    //6) 앱 개발과정에서 가장 많이 사용되는 뷰 중에 하나인 RecyclerView 에서 버터나이프 실습 [ ViewHolder 에서의 사용 ]
    // - 원래는 onCreate()에서 작업하지만 위 실습과 분리해서 보기위해 onResume()에서 리사이클러뷰 작업코딩
    @BindView(R.id.recycler) RecyclerView recycler;

    ArrayList<ItemVO> items= new ArrayList<>(); //리사이클러 뷰가 보여줄 대량의 데이터를 가지고 있는 리시트객체
    MyAdapter adapter;   //리사이클러뷰가 보여줄 뷰을 만들어내는 객체참조변수

    @Override
    protected void onResume() {
        super.onResume();

        //임이의 대량의 데이터 추가
        items.add(new ItemVO(R.drawable.newyork, "newyork"));
        items.add(new ItemVO(R.drawable.paris, "paris"));
        items.add(new ItemVO(R.drawable.sydney, "sydney"));
        items.add(new ItemVO(R.drawable.newyork, "뉴욕"));
        items.add(new ItemVO(R.drawable.paris, "파리"));
        items.add(new ItemVO(R.drawable.sydney, "시드니"));

        //아답터생성 및 리사이클러뷰에 설정
        adapter= new MyAdapter(this, items);
        recycler.setAdapter(adapter);
    }

    // ** 액티비티의 기본 소프트키보드관련 설정인 android:windowSoftInputMode="adjustUnspecified" 인 상태[default 상태] 로 되어 있으면 소프트키패드에 의해 리사이클러뷰가 완전히 가려지면 키보드가 사라졌을때 화면에 보이지 않을 수 있음 **
    //  android:windowSoftInputMode="adjustPan" 와 같이 "adjustUnspecified"이 아닌것으로 지정하면 키보드에 가려져도 UI가 지워지지 않음.
}

 

처음 Butter Knife 를 사용하시는 초보개발자 분들은 새로운 라이브러리이기도 하고 @어노테이션에 대해 이해하는 데 어려움을 느끼시는 분들이 있는 편이라 거부감이 있을지도 모르겠습니다. 하지만 분명 많을 개발자들로 부터 사랑받고 애용되었다는 것은 그 만큼 기존의 뷰 참조 방식인 findViewById()에 비해 장점을 많이 느꼈다는 것을 반증한다고 봅니다.

 

우선 1)~ 6)까지의 간단한 실습을 따라 수행해 보면 좀더 복잡한 앱에도 적용이 가능해지실 것이고 그런 앱을 만드시다 보면 분명 버터나이프를 좀더 좋아하시게 될 거라 생각합니다.

 

다음 포스트에서는 Butter Kinfe 외부 라이브러리와 같은 이유로 등장한 Android 아키텍처 컴포넌트인 뷰 바인딩(ViewBinding)을 소개하겠습니다. 버터나이프 라이브러리를 대체하여 요즘의 안드로이드 앱 개발에 상당히 많이 사용되는 기법이니 이어서 보시길 바랍니다.

반응형

+ Recent posts