반응형

Ex14AlertDialog

안드로이드 네이티브 앱 개발 수업 예제#14

주요코드

AlertDialog 객체 생성 및 보이기와 여러종류의 AlertDialog 알아보기

  • 버튼을 클릭하여 기본적인 String메세지를 보여주는 Alert 객체 생성 및 보이기 [AlertDialog를 만들어주는 건축가(Builder)객체 생성]
  • 여러모양의 AlertDialog 만들기
    1. 단순 string 문자열 보여주기
    2. AlertDialog의 메세지 영역에 단순 문자열이 아닌 항목(list)형태로 메세지영역 설정해 보기
    3. 라디오버튼이 있는 항목형태의 설정해 보기
    4. 체크박스버튼이 있는 리스트형태의 설정
    5. 커스텀뷰로 메세지영역 설정하기

실행모습

여러모양의 AlertDialog

기본적인 AlertDialog

 

 

리스트 형태의 AlertDialog

 

 

라이디버튼 Single Choice 다이얼로그, 체크박스 Multiple Choice 다이얼로그

 

 

커스텀 뷰 다이얼로그

 

 

소스코드

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

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Show AlertDialog"
        android:textAllCaps="false"
        android:onClick="clickBtn"/>

</LinearLayout>

 

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    //2.2~ 실습) 리스트형태의 AlertDialog 실습때 사용할 String배열 멤버변수
    String[] items= new String[]{"Apple", "Banana", "Orange"};

    //2.4 실습) 체크박스를 가진 리스트형태의 AlertDialog 실습때 사용할 boolean 배열 멤버변수
    boolean[] checked= new boolean[]{true, false, true};

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

    public void clickBtn(View v){

        //AlertDialog를 만들어주는 건축가(Builder)객체 생성
        AlertDialog.Builder builder= new AlertDialog.Builder(this);

        //1. 건축가객체(Builder)에게 만들고자하는 AlertDialog의 제목과 아이콘을 설정
        builder.setTitle("다이얼로그");
        builder.setIcon(android.R.drawable.ic_dialog_alert);

        //2. 만들고자하는 AlertDialog의 메세지를 설정
//        builder.setMessage("Do you wanna Quit??");

        //2.2) AlertDialog의 메세지 영역에 단순 문자열이 아닌 항목(list)형태로 메세지영역 설정해 보기
//        builder.setItems(items, new DialogInterface.OnClickListener() {
//            @Override
//            public void onClick(DialogInterface dialogInterface, int which) {
//                Toast t= Toast.makeText(MainActivity.this, items[which], Toast.LENGTH_SHORT);
//                t.show();
//            }
//        });

        //2.3) 라디오버튼이 있는 항목형태의 설정해 보기
//        builder.setSingleChoiceItems(items, 1, new DialogInterface.OnClickListener() {
//            @Override
//            public void onClick(DialogInterface dialogInterface, int which) {
//                Toast t= Toast.makeText(MainActivity.this, items[which], Toast.LENGTH_SHORT);
//                t.show();
//
//            }
//        });

        //2.4) 체크박스버튼이 있는 리스트형태의 설정
//        builder.setMultiChoiceItems(items, checked, new DialogInterface.OnMultiChoiceClickListener() {
//            @Override
//            public void onClick(DialogInterface dialogInterface, int which, boolean b) {
//                checked[which]= b;
//
//            }
//        });


        //2.5) 커스텀뷰로 메세지영역 설정하기
        LayoutInflater inflater= getLayoutInflater();
        View view = inflater.inflate(R.layout.dialog, null);

        builder.setView(view);


        //3. 다이얼로그의 하단에 붙일 버튼 설정 [ 최소0개 ~ 최대3개까지 추가가능 : Positive, Negative, Neutral ]
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                Toast t= Toast.makeText(MainActivity.this, "OK", Toast.LENGTH_SHORT);
                t.show();
            }
        });

        builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                Toast t= Toast.makeText(MainActivity.this, "CANCEL", Toast.LENGTH_SHORT);
                t.show();
            }
        });


        //4. 건축가객체(Builder)에게 위에서 설정한 내역으로 AlertDialog 객체를 생성해 달라고 요청
        AlertDialog dialog= builder.create();

        //5. 다이얼로그 보이기!!!
        dialog.setCanceledOnTouchOutside(false);// 다이얼로그의 영역 밖을 터치하였을 때 다이얼로그가 꺼지는 것을 방지
        dialog.show(); //다이얼로그 보이기

        // ** 참고로 아래 설정은 디바이스의 [뒤로가기]버튼을 클릭해도 다이얼로그 꺼지지 않음.
        //dialog.setCancelable(false); //[ 다이얼로그의 버튼들을 누르거나,  dialog.dismiss() 를 호출해야 꺼짐 ]


        // ## 여기서부터는  2.5)실습의 추가내용 #############################
        // 커스텀뷰로 부터 뷰객체들 참조해오기
        dialogEt= view.findViewById(R.id.dialog_et);
        dialogTv= view.findViewById(R.id.dialog_tv);
    }

    //멤버변수
    EditText dialogEt;
    TextView dialogTv;

    //커스텀뷰의 버튼(dialog.xml문서안에 있는 Button)에 설정된 onClick속성의 콜백메소드
    public void clickDialogBtn(View v){
        //MainActivity가 보여주고 있는 activit_main.xml에 EditText, TextView가 있는 것이 아니라서 찾을 수(findView) 없음.
        //EditText et= findViewById(R.id.dialog_et);
        //TextView tv= findViewById(R.id.dialog_tv);
        //tv.setText( et.getText().toString() );

        //이를 찾으려면 EditText, TextView를 가지고 있는 View에게 찾아달라고 해야함.
        //저 위에 builder에게 setView할때 주어졌던 view( inflate를 통해 생성된 )가 dialog.xml의 root Elemnent(LinearLayout)이므로..
        //이 view를 통해 미리 두개의 뷰를 얻어내었어야함.
        dialogTv.setText( dialogEt.getText().toString() );

    }
}

 

 

■ 커스텀 뷰 레이아웃 xml 파일

# res / layout / dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="200dp"
    android:layout_height="match_parent"
    android:padding="16dp">

    <EditText
        android:id="@+id/dialog_et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="input text"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="btn"
        android:onClick="clickDialogBtn"/>
    <TextView
        android:id="@+id/dialog_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="8dp"
        android:text="SHOW DATA"/>

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

1. ButterKnife 외부 라이브러리 사용하기 (버터 나이프 라이브러리)
2. ViewBinding 안드로이드 아키텍처 구성요소
3. DataBinding (view binding + data binding) : 뷰결합 + 데이터 결합 기능까지 가진 아키텍처 구성요소

 

이전 글에서 소개했던 ButterKnife 라이브러리는 구글 안드로이의 고유 기능 SDK 가 아닙니다. 그렇기에 언제나 문제없이 안전하게 사용하기에는 제약이 있다고 볼 수도 있습니다. 물론. 지금까지 큰 문제 없이 잘 사용되어 왔었기에 앞으로도 충분히 그러하리라 예상되기는 합니다. 다만, @어노테이션을 통한 작업은 생각보다 조금 귀찮은 추가 작업이고 뷰들이 많아지면 그만큼 @Bind 어노테이션이 늘어나고 액티비티의 필드(멤버변수) 위치에 여러줄의 참조변수를 만드는 작업이 필요합니다. 은근히 귀찮고 지저분합니다.

 

버터나이프에 대해 좀더 알아보고 싶으신 분은 이전 글을 보고 오시기 바랍니다.

2021.09.13 - [소소한 소스코드] - [안드로이드 Android] Butter Knife (버터나이프) 라이브러리
 

[안드로이드 Android] Butter Knife (버터나이프) 라이브러리

안드로이드 앱을 개발하면서 가장 많이 사용하게 되시는 메소드 중 하나가 findViewById() 입니다. 안드로이드 앱개발에서 화면 UI는 xml언어를 사용하고 컨트롤을 위한 코드는 자바 or 코틀린 언어를

kitesoft.tistory.com


이번에 소개할 뷰 바인딩(view binding)은 안드로이드 아키텍처 구성요소로서 앱 모듈별로 설정하여 사용할 수 있는 기능입니다.

뷰 바인딩 기능을 사용하면 뷰를 제어하는 코드를 쉽게 작성할 수 있습니다. 

 

뷰 바인딩과 실습에 대한 소개는 코드의 주석으로 설명을 좀 더 간략하게 소개하니 아래 글이 복잡하다면 그냥 바로 코드만 보셔도 됩니다.

 

뷰 바인딩의 가장 큰 특징은,  레이아웃 xml 파일와 연결되는 바인딩 클래스가 자동으로 설계되어 만들어지고 그 클래스의 멤버로서 레이아웃 xml에 ID가 있는 모든 뷰를 참조하는 참조변수가 포함되어 있다는 겁니다. 그렇기에 별도의 findViewById()를 하지 않아도 이 바인딩 클래스의 객체(인스턴스)안에 모든 뷰들의 참조변수가 만들어져 있기에 그냥 사용하기만 하면 됩니다. 백마디 말보다 한번 코드를 보면 더 이해하기 좋을 겁니다. 

 

뷰바인딩 기능에 의해 레이아웃 xml 파일와 연결되는 바인딩 클래스는 자동으로 만들어지기에 그 이름이 특정한 규칙에 맞게 명명되어 만들어집니다. 딱 봐도 어떤 xml과 연결되었는지 바로 알 수 있는 이름입니다. 몇가지 예로 확인해보겠습니다.

 

ex.1) 액티비티 레이아웃 파일명과 자동으로 만들어지는 바인딩 클래스명 
activity_main.xml --> ActivityMainBinding 
activity_second.xml --> ActivitySecondBinding
activity_xxx.xml --> ActivityXxxBinding 

ex.2) 프레그먼트 레이아웃 파일명과 자동으로 만들어지는 바인딩 클래스명 
fragment_my.xml --> FragmentMyBinding 
fragment_login.xml --> FragmentLoginBinding 
fragment_xxx.xml --> FragmentXxxBinding 

ex.3) 리사이클러뷰(아답터뷰)의 아이템 1개 레이아웃 파일명과 자동으로 만들어지는 바인딩 클래스명 
recycler_item.xml --> RecyclerItemBinding 
recycler_xxx.xml --> RecyclerXxxBinding

 

 

뷰 바인딩 역시 ButterKinfe 라이브러리처럼 뷰 참조를 간단히 하여 제어하는 기능이기에 예제 실습을 버터나이프와 거의 똑같은 순서로 진행하고자 합니다. 그래서 간단하게 액티비티에서 보여주는 TextView의 글씨를 변경하는 실습부터, Fragment에서 뷰 바인딩 하기와 마지막으로 RecyclerView의 ViewHolder에서 뷰바인딩을 적용하는 모습을 순서대로 소개하고자 합니다.

 

이전 버터나이트에서는 모든 실습을 하나의 포스트에 모두 작성하였는데 너무 길어서 보기도 힘들고 글쓰기도 어려워서 실습단계별로 글을 나누어 1)~5) 실습을 5개의 포스트로 나누어 소개하고자 합니다.

 

1) 먼저 간단하게 TextView 1개를 만들고 이를 뷰 바인딩으로 참조하여 글씨 변경 해보기

 

■ 뷰 바인딩 사용 설정

ViewBinding 외부 라이브러리가 아니고 안드로이의 아키텍처 구성요소이기에 사용설정을 (모듈단위)build.gradle 에서 해주면 됩니다.

※ Android Studio 4.0 버전 이전과 이후가 다르니 주의 하시면서 주석 // ##  부분을 주의깊게 보시고 적용하시기 바랍니다. 현재 안드로이드 개발자 사이트의 문서에서는 4.0버전 이전에서의 코드가 소개되고 있으니 주의하세요.

# build.gradle(Module:app)
plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.mrhi2021.ex98viewbinding"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    // ## ViewBinding 사용 설정 ######################
    //Android Studio 버전 4.0 이상
    buildFeatures {
        viewBinding = true
    }

    //Android Studio 버전 4.0 이전일 경우 - [3.6이전에서는 뷰바인딩 사용불가]
//    viewBinding {
//        enabled = true
//    }
    // ##############################################
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

 

 

- 레이아웃파일과 액티비티자바 파일을 보겠습니다. 상세설명은 주석을 반드시 참고해 주시기 바랍니다.

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

 

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

import com.mrhi2021.ex98viewbinding.databinding.ActivityMainBinding;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

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

    // 이번 실습에서는 뷰 참조만 관점으로 볼때 가장 성능이 좋은 ViewBinding 안드로이드 아키텍처 구성요소 예제 소개
    // ButterKnife의 어노테이션을 이용한 BindView 역시 코드가 간결하다고 볼수없음. 이를 개선하였다고 보면 됨.

    // ViewBinding은 외부 라이브러리가 아니고 안드로이의 아키텍처 구성요소이기에 사용설정을 (모듈단위) build.gradle 에서 해주면 됨.

    // 뷰바인딩 기능의 주요특징은 xml 레이아웃파일(activity_main.xml)의 뷰들을 이미 연결(Bind)하고 있는 클래스가 자동으로 만들어져서
    // 개발자가 직접 뷰 참조변수를 만들어서 findViewBy()로 찾아서 연결하는 일련의 모든 작업을 할 필요가 없게 만들어 놓은 기능임.

    // 이때, 자동으로 xml 레이아웃 파일의 뷰들의 id와 같은 이름을 가진 멤버변수를 가진 클래스는 그 이름이 규칙적으로 명명되어 만들어짐.
    // 레이아웃파일의 이름에 맞게 클래스이름이 만들어짐.

    // ex.1) 액티비티 레이아웃 파일명과 자동으로 만들어지는 바인딩 클래스명
    // activity_main.xml      -->   ActivityMainBinding
    // activity_second.xml    -->   ActivitySecondBinding
    // activity_xxx.xml       -->   ActivityXxxBinding

    // ex.2) 프레그먼트 레이아웃 파일명과 자동으로 만들어지는 바인딩 클래스명
    // fragment_my.xml        -->   FragmentMyBinding
    // fragment_login.xml     -->   FragmentLoginBinding
    // fragment_xxx.xml       -->   FragmentXxxBinding

    // ex.3) 리사이클러뷰(아답터뷰)의 아이템 1개 레이아웃 파일명과 자동으로 만들어지는 바인딩 클래스명
    // recycler_item.xml      -->   RecyclerItemBinding
    // recycler_xxx.xml       -->   RecyclerXxxBinding


    // 실습방법은 이전에 실습했던 ButterKnife와 완전히 동일하게 진행해 봄으로서 차이점을 극명하게 확인.

    //1) 먼저 간단하게 TextView 1개를 만들고 이를 뷰 바인딩으로 참조하여 글씨 변경해 보기
    // - ViewBinding 초기화 -
    // 액티비티의 onCreate()에서 액티비티가 직접 setContentView()를 하지 말고 뷰바인딩 클래스가 레이아웃파일을 inflate해서 만들고 참조하여 멤버변수로 가지는 객체를 생성

    // activity_main.xml 파일과 연결되어 있는 바인딩 클래스 참조변수
    ActivityMainBinding mainBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //액티비티가 직접 setContentView()로 뷰들을 생성하지 않도록... 주석처리
        //setContentView(R.layout.activity_main);

        // xml 레이아웃파일명과 관련되어 뷰바이딩 기능에 의해 자동으로 설계되어진 클래스을 이용하여 뷰들의 id와 같은 이름을 가진 참조변수를 멤버로 가지는 객체 생성
        //activity_main.xml과 연결되는 바인딩클래스인 ActivityMainBinding 클래스의 inflate() static 메소드를 통해 뷰를 바인딩클래스가 직접 생성(inflate)
        // 바인딩 클래스의 멤버로 여러 뷰들의 참조변수들이 있으므로 가급적 바인딩클래스의 참조변수는 이 액티비티클래스의 멤버변수 위치를 만들것을 권장함.
        mainBinding= ActivityMainBinding.inflate(getLayoutInflater()); //파라미터로 LayoutInflater객체 전달

        //이 액티비티가 보여줄 내용물 뷰를 바인딩 클래스 객체의 최상위 뷰(rootView : activity_main.xml의 첫번째 레이아웃 뷰 - 이 예제에서는 LinearLayout)로 설정
        setContentView(mainBinding.getRoot());

        //1) TextView 글씨 변경하기
        //이미 activity_main.xml의 뷰들을 참조하여 연결하고 있는 바인딩 클래스인 ActivityMainBinding의 객체인 mainBinding의 멤버변수로
        //각 뷰들의 id 값과 이름이 완전 똑같은 해당뷰의 참조변수들이 찾아져서 연결되어 있는 상태임.
        mainBinding.tv.setText("Nice to meet you. ViewBinding");
    }
   
}

뷰가 TextVeiw 1개여서 아직은 뷰 바인딩이 더 코드가 간결한 건지 잘 안느껴지실 수 있습니다. 다음 실습을 따라하시면서 얼마나 좋은 점점 알아가시게 될겁니다.

 

다음 글에서는 버튼을 클릭하여 글씨를 변경하는 코드를 뷰 바인딩으로 제어해 보겠습니다.

반응형
반응형

Ex12Toast

안드로이드 네이티브 앱 개발 수업 예제#12

주요코드

Toast 객체 생성 및 보이기와 위치설정 알아보기

  • 버튼을 클릭하여 기본적인 String메세지를 보여주는 토스트 객체 생성 및 보이기
  • 토스트에서 보여줄 String메세지를 res폴더>>values>>strings.xml문서안에 만들고 토스트에서 보여주기
  • 토스트가 보여지는 위치를 설정하기 (Gravity설정 및 xOffset, yOffset소개)
  • 토스트에 단순 문자열이 아닌 원하는 모양의 View 보여주기
    1. Java언어만으로 원하는 View모양 만들기[ res폴더>>layout폴더>>toast.xml문서 ]
    2. XML로 View객체 생성하고 적용하기 : layout폴더 안에 있는 toast.xml이라는 문서를 읽어서 View객체로 만들어(부풀려주는 inflate) 주는 LayoutInflater 객체소개

실행모습

[1.기본 Toast] 

  

[2.Toast 위치지정]          [3.Custom View Toast]

실행모습 GIF

 

소스코드

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:padding="16dp"
    tools:context=".MainActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Show Toast"
        android:textAllCaps="false"
        android:onClick="clickBtn"/>

</RelativeLayout>

 

# MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    Toast t;

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

    //onClick속성이 부여된 View가 클릭되면 자동으로
    //실행되는 메소드
    public void clickBtn(View v){

        //## 아래 1. 실습진행 후.... 버튼을 여러번 눌렀을 때 토스트가 밀리는 현상을 관찰한 후....... ###########
        //### 1.2 Toast 객체 생성가 null이 아니면 토스트가 이미 있는 상태여서 새로운 토스트가 대기했다가 보여짐. #########
        // 그래서 기존 토스트가 있으면 캔슬하도록...
//        if(t!=null){
//            t.cancel();
//            t=null;
//        }
        //##################################################################################################################

        //1. 토스트 객체 생성 [ new 키워드 대신에 makeText()메소드를 통해 토스트객체 생성 ]
        t= Toast.makeText( this , "clicked!!", Toast.LENGTH_SHORT);
        //토스트 객체를 만들었으니 화면에 보여주라는 기능메소드 호출!!
        t.show();


        //2. res/values/strings.xml안에 문자열을 작성하고 토스트에서 문자열 읽어와 보여주기
        t= Toast.makeText( this , R.string.text, Toast.LENGTH_SHORT);
        //토스트의 위치 지정 [ xOffset, yOffset : 첫번재 파라미터로 지정한 위치(Gravity.CENTER)에서 몇 픽셀 차이가 나도록 할 지 설정 ]
        t.setGravity(Gravity.CENTER, 0, -100);
        //토스트 보여주기
        t.show();


        //3. 토스트에 문자열이 아닌 원하는 모양의 View 보여주기
        // 빈 문자열을 가진 토스트객체 생성
        t= Toast.makeText(this, "", Toast.LENGTH_SHORT);

        // ### 3.1 Java언어로 View 객체 생성하는 방법  ###############
//        ImageView iv= new ImageView(this);
//        iv.setImageResource(android.R.drawable.ic_lock_silent_mode);
//
//        TextView tv= new TextView(this);
//        tv.setText("음소거");
//
//        LinearLayout layout= new LinearLayout(this);
//        layout.setOrientation(LinearLayout.VERTICAL);
//
//        layout.addView(iv);
//        layout.addView(tv);
//
//        t.setView(layout);
        //############################################################


        //### 3.2 XML로 View객체 생성하고 적용하기  ################################
        //layout폴더 안에 있는 toast.xml이라는 문서를 읽어서
        //View객체로 만들어(부풀려주는 inflate) 주는 객체를 Context로 부터 얻어오기
        LayoutInflater inflater= getLayoutInflater();
        View view= inflater.inflate(R.layout.toast, null);
        t.setView(view);

        t.setGravity(Gravity.CENTER, 0,0);
        t.show();
        //##########################################################################

    }
}

 

# res / layout / toast.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:gravity="center">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@android:drawable/ic_lock_silent_mode"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="음소거 모드"/>

</LinearLayout>

 

반응형
반응형

안드로이드 앱을 개발하면서 가장 많이 사용하게 되시는 메소드 중 하나가 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)을 소개하겠습니다. 버터나이프 라이브러리를 대체하여 요즘의 안드로이드 앱 개발에 상당히 많이 사용되는 기법이니 이어서 보시길 바랍니다.

반응형
반응형

Ex12EditText

안드로이드 네이티브 앱 개발 수업 예제#12

주요코드

EditText의 여러 속성과 기법 알아보기

  • res폴더>>layout폴더안에 있는 activity_main.xml문서를 수정하여 화면제작
  • inputType속성값 (text, number, phone, textPassword 등)에 따라 디바이스의 입력 소프트키패드가 다른 UI모양으로 보여짐

지정이 없으면 엔터를 가진 키패드가 올라옴.(줄바꿈이 됨)- height값이 wrap이면 뷰가 커지고 수치값이면 안에 내용물이 안보이게 됨.

  • 여러줄 입력 : inputType="textMultiLine" 및 ultiLine의 뷰 높이 관련속성 lines , maxLines
  • EditText에 이미지 넣기 : drawableRight="@drawable/ic_favorite_black_40dp" ( 실습예제에 사용된 이미지는 AndroidStudio의 Image Asset메뉴를 통해 제작 )
  • EditText 커서 안보이기 : cursorVisible="false"
  • width를 글자수로 지정하기 : ems="5"
  • background속성으로 배경을 지정하면 언더라인이 안보임. 즉, 언더라인이 EidText의 기본 배경이었던 것임
  • EditText의 포커스 자동 이동 : Java 코드를 통한 커서 이동처리 필요함 (전화번호 입력폼에 유용한 기법)
  • 기본적으로 화면에 EditText가 있으면 처을 실행할 때 자동으로 포커스를 가지게 됨. 이게 싫다면 ViewGroup(예제에서는 root의 LineaerLayout)에게 focusableInTouchMode="true"로 지정하면 EditText가 기본적으로 가지게 되는 Focus를 뺏어올 수 있음
  • 소프트키패드 강제 보이기/숨기기/토글하기

실행모습

inputType속성에 따라 다른 모양의 소프트키패드 [ text | number | phone | textPassword ]

 

 

 

 

EditText의 기타 속성들에 따른 실행모습

 

 

  

실행모습 GIF

 

 

 

소스코드

# 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"
    android:focusableInTouchMode="true"
    tools:context=".MainActivity">
    <!--focusableInTouchMode="true"로 하면 EditText가 기본적으로 가지게 되는 Focus를 뺏어올 수 있음-->

    <!--///////// 아래의 EditText실습이 모두 종료된 후 실습 /////////////////////-->
    <!--소프트키패드 강제 보이기/숨기기/토글하기-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginBottom="16dp">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="show SOFTKEY"
            android:textAllCaps="false"
            android:onClick="clickBtn"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="hide SOFTKEY"
            android:textAllCaps="false"
            android:onClick="clickBtn2"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="toggle SOFTKEY"
            android:textAllCaps="false"
            android:onClick="clickBtn3"/>
    </LinearLayout>
    <!-- ///////////////////////////////////////////////////////////////////////// -->

    <!--inputType속성 주요 설정값-->
    <!-- -지정이 없으면 엔터를 가진 키패드가 올라옴.(줄바꿈이 됨)- height값이 wrap이면 뷰가 커지고 수치값이면 안에 내용물이 안보이게 됨.-->
    <!-- -"date": 숫자 키패드(/, -, . 3개의 특수문자 키) - 줄바꿈 안됨.(포커스가 다음으로 이동됨)-->
    <!-- -"datetime": 숫자 키패드(/, -, . , :(롱클릭) 4개의 특수문자 키) - 줄바꿈 안됨.-->
    <!-- -"number":숫자 키패드 - 줄바꿈 안됨.-->
    <!-- -"numberSigned": 음수부호 입력 가능 - 줄바꿈 안됨.-->
    <!-- -"phone":다이얼 키패드 - 줄바꿈 안됨.-->
    <!-- -"text" : -줄바꿈 안됨-->
    <!-- -"textCapCharacters" : 모두 대문자 -줄바꿈 안됨-->
    <!-- -"textCapWords" : 단어의 첫글자에서 대문자 다음부터 자동 소문자(띄어쓰기 후 다시 대문자)-->
    <!-- -"textCapSentences " : 문자의 첫글자 대문자.-->
    <!-- -"textMultiLine" : 여러줄 입력 -엔터가 줄바꿈으로 됨.-->
    <!-- -"textPassword" : 입력된 글자가 가려짐 : 영어만 입력가능함 -->
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="input data - inputType attribute"
        android:inputType="text"/>

    <!-- multiLine의 뷰 높이 관련 -->
    <!-- 라인수 관련(뷰의 높이사이즈 관련) 속성 -->
    <!-- lines : 처음 화면에 보이는 EditTest의 라인수 [ex. line=3 이면 3줄크기의 EditText 출력 - 단, 스크롤을 통해 여러줄 입력가능] -->
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="input multi line : lines속성"
        android:inputType="textMultiLine"
        android:lines="3"
        android:gravity="top"/>

    <!-- maxLines : 처음 보일때는 한줄입력 EditText로 보여지다가 엔터를 통해 늘어날때 최대 3줄크기까지 늘어남. (스크롤을 통해 여러줄 입력은 여전히 가능함) -->
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="input multi line : maxLines속성"
        android:inputType="textMultiLine"
        android:maxLines="3"/>

    <!-- 특정 줄 수 입력은 현재는 없으며 한줄만 입력가능하게 하는것은 inputType=text로 처리! [line속성은 무시됨] -->

    <!--EditText에 이미지 넣기-->
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:drawableRight="@drawable/ic_favorite_black_40dp"/>

    <!--EditText 커서 안보이기-->
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="no cursor"
        android:cursorVisible="false"/>

    <!-- width를 글자수로 지정하기-->
    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="5"/>

    <!-- 배경을 바꾸면 언더라인이 없어짐-->
    <EditText
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:background="#008800"
        android:textColor="#FFFFFF"
        android:padding="8dp"
        android:gravity="top"
        android:hint="No Under Line"
        android:textColorHint="#EEEEEE"/>

    <!-- 전화번호 입력폼에 유용한 기법 -->
    <!--EditText의 포커스 자동 이동 : Java 코드를 통한 커서 이동처리 필요함. -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/edit01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="3"
            android:inputType="number"
            android:maxLength="3"
            android:hint="010"
            android:gravity="center"/>
        <EditText
            android:id="@+id/edit02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="4"
            android:maxLength="4"
            android:inputType="number"
            android:hint="1234"
            android:gravity="center"/>
        <EditText
            android:id="@+id/edit03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ems="4"
            android:maxLength="4"
            android:inputType="number"
            android:hint="5678"
            android:gravity="center"/>

    </LinearLayout>


</LinearLayout>

 

@drawable/ic_favorite_black_40dp.xml 이미지 아이콘 파일은 AndroidStudio의 Vector Asset 메뉴를 통해 만든 이미지임.

 

 

# MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    EditText edit01, edit02, edit03;

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

        edit01= findViewById(R.id.edit01);
        edit02= findViewById(R.id.edit02);
        edit03= findViewById(R.id.edit03);

        //EditText의 글씨가 변경될때 마다 반응하기 - 텍스트변경리스터 TextChangedListener로서 TextWatcher객체를 전달
        edit01.addTextChangedListener(new TextWatcher() {

            //텍스트가 변경되기 이전에 자동 실행되는 메소드 - 변경 이전의 텍스트를 얻어올 때 활용가능
            @Override
            public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {

            }

            //텍스트가 변경되었을 때 자동 실행되는 메소드 - 변경된 텍스트를 얻어올 때 활용
            @Override
            public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
                //첫번재 파라미터 charSequence : 현재 EditText에 써있는 글씨

                // 전화번호의 첫번째 자리는 010 처럼 3자리 이므로..
                //EditText에 작성된 글씨(String)의 길이(글자수)가 3가 이상인가? - 그럼 edit02객체가 포커스를 가지도록 요청
                if(charSequence.length()>=3){
                    edit02.requestFocus();//포커스 요청
                }
            }

            //텍스트변경 작업이 완료된 후 자동 실행되는 메소드
            @Override
            public void afterTextChanged(Editable editable) {

            }
        });

        // 위와 마찬가기로 전화번호의 2번째 자리도 4자리로 구성되어 있으므로 4글자 이상이면 마지막 edit03으로 포커스 이동시키는 코드
        edit02.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if(charSequence.length()>=4){ //입력된 글씨가 4글자 이상인가?
                    edit03.requestFocus();
                }
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });

    }//onCreat method....



    //소프트 키패드 보이기
    public void clickBtn(View view) {

        //소프트키패드를 관리하는 InputMethodManager객체를 시스템객체(Context)로 부터 얻어오기
        // 참고 : Context - 안드로이드 운영체제의 주요기능관리객체(시스템서비스객체)들을 가진 객체[일종의 운영체체 대리인 - 운영체제의 능력을 사용하고 싶을 때 호출]
        //                - InputMethodManager가 바로 주요기능 관리객체(시스템서비스객체)임.
        //                - Activity는 Context를 상속받아 만들어진 클래스 여서 Context의 능력을 그대로 사용할 수 있음.
        InputMethodManager imm= (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);

        //InputMethodManager객체에게 소프트 키패드를 보이도록 요청 - 파라미터로 전달된 값은 추후 소개
        imm.showSoftInput(getCurrentFocus(), 0);
    }

    //소프트 키패드 숨기기
    public void clickBtn2(View view){

        InputMethodManager imm= (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);

        //InputMethodManager객체에게 소프트 키패드를 숨기도록 요청 - 파라미터로 전달된 값은 추후 소개
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }

    //소프트 키패드 토글
    public void clickBtn3(View view) {

        InputMethodManager imm= (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);

        //InputMethodManager객체에게 소프트 키패드가 보이면 숨기고 안보이면 보이도록 요청 - 파라미터로 전달된 값은 추후 소개
        imm.toggleSoftInput(0, 0);
    }

}

 

※ 이 포스트의 코드는 수정예정임.

반응형
반응형

Ex11ScrollView

안드로이드 네이티브 앱 개발 수업 예제#11

주요코드

ScrollView 알아보기

  • res폴더>>layout폴더안에 있는 activity_main.xml문서를 수정하여 화면제작
  • ScrollView는 기본적으로 세로방향을 스크롤 됨
  • 스크롤뷰 안에는 오직 1개의 View만 추가가 가능함. 그래서 여러개를 추가하려면 안에 Layout같은 ViewGroup을 놓고 그 안에 View들을 추가해야함
  • 스크롤뷰안에 있는 View의 height은 값을 어떻게 지정하던지 무조건 wrap_content로 됨
  • 수평방향의 스크롤뷰는 별도의 클래스로 존재함 : HorizontalScrollView
  • 수평, 수직 모두 스크롤되게 하려면 중첩스크롤 구조로 만들면 됨
  • 스크롤뷰의 스크롤 위치를 Java코드를 통해 마지막으로 이동시키기 (버튼을 클릭하여 실행)

실행모습

 

 

 

실행모습 GIF

 

 

소스코드

# 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개의 View만 추가가 가능함-->
    <!--여러개를 추가하려면 안에 Layout같은 ViewGroup을 놓고 그 안에 View들을 추가해야함-->
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="100dp">

        <!-- 스크롤뷰안에 있는 View의 height은 값을 어떻게 지정하던지 무조건 wrap_content로 됨-->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:background="#ff0000"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:background="#00ff00"/>
            <TextView
                android:layout_width="match_parent"
                android:layout_height="250dp"
                android:background="#0000ff"/>

        </LinearLayout>

    </ScrollView>

    <!--수평방향의 스크롤뷰는 별도의 클래스로 존재함-->
    <HorizontalScrollView
        android:layout_marginTop="16dp"
        android:layout_width="300dp"
        android:layout_height="150dp">

        <!-- HorizontalScrollView의 경우 안에 있는 뷰의 width값이 무조건 wrap_content로 됨-->
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal">

            <TextView
                android:layout_width="200dp"
                android:layout_height="match_parent"
                android:background="#ff0000"/>
            <TextView
                android:layout_width="200dp"
                android:layout_height="match_parent"
                android:background="#00ff00"/>
            <TextView
                android:layout_width="200dp"
                android:layout_height="match_parent"
                android:background="#0000ff"/>


        </LinearLayout>


    </HorizontalScrollView>

    <!-- 수평, 수직 모두 스크롤되게 하려면 중첩스크롤 구조로 만들면 됨-->
    <ScrollView
        android:layout_marginTop="16dp"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:id="@+id/sv">

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

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/penguins"/>

        </HorizontalScrollView>

    </ScrollView>

    <!-- 스크롤뷰의 스크롤 위치를 Java코드를 통해 마지막으로 이동시키기-->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="스크롤을 마지막으로"
        android:onClick="clickBtn"/>


</LinearLayout>

 

 

# MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ScrollView;

public class MainActivity extends AppCompatActivity {

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

    //onclick속성이 지정된 버튼을 클릭하였을 때 자동으로 실행되도록 지정된 콜백메소드
    public void clickBtn(View view) {
        //스크롤 뷰 참조하기
        ScrollView sv= findViewById(R.id.sv);

        //스크롤뷰의 스크롤위치를 가장 아래쪽으로 이동
        sv.fullScroll(ScrollView.FOCUS_DOWN);
    }

}
반응형
반응형

Ex09GridLayout

안드로이드 네이티브 앱 개발 수업 예제#9

주요코드

GridLayout 알아보기

TableLayout의 미흡한 점을 개선하기 위해 만든 레이아웃, 뷰들이 겹쳐지며, RowSpan이 가능함, TableRow같은 중첩구조가 없음

  • res폴더>>layout폴더안에 있는 activity_main.xml문서를 수정하여 화면제작
  • GridLayout의 layout_column, layout_row, layout_columnSpan, layout_rowSpan, layout_gravity속성들 적용
  • TableLayout처럼 stretchColumn이 없기에 균등분할이 더 어려움 (API21버전이상에는layout_columnWeight속성으로 LinearLayout의 layout_weight같은 능력의 셀(뷰) 사이즈 지정이 가능함)

실행모습

 

 

 

소스코드

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".MainActivity">

    <!-- TableLayout의 미흡한 점을 개선하기 위해 만든 레이아웃, 뷰들이 겹쳐지며, RowSpan이 가능함, TableRow같은 중첩구조가 없음 -->

    <!-- orientation=""을 통해 칸개수, 또는 줄개수를 지정할 수 있음-->
    <!-- orientation="horizontal"이고 columnCount="3"이면 이 레이아웃의 뷰들은 가로로 최대 3개까지만 추가되고 자동 줄바꿈이 되어 배치됨-->
    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="350dp"
        android:orientation="horizontal"
        android:columnCount="3">

        <!--기본 width는 wrap_content임-->
        <Button android:text="1"/>
        <Button android:text="2"/>
        <Button android:text="3"/>
        <Button android:text="4"/>
        <Button android:text="5"/>
        <!--셀(뷰)의 사이즈를 조정하여도 다른 셀의 크기에 영향을 주지 않음-->
        <Button android:text="6" android:layout_width="200dp"/>

        <!-- 칸위치(column)를 지정할 때 순서가 뒤바뀌어도 적용됨-->
        <Button android:text="7" android:layout_column="1"/>
        <Button android:text="8" android:layout_column="0"/>

        <!-- 줄위치(row)를 지정하여 배치하는 것도 가능함-->
        <Button android:text="9" android:layout_row="3" android:layout_column="2"/>

        <!-- 같은 위치에 배치하면 겹쳐서 배치됨(Button 9 와 같은 위치 지정)-->
        <Button android:text="10" android:layout_row="3" android:layout_column="2"/>

        <!--셀병합(columnSpan)지정 가능하며 기본적으로 공간은 차지하지만 셀(뷰)크기는 바꾸지 않기에 layout_gravity를 통해 사이즈를 조절함-->
        <Button android:text="11" android:layout_columnSpan="2" android:layout_gravity="fill_horizontal"/>

        <!--row병합도 가능함. columnSpan과 방법은 같음-->
        <Button android:layout_rowSpan="2" android:layout_gravity="fill_vertical"/>

        <!--row병합 다음줄에 배치되는 셀(뷰)알아보기-->
        <Button/>
        <Button android:layout_column="2"/>

    </GridLayout>


    <!-- TableLayout처럼 stretchColumn이 없기에 균등분할이 더 어려움-->
    <!-- API21버전이상에는layout_columnWeight속성으로 LinearLayout의 layout_weight같은 능력의 셀(뷰) 사이즈 지정이 가능함 -->
    <!-- Phone이 21버전보다 낮으면 layout_columnWeight속성이 무시됨.-->

    <!--orientation="vertical"이면 layout_rowWeight으로 지정-->
    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:orientation="horizontal"
        android:columnCount="2"
        android:layout_alignParentBottom="true">

        <Button android:layout_columnWeight="1"/>
        <Button android:layout_columnWeight="1"/>
        <Button android:layout_columnWeight="1"/>

        <!--중첩 GridLayout배치 가능 , weight을 주더라도 기본적으로 column여려개를 배치하기 위한 공간을 확보하기위해 사이즈가 좀 달라질 수 있음.-->
        <GridLayout android:layout_columnWeight="1" android:orientation="vertical" android:rowCount="2">

            <Button android:text="1"/>
            <Button android:text="2"/>
            <Button android:text="3"/>

        </GridLayout>

    </GridLayout>


</RelativeLayout>
반응형
반응형

Ex08TableLayout

안드로이드 네이티브 앱 개발 수업 예제#8

주요코드

TableLayout 알아보기

TableLayout은 LineaerLayout을 상속하였기에 뷰들이 겹쳐질 수 없음 : 기본적으로 LinearLayout의 중첩구조를 위해 만들어진 Layout

  • res폴더>>layout폴더안에 있는 activity_main.xml문서를 수정하여 화면제작
  • TableLayout뷰는 LinearLayout의 vertical orientaion을 기반으로 만들어진 뷰
  • TableRow뷰는 LinearLayout의 horizontal orientaion을 기반으로 만들어진 뷰
  • TableLayout안에 TableRow로 한줄씩 배치하는 방식의 Layout
  • 자식뷰들의 사이즈 지정(layout_widht, layout_height)이 명시적으로 없이도 기본 wrap_content로 지정되어 코딩이 간소화되는 이점이 있음
  • TableLayout의 layout_column, layout_span, stretchColumns, shrinkColumns, collapseColumns속성들 적용하기

실행모습

 

 

 

소스코드

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

    <!--TableLayout은 기본적으로 LinearLayout을 상속받아 제작되었기에 뷰들이 겹칠 수 없음-->
    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <!--첫번째 줄 : 사이즈를 안줘도 기본 wrap_content -->
        <TableRow>
            <Button android:text="1"/>
            <Button android:text="2"/>
            <Button android:text="3"/>
        </TableRow>

        <!-- 두번째 줄 : 개수가 달라도 됨 -->
        <TableRow>
            <Button android:text="4"/>
            <Button android:text="5"/>
        </TableRow>

    </TableLayout>


    <!--기본 뷰들의 사이즈는 wrap_content이며 stretchColumns="1,2" 를 통해 widht 사이즈 늘리기 가능, *을 통해 균등배치도 가능-->
    <!--TableLayout안의 TableRow들은 서로의 칸(Column)수에 영향을 받음. 모두 같은 칸수를 가지게 됨-->
    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:stretchColumns="*"
        android:layout_marginTop="16dp">

        <!--사이즈를 안줘도 기본 wrap_content -->
        <TableRow>
            <Button android:text="1"/>
            <Button />
            <Button />
        </TableRow>

        <!-- 원래 배치 순서를 바꿘 column번호 지정 가능-->
        <TableRow>
            <Button />
            <Button android:layout_column="2"/>
            <!--번호의 순서를 바꿔서 지정하는 것은 불가능-->
            <!--<Button android:layout_column="1"/>-->
        </TableRow>

        <!--셀병합( 칸합치기 )-->
        <TableRow>
            <Button />
            <Button android:layout_span="2"/>
        </TableRow>

    </TableLayout>


    <!--TableLayout의 가로 사이즈를 벋어나는 뷰들이 있을때 기본 wrap을 사용하면 화면에서 가려지므로 반대로 줄일 수 있음-->
    <!--shrinkColumns="1,2"를 통해 줄일 뷰를 지정하거나 *을 통해 균등하게 줄이는 것이 가능-->
    <TableLayout
        android:shrinkColumns="*"
        android:layout_marginTop="16dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TableRow>
            <Button android:text="1"/>
            <Button android:text="2"/>
            <Button android:text="3"/>
            <Button android:text="4"/>
            <Button android:text="5"/>
        </TableRow>

        <TableRow>
            <Button />
            <Button />
        </TableRow>

    </TableLayout>


    <!-- 셀숨기기 : collapseColumns="1"을 통해 칸 숨기기 가능-->
    <TableLayout
        android:collapseColumns=""
        android:layout_marginTop="16dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <!--이렇게 TableRow가 아닌 것이 하나의 Row를 차지하는 것도 가능함 -->
        <Button />

        <!--개별셀(뷰)들에게 사이즈 지정 가능 : 그 Row의 다른 칸들의 배치에도 영향을 줌-->
        <TableRow>
            <Button />
            <Button android:layout_width="150dp"/>
        </TableRow>

        <TableRow>
            <Button />
            <Button android:layout_height="80dp"/>
        </TableRow>

    </TableLayout>

</LinearLayout>
반응형
반응형

Ex07FrameLayout

안드로이드 네이티브 앱 개발 수업 예제#7

주요코드

FrameLayout 배치 특징 알아보기

  • res폴더>>layout폴더안에 있는 activity_main.xml문서를 수정하여 화면제작
  • FrameLayout안에 배치된 자식뷰들은 기본적으로 좌상단에 겹쳐서 배치됨
  • 버튼을 클릭할 때 마다 화면이 전환되는 Tab과 비슷한 동작으로 FrameLayout으로 구현해보기

실행모습

 

 

실행모습 GIF

 

 

소스코드

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

    <!-- 아래 FrameLayout이 보여주는 자식뷰들을 변경하기 위한 버튼들 수평 배치 -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">

        <Button
            android:id="@+id/btn01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="korea"
            android:onClick="clickBtn"/>
        <Button
            android:id="@+id/btn02"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="japan"
            android:onClick="clickBtn"/>
        <Button
            android:id="@+id/btn03"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="china"
            android:onClick="clickBtn"/>

    </LinearLayout>

    <!-- FramLayout안에 놓여진 뷰들은 기본적으로 겹쳐짐 -->
    <!-- 특별한 기능 없이 뷰들을 좌상단에 배치함-->
    <!-- 이 예제에서는 자식뷰로 LineaerLayout 3개을 가지면 자식뷰의 사이즈가 match_parent 여서 좌상단 배치처럼 보이지 않는 것임-->
    <!-- RelativeLayout의 전신이며 지금도 TabWidget등에서 그 필요성이 있음. 하지만 대부분은 RelativeLayout을 더 선호하여 사용함 -->
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- 태극기이미지와 글씨를 가진 자식뷰 1 -->
        <LinearLayout
            android:id="@+id/layout_korea"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/korea"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="KOREA"
                android:textSize="30sp"/>

        </LinearLayout>

        <!-- 일본국기이미지와 글씨를 가진 자식뷰 2 -->
        <!-- 기본적으로 FrameLayout안에서는 나중에 작성한 뷰가 위에 배치되어 태극기가 가려져야 하지만 이 LinearLayout에 visibility="gone"속성을 줘서 현재 보이지 않도록 되어 있음 -->
        <LinearLayout
            android:id="@+id/layout_japan"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:visibility="gone">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/japan"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="JAPAN"
                android:textSize="30sp"/>

        </LinearLayout>

        <!-- 중국국기이미지와 글씨를 가진 자식뷰 3 -->
        <!-- 기본적으로 FrameLayout안에서는 나중에 작성한 뷰가 위에 배치되어 태극기가 가려져야 하지만 이 LinearLayout에 visibility="gone"속성을 줘서 현재 보이지 않도록 되어 있음 -->
        <LinearLayout
            android:id="@+id/layout_china"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:visibility="gone">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/china"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="CHINA"
                android:textSize="30sp"/>

        </LinearLayout>

    </FrameLayout>

</LinearLayout>

 

# MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    LinearLayout layoutKorea;
    LinearLayout layoutJapan;
    LinearLayout layoutChina;

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

        //FrmaeLayout안에 있는 자식뷰들 3개를 참조해오기
        layoutKorea= findViewById(R.id.layout_korea);
        layoutJapan= findViewById(R.id.layout_japan);
        layoutChina= findViewById(R.id.layout_china);
    }

    // 버튼들 중 하나를 클릭하였을 때 자동으로 실행되는 콜백메소드 ( 버튼들에 onclick속성으로 지정된 메소드 )
    public void clickBtn(View v){

        // 우선 3개의 자식뷰들을 모두 보이지 않도록 하고..
        layoutKorea.setVisibility(View.GONE);
        layoutJapan.setVisibility(View.GONE);
        layoutChina.setVisibility(View.GONE);

        switch ( v.getId() ){
            case R.id.btn01: // KOREA 버튼을 클릭하였을 때 첫번째 자식뷰(태극기이미지와 KOREA글씨) 보이기
                layoutKorea.setVisibility(View.VISIBLE);
                break;

            case R.id.btn02: // JAPAN 버튼을 클릭하였을 때 두번째 자식뷰(일본국기이미지와 JAPAN글씨) 보이기
                layoutJapan.setVisibility(View.VISIBLE);
                break;

            case R.id.btn03: // CHINA 버튼을 클릭하였을 때 세번째 자식뷰(중국국기이미지와 CHINA글씨) 보이기
                layoutChina.setVisibility(View.VISIBLE);
                break;
        }
    }

}
반응형
반응형

Ex06RelativeLayout

안드로이드 네이티브 앱 개발 수업 예제#6

주요코드

RelativeLayout 알아보기
- LinearLayout은 자식뷰들이 서로 겹쳐있을 수 없으나 RelativeLayout안에서는 자식뷰들이 겹쳐서 배치될 수 있음

  • res폴더>>layout폴더안에 있는 activity_main.xml문서를 수정하여 화면제작
  • RelativeLayout의 자식뷰들 배치에 대해 실습
  1. 부모뷰(RelativeLayout)를 기준으로 한 배치 : layout_alingParentXXX
  2. 특정뷰를 기준으로 한 상대적인(Relative) 배치
  3. 겹쳐서 배치할 때 주의할 점
    - 기본적으로는 나중에 배치된 것이 위에 놓여져서 먼저 배치된 뷰가 가려져야 하지만 click이 가능한 뷰가 우선시 되어 위에 배치될 수 있음
    - Button은 마크업순서와 상관없이 기본적으로 다른 뷰들 보다 먼저 배치됨. (당연히 같은 Button끼리는 나중에 작성한 Button이 위에 배치됨)

실행모습

 

 

소스코드

# activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".MainActivity">

    <!-- 기본 배치 : 좌상단 -->
    <Button
        android:id="@+id/btn01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn01"/>

    <!--1) 부모뷰를 기준으로 한 배치 : alignParentXXX -->
    <!-- 부모뷰(RelativeLayout) 우측 상단 배치 : alignParentRight -->
    <Button
        android:id="@+id/btn02"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn02"
        android:layout_alignParentRight="true"/>

    <!-- 부모뷰(RelativeLayout) 좌측 하단 배치 : layout_alignParentBottom -->
    <Button
        android:id="@+id/btn03"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn03"
        android:layout_alignParentBottom="true"/>

    <!-- 부모뷰(RelativeLayout) 우측 하단 배치 : layout_alignParentBottom, layout_alignParentRight -->
    <Button
        android:id="@+id/btn04"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn04"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"/>

    <!-- 부모뷰(RelativeLayout) 가운데 배치 : layout_centerInParent -->
    <Button
        android:id="@+id/btn05"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn05"
        android:layout_centerInParent="true"/>

    <!-- 부모뷰(RelativeLayout) 가로축(수평) 가운데 배치 : layout_centerHorizontal -->
    <Button
        android:id="@+id/btn06"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn06"
        android:layout_centerHorizontal="true"/>

    <!-- 부모뷰(RelativeLayout) 세로축(수직) 가운데 배치 & 우축정렬 : layout_centerVertical, layout_alignParentRight -->
    <Button
        android:id="@+id/btn07"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn07"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"/>



    <!--2) 특정 뷰를 기준으로 한 상대적인(Relative) 배치-->
    <!-- 특정 뷰(id="btn01")를 기준으로 상대적인 (Relative) 배치 - 특정뷰 아래쪽배치 : layout_below -->
    <Button
        android:id="@+id/btn08"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn08"
        android:layout_below="@id/btn01"/>

    <!--아래에 있는 btn09번부터하고 작성 - because, @id, @+id의 차이를 알아보기위해-->
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="aaaaaaaa"
        android:layout_above="@+id/btn09"/>

    <!-- 특정 뷰(id="btn03")를 기준으로 상대적인 (Relative) 배치 - 특정뷰의 왼쪽배치 : layout_toRightOf & 부모뷰 하단 배치 : layout_alignParentBottom -->
    <Button
        android:id="@+id/btn09"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="btn09"
        android:layout_toRightOf="@id/btn03"
        android:layout_alignParentBottom="true"/>

    <!-- 특정 뷰(id="btn05")를 기준으로 상대적인 (Relative) 배치 - 특정뷰 위 배치 및 왼쪽시작 위치 맞추기 : layout_above &  layout_alignLeft -->
    <Button
        android:id="@+id/btn10"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello world"
        android:layout_above="@id/btn05"
        android:layout_alignLeft="@id/btn05"/>



    <!-- 3) 겹쳐서 배치할 때 특이점-->
    <!-- 나중에 만든 뷰가 기존 뷰들을 덮어야 하지만 Button은 항상 최우선이 됨-->

    <!--이미지뷰가 LinearLayout보다 ...먼저 있으면 아래.. 나중에 있으면 위에.. 보이게 됨.-->
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:layout_centerVertical="true"/>

    <!-- 나중에 만든 뷰가 기존 뷰들을 덮어야 하지만 Button은 항상 최우선이 됨 : btn05버튼이 이 LineaerLayout보다 위에 있음.-->
    <!--LinearLayout안에 있는 Button도 가려짐.-->
    <LinearLayout
        android:id="@+id/layout"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="#ff0000"
        android:orientation="horizontal"
        android:layout_centerVertical="true">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher_foreground"
            android:clickable="true"/>

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

    </LinearLayout>

    <!--이미지뷰가 나중에 있으면 위에 보이게 됨.-->
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:layout_centerVertical="true"/>

</RelativeLayout>
반응형

+ Recent posts