2023.02.14 - 안드로이드 앱개발자 과정

CHA·2023년 2월 15일
0

Android



EditText


hint / textColorHint

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="input data"
    android:textColorHint="#FF0000"/> 

hint 속성은 아마 여기저기서 많이 볼 수 있는 속성입니다. 어떤 입력을 하고자 할때, 입력하기도 전에 이미 글씨가 써져있는 걸 본적 있을겁니다. 그리고 입력하면 그 글씨는 사라지죠. 이 속성은 사용자에게 여기에 어떤 내용이 들어가야한다고 알려주는 역할을 합니다.

textColorHint 속성은 이름 그대로 앞서 설정했던 hint 로 작성된 글씨의 색을 입히는 속성입니다.


inputType

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="input data"
    android:textColorHint="#FF0000"
    android:inputType="phone"/> 

가끔 핸드폰을 사용하다 보면 어떤 경우에는 숫자만 입력할 수 있는 키보드가 나오고, 어떤 경우에는 문자, 숫자 모두 입력할 수 있는 키보드가 나오는걸 볼 수 있습니다. 바로 inputType 속성 때문인데요, inputType 속성은 EditText 에 어떤 값이 입력되야 할지를 미리 지정하는 속성입니다. 그래서 inputType 의 속성값에 따라 소프트 키보드의 UI 도 바뀌는것을 알 수 있습니다. 속성값으로는 text , phone 외에도 이메일용 키보드, 패스워드 용 키보드 등 여러가지로 설정이 가능합니다.


lines / maxLines

<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="textMultiLine"
    android:lines="3"
    android:gravity="top"/>

우리가 흔히 사용하는 카카오톡 채팅을 보면, 글자를 입력하다가 줄이 넘어가면 글자 입력 상자의 크기가 한줄씩 커지는걸 볼 수 있습니다. 이건 maxLines 속성이 설정되어 있는건데, maxLines 속성은 설정한 속성값만큼의 줄을 입력할 수 있습니다. 그리고 그 줄 수를 넘어가게 되면 스크롤을 통해 이전에 입력했던 문자들도 볼 수 있는것이죠. lines 속성도 기능은 똑같습니다. 다만, 처음 EditText 가 제공될 때 maxLines 의 경우 한줄로 표시되고 글자를 입력해야 입력상자가 커지는 반면에, lines 의 경우 설정한 속성값만큼 미리 공간을 확보하는 차이점이 있습니다.

또한 gravity 속성을 봅시다. 속성값으로 top 을 설정했는데, 이렇게 해두면 글자를 입력할 때 맨 위부터 차례대로 글자를 입력할 수 있습니다.


ems

<EditText
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:ems="5"/>

앞선 예제들에서의 EditText 를 보면 얼마 입력하지도 않는데 EditText 의 밑줄길이, 즉 뷰의 길이가 너무 긴것 같습니다. ems 속성은 설정한 속성값 만큼 뷰의 너비를 지정해 줄 수 있습니다. 단, 너비를 지정하는것이지 글자 수를 제한하는것은 아닙니다.


아이콘 이미지 / drawble

--------- activity_main.xml
<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:drawableRight="@drawable/baseline_battery_charging_full_24"/>
------ baseline_battery_charging_full_24.xml
<vector android:height="24dp" android:tint="#000000"
    android:viewportHeight="24" android:viewportWidth="24"
    android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path 
        android:fillColor="@android:color/white" 
        android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4zM11,20v-5.5H9L13,7v5.5h2L11,20z"/>
</vector>

EditText 의 기본적인 형태는 위 그림처럼 언더바가 그려진 형태입니다. 별로 이쁘지는 않죠. drawable 속성을 이용하면 좀 더 이쁘게 꾸며볼 수 있습니다.


background

--------- activity_main.xml
<EditText
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:background="@drawable/bg_edit"
    android:padding="8dp"
    android:gravity="top"
    android:inputType="textMultiLine"/>
--------- bg_edit.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <stroke android:width="2dp" android:color="#FF0000"/>
        <solid android:color="@color/white"/>

    <corners android:radius="8dp"/>

</shape>

background 속성을 이용하면 EditText 의 배경을 지정해줄 수 있습니다. res 폴더의 drawable 폴더에 드로어블 리소스 파일을 새로 하나 만든 뒤, <stroke>, <corners> 등의 속성들을 이용하여 배경에 들어갈 이미지를 만들어줄 수 있습니다. 그 뒤에 background 의 속성값으로 이 드로어블 파일을 지정해주면 됩니다.


전화번호 입력받기 (자동 포커스 변경)

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_marginTop="16dp">

    <EditText
        android:id="@+id/et01"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="3"
        android:gravity="center"
        android:hint="010"
        android:inputType="phone"
        android:background="@drawable/bg_edit"
        android:padding="8dp"
        android:layout_margin="2dp"
        android:maxLength="3"/>

    <EditText
        android:id="@+id/et02"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="3"
        android:gravity="center"
        android:hint="1234"
        android:inputType="phone"
        android:background="@drawable/bg_edit"
        android:padding="8dp"
        android:layout_margin="2dp"
        android:maxLength="4"/>

    <EditText
        android:id="@+id/et03"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="3"
        android:gravity="center"
        android:hint="5678"
        android:inputType="phone"
        android:background="@drawable/bg_edit"
        android:padding="8dp"
        android:layout_margin="2dp"
        android:maxLength="4"/>

</LinearLayout>
public class MainActivity extends AppCompatActivity {

    EditText et01,et02,et03;

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

        et01 = findViewById(R.id.et01);
        et02 = findViewById(R.id.et02);
        et03 = findViewById(R.id.et03);

        et01.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() >= 3) et02.requestFocus();
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
    }
}

xml 파일에서는 앞서 배웠던 EditText 의 속성들을 활용하여 화면을 작성했으니 한번 보기만 하면 될것같습니다.

한번 보고 가야할 부분은 java 파일에 있는 addTextChangedListener() 입니다. 우리가 만들고자 하는 프로그램은 첫번째 EditText에서 3자리의 숫자입력을 다하면 자동으로 두번째 EditText로 포커스가 이동되는 프로그램입니다. 두번째와 세번째도 마찬가지이구요. 그러려면 EditText 에 글씨가 써질 때 마다마다를 체크해서 입력이 완료되면 포커스를 이동해주어야 합니다. 그럴때 사용할 메서드가 바로 addTextChangedListener() 입니다.

보통 우리가 버튼에서 리스너를 붙일 때, new View.OnClickListener() 를 이용하여 리스너 객체를 생성했습니다. 하지만 EditText 에서는 TextWatcher() 리스너를 사용합니다. TextWatcher 는 인터페이스 이며, 이 인터페이스를 익명클래스를 통해 객체로 만들어 사용하기 위해서는 세가지의 추상메소드를 구현해주어야 합니다.

  • public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

    이 메서드는 이름 그대로, 입력이 되기 전에 호출되는 메서드 입니다.

  • public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}

    이 메서드는 텍스트가 변경될때마다 호출되는 메소드 입니다. 여기에서 if 문으로 조건을 달아서 처리해줄 수 있습니다.

  • public void afterTextChanged(Editable editable) {}

    이 메서드는 텍스트의 입력이 완료된 시점에서 호출이 되는 메소드 입니다.


사용자 알림


Context

일단은 운영체제의 대리인과 같은 친구라고 생각합시다. 안드로이드 앱은 직접적으로 안드로이드 OS에 접근이 불가능하기 때문에 그 대신 Context 가 그 대리인 역할을 하는 구조입니다.

Context 안에는 여러가지 객체들이 존재한다. 진동관련, 바탕화면 관련 이것저것. 사실 우리는 이 객체들을 사용하는것입니다.

액티비티는 Context 를 상속받고, 그리고 MainActivity 는 액티비티를 상속받습니다. 그렇기 때문에 MainActivity.this 를 사용하면 context 의 기능을 사용할 수 있다.

Context 에 관한 이야기는 따로 포스팅을 해보도록 하겠습니다.


Toast

-------- activity_main.xml
<Button
    android:id="@+id/btn01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="토스트 띄우기"
    android:layout_gravity="center"/>
-------- MainActivity.java
public class MainActivity extends AppCompatActivity {

    Button btn01;

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

        btn01 = findViewById(R.id.btn01);
        
        btn01.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) { Toast.makeText(MainActivity.this,"통키",Toast.LENGTH_SHORT).show();
            }
        });
    }
}

버튼을 누르면 토스트 메시지를 띄워주는 프로그램 입니다. 여기에서 볼 건 버튼 리스너 안쪽에 작성된 토스트 생성 코드 입니다.

Toast.makeText(MainActivity.this,"통키",Toast.LENGTH_SHORT).show();

토스트를 띄우기 위해 Toast 클래스에게 요청합시다. 그러면 토스트 클래스의 static 메소드인 makeText() 를 호출합시다. 토스트 메시지를 띄우는 행위 자체는 아무래도 운영체제의 힘이 필요할 것 같습니다. 그래서 매개변수로 context 가 필요합니다. 두번째 파라미터로는 토스트로 띄울 문구를 입력하고, 마지막 파라미터로 얼마동안 띄울지를 전달합니다. 그리고 .show() 를 통해 화면에 보여줍시다.

한가지 주의할 점은 Context 를 전달할 때, 그냥 this 만 작성하면 안된다는 것 입니다. 우리는 지금 버튼을 눌렀을 때, 토스트를 작성했습니다. 즉, 익명 클래스 내부에서 토스트 코드가 작성된것 입니다. 우리가 필요한 Context 는 MainActivity 의 Context 입니다. 그런데 이 부분에서 그냥 this 만을 전달한다면, MainActivity 가 아닌 익명클래스를 지칭하게 됩니다. 그렇기 때문에 MainActivity.this 를 전달해주어야 합니다.


Dialog

혹시 인터넷에서 사진을 보거나, 무언가를 클릭했을 때 우리가 보고 있던 창 위로 다른 창이 떠서 무언가를 선택하게 하거나 하는 창을 본적 있을까요? 그것들이 바로 Dialog 입니다. 이번 예제에서는 버튼을 눌렀을 때, 다이얼로그가 뜨는 프로그램을 만들어 봅시다.

단순 메시지

먼저, 다이얼로그를 만들기 위해서는 AlertDialogBuilder 클래스의 도움이 필요합니다. 먼저 빌더 클래스에게 우리 다이얼로그 좀 만들어달라 부탁해야 하니, 빌더 객체를 하나 만듭시다.

아, 그리고 빌더라는 클래스는 다이얼로그 외에도 아주 많은 부분에서 사용이 되는 클래스 입니다.(이름만 같을 뿐 다른 기능을 하는 클래스들 이겠죠.) 그래서 어떤 클래스의 빌더인지를 명시해줄 필요가 있겠습니다. 여기까지만 보더라도 빌더 클래스라는 놈은 AlertDialog 의 이너 클래스겠네요. 자, 만들어 봅시다.

public class MainActivity extends AppCompatActivity {

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

        btn02 = findViewById(R.id.btn02);

        

        btn02.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

                builder.setTitle("다이얼로그");
                builder.setIcon(R.mipmap.ic_launcher);
				builder.setMessage("Do you wanna Quit?");
                builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                         Toast.makeText(MainActivity.this,"OK 버튼이 눌렸습니다.",Toast.LENGTH_SHORT).show();
                    }
                });

                builder.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Toast.makeText(MainActivity.this,"CANCEL 버튼이 눌렸습니다.",Toast.LENGTH_SHORT).show();
                    }
                });
                AlertDialog dialog = builder.create();
                
                dialog.setCanceledOnTouchOutside(false);
                dialog.setCancelable(false);
                dialog.show();

            }
        });
    }
}

AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); 를 이용하여 빌더 객체를 만들어 주었습니다. 그리고 builder.setTitle("다이얼로그"); , builder.setIcon(R.mipmap.ic_launcher); , builder.setMessage("Do you wanna Quit?");를 이용하여 다이얼로그에 대한 속성들을 설정해주었습니다.

그리고 setPositiveButton()setNegativeButton() 을 이용하여 OK 버튼과 CANCEL 버튼을 만들어줍시다.

어느정도 윤곽이 잡혔다면, AlertDialog dialog = builder.create(); 를 이용하여 빌더에게 요청합니다. 다이얼로그를 만들어줘!

그러면 빌더는 열심히 다이얼로그를 만들겠죠. 마지막으로 dialog.show() 를 해주면 설정된 다이얼로그가 버튼을 클릭하는 순간 보일겁니다. 추가적으로 dialog.setCanceledOnTouchOutside(false); , dialog.setCancelable(false); 속성은 각각 다이얼로그 바깥쪽을 클릭했을 때 다이얼로그가 꺼지는 속성과 다이얼로그 버튼이 아니면 어떤 방법으로든 다이얼로그를 끌 수 없게끔 하는 속성입니다.

List

단순 메시지만 출력하려니 조금 심심합니다. 그래서 과일 목록들을 만들어서 어떤 과일이 좋은지 한번 선택하는 다이얼로그를 만들어 봅시다. 코드가 좀 기니까 바뀐 부분만 간략하게 봅시다.

public class MainActivity extends AppCompatActivity {

''' 중략
    String[] items = new String[]{ "Apple" , "Banana" , "Orange"};
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn02 = findViewById(R.id.btn02);

        btn02.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ''' 중략
				// builder.setMessage("Do you wanna Quit?");
                builder.setItems(items, new DialogInterface.OnClickListener() {
                   
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Toast.makeText(MainActivity.this,items[i],Toast.LENGTH_SHORT).show();
                    }
                });
                ... 중략

            }
        });
    }
}

Apple, Banana, Orange 의 값이 담긴 String 배열 하나를 만들고, 빌더에게 메시지를 설정하는것 대신에 setItems() 로 아이템을 설정하라고 요구했습니다. 그러면 파라미터로 리스너를 전달해주어야 해서 클릭 리스너 객체를 전달해주었습니다. onClick() 메서드 안에는 토스트 메시지를 띄워주었습니다. onClick() 메서드의 두번째 파라미터 i 는 시작 숫자가 0임을 주의합시다.

Single Choice

그런데 앞서 만든 List 를 사용하다 보니, 좀 문제가 있는것 같습니다. 리스트의 아이템들을 선택을 하니 바로 다이얼로그가 꺼져버리는 문제입니다. 이렇게 되면 실수로 잘못누를 가능성도 생기게 됩니다. 그래서 이번에는 라디오 버튼이 들어가 있는 Single Choice 다이얼로그를 만들어 봅시다.

public class MainActivity extends AppCompatActivity {

''' 중략
    String[] items = new String[]{ "Apple" , "Banana" , "Orange"};
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn02 = findViewById(R.id.btn02);

        btn02.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ''' 중략
				
//                builder.setItems(items, new DialogInterface.OnClickListener() {
//                    // 14_ 리스트를 선택할 때의 리스너를 만들자!
//                    @Override
//                    public void onClick(DialogInterface dialogInterface, int i) {
//                        // 15_ 두번째 파라미터 i : 클릭된 항목의 위치 인덱스 번호! 시작 숫자가 0 이다!
//                        Toast.makeText(MainActivity.this,items[i],Toast.LENGTH_SHORT).show();
//                    }
//                });
                builder.setSingleChoiceItems(items, selectedItemPosition, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        Toast.makeText(MainActivity.this,items[i],Toast.LENGTH_SHORT).show();
                    }
                });
                ... 중략

            }
        });
    }
}

앞서 우리가 빌더에게 아이템들을 설정하라고 요구해서 빌더가 아이템들의 리스트를 작성하여 다이얼로그를 만들어주었습니다. 다만 실수로 선택할수도 있어서 라디오 버튼을 이용한 다이얼로그를 만들어보기로 했었습니다.

그래서 setItems() 대신에 setSingleChoiceItems() 를 이용해 아이템들을 하나만 선택하라고 요구했습니다. 역시 여기서도 onClick() 메서드의 두번째 파라미터는 0부터 시작입니다.

이렇게 라디오 버튼을 활용하여 다이얼로그를 만드니 선택도 가능하고 좀 더 직관적이 되었습니다. 하지만 라디오 버튼의 특성상 여러개를 선택할수는 없죠. 이제 여러개를 선택할 수 있는 다이얼로그도 만들어 봅시다.

Multi Choice

public class MainActivity extends AppCompatActivity {

''' 중략
	boolean[] checkedItems = new boolean[] { true , false , true };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn02 = findViewById(R.id.btn02);

        btn02.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ''' 중략

//                builder.setSingleChoiceItems(items, selectedItemPosition, new DialogInterface.OnClickListener() {
//                    @Override
//                    public void onClick(DialogInterface dialogInterface, int i) {
//                        Toast.makeText(MainActivity.this,items[i],Toast.LENGTH_SHORT).show();
//                    }
//                });
                builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i, boolean b) {
                        checkedItems[i] = b;
                    }
                });

                builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        
                        String s = "";
                        for(int k = 0; k < items.length ; k++) {
                            if(checkedItems[k]) s += items[k] + " ";
                        }

                        Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show();
                    }
                });
                ... 중략

            }
        });
    }
}

이 다이얼로그 또한 앞의 두 예제와 비슷합니다. 다만 하나가 아니라 여러개를 선택할 수 있는 셈이죠. 다시 말해, 라디오 버튼이 아니라 체크박스를 이용한 다이얼로그 입니다. setMultiChoiceItems() 메서드를 호출하면 파라미터로 보고자 하는 배열과 boolean 배열, 그리고 클릭리스너를 전달해주어야 합니다.

클릭 리스너는 익명클래스를 통해 객체 생성과 동시에 onClick() 메서드를 구현해주었으며, 이 메서드의 두번째 파라미터인 i 는 클릭된 항목의 위치 인덱스 번호를 의미하며, 세번째 파라미터인 b 는 클릭된 항목의 true / false 값 여부를 의미합니다.

그래서 checkedItems[i] = b; 라는 코드를 작성하게 되면, 클릭을 했을 때, 클릭된 항목의 true 혹은 false 값을 boolean 배열인 checkedItems 배열에 담는것 입니다. 그래서 담겨진 boolean 배열을 이용하여 setPositiveButton()onClick() 메서드 안쪽에 눌린 항목의 문자열 값만 저장을 해준뒤 토스트 메시지로 띄울 수 있게됩니다. 그러면 다이얼로그의 OK 버튼이 눌렸을 때, 토스트 메시지로 눌린 항목에 대한 내용만 띄워지게 됩니다.

Custom View

지금까지 해보았던 예제들에서는 모두 빌더가 이미 만들어 놓은 다이얼로그의 UI 를 사용해왔습니다. 우리는 우리 맘대로 UI 를 변경해서 사용해봅시다. 그러려면 먼저 xml 을 이용하여 화면을 구성해주어야 겠죠?

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="200dp"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <TextView
        android:id="@+id/dialogTv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="CUSTOM VIEW"
        android:textSize="24sp"
        android:textStyle="bold"
        android:textColor="@color/teal_700"/>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        android:adjustViewBounds="true"/>

    <Button
        android:id="@+id/dialogBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button"/>

</LinearLayout>

그리고 난 뒤에 빌더에게 우리가 만든 xml 파일로 다이얼로그를 구성해 달라고 부탁해야 합니다.

public class MainActivity extends AppCompatActivity {

	Button btn02;
    TextView dialogTv;
    Button dialogBtn;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        btn02 = findViewById(R.id.btn02);
        
        btn02.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

                builder.setView(R.layout.dialog);

                AlertDialog dialog = builder.create();
                dialog.setCanceledOnTouchOutside(false);
                dialog.setCancelable(false);
                dialog.show();

             
                dialogTv = dialog.findViewById(R.id.dialogTv);
                dialogBtn = dialog.findViewById(R.id.dialogBtn);
                dialogBtn.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        dialogTv.setText("Good!");
                    }
                });
            }
        });
    }
}

builder.setView(R.layout.dialog); 를 이용하여 우리가 만든 dialog.xml 파일을 빌더에게 주고 이걸 기반으로 다이얼로그를 만들어 달라고 합니다. 그러고 난 뒤에는 앞선 예제와 동일하게 create() 을 통해 만들어주고, show() 를 통해 화면에 띄워줍니다.

그리고 나서 우리가 해야할 작업은 무엇이냐하면, 앞서 만들었던 dialog.xml 에 버튼 하나와 텍스트뷰 하나를 만들어놨습니다. 그래서 버튼을 누르면, setText 를 통해 텍스트뷰의 글씨를 바꿔줄 예정입니다.

여기서 아주 중요한 부분이 있습니다. 바로 findViewById() 입니다. 지금까지 우리는 xml 로 만들어진 뷰를 제어하기 위해 뷰의 id 값을 자바 파일로 가져와 제어하였습니다. 그때 id 값을 가져올 수 있는 메서드가 바로 findViewById() 였습니다.

그런데 중요한 부분은 우리가 가져왔던 많은 뷰의 id 들이 모두 activity_main.xml 파일이었다는 점입니다. 그런데 다이얼로그에 띄워진 버튼과 텍스트뷰는 activity_main.xml 에 들어있는 놈들이 아니라 dialog.xml 에 들어있는 놈들인거죠. 그렇기 때문에 똑같이 findViewById() 를 사용할 수 없습니다. 메서드 앞쪽에 어느 객체의 뷰인지 명시해주어야 합니다. 이 부분에 관해서는 따로 다뤄보도록 하겠습니다.

어쨋든 버튼과 텍스트 뷰는 dialog 위에 있는 놈들이므로, AlertDialog dialog = builder.create(); 를 통해 만들어진 dialog 객체를 이용하여 findViewById() 를 해줘서 버튼과 텍스트 뷰의 id 값을 가져옵시다. 이렇게 하면 다이얼로그의 뷰들 또한 제어가 가능해집니다.


소리

안드로이드에는 음악과 관련한 클래스가 2가지 존재합니다. 하나는 MediaPlayer로 음악이나 비디오를 위한 클래스입니다. 그래서 음악의 길이가 긴 음악에 사용하기 좋습니다. 다른 하나는 이제부터 사용해볼 SoundPool 입니다. 9초 정도의 짧은 음악을 사용할 때 사용하는 클래스 입니다.

SoundPool 의 경우 음악파일을 사운드풀 객체가 load() 메서드를 통해 음원들을 SoundPool 내부에 등록시킵니다. 등록을 시키면 각 음원들은 고유한 번호를 갖게 됩니다. 이 ID 를 기반으로 우리가 음원을 가져와 사용하는 구조인데, 이 사운드풀을 만들기 위해서는 SoundPool.Builder 의 도움이 필요합니다.

이번에는 각 4개의 버튼들을 만들어서 버튼을 누르면 음악이 나오는 프로그램을 한번 짜봅시다. 이번에 xml 에서 볼만한 부분은 크게 없기 때문에 코드만 보고 자바파일 위주로 한번 봅시다.

<Button
    android:id="@+id/btn01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="START"/>

<Button
    android:id="@+id/btn02"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:backgroundTint="@color/black"
    android:text="AGAIN"/>

<Button
    android:id="@+id/btn03"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:backgroundTint="@color/teal_700"
    android:text="Good job"/>

<Button
    android:id="@+id/btn04"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="긴 음원 플레이"
    android:backgroundTint="#FF8809"/>

builder 객체 만들어 SoundPool 만들기

앞서 다이얼로그에서 AlertDialog 를 만들기 위해 빌더에게 요청했던게 기억나실까요? 소리 또한 마찬가지로 빌더 클래스의 도움을 받아야 합니다. 그러면 빌더 객체를 한번 만들어 봅시다.

public class MainActivity extends AppCompatActivity {

    Button btn01, btn02, btn03, btn04;
    SoundPool sp;
    SoundPool.Builder builder;
    int sdStart, sdAgain, sdGoodJob, sdMusic; 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        builder = new SoundPool.Builder();
        builder.setMaxStreams(10); 
        sp = builder.build();

        
    }
    
}

builder = new SoundPool.Builder(); 를 이용하여 빌더 객체를 생성해줍니다. 그리고 나서 builder.setMaxStreams(10); 을 이용하여 한번에 플레이가 가능한 음원의 수를 지정해줍니다. 그리고 난 뒤에 sp = builder.build(); 을 이용해서 SoundPool 을 생성해주면 됩니다.

음원 등록하기

Builder 를 이용해서 SoundPool을 생성했으므로 이제 SoundPool 을 이용해 음원을 등록해봅시다.

public class MainActivity extends AppCompatActivity {

    Button btn01, btn02, btn03, btn04;
    SoundPool sp;
    SoundPool.Builder builder;
    int sdStart, sdAgain, sdGoodJob, sdMusic; 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        builder = new SoundPool.Builder();
        builder.setMaxStreams(10); 
        sp = builder.build();

        sdStart = sp.load(this,R.raw.s_sijak,0); 
        sdAgain = sp.load(this,R.raw.s_again,0);
        sdGoodJob = sp.load(this,R.raw.s_goodjob,0);
        sdMusic = sp.load(this,R.raw.kalimba,0);
        
    }
}

SoundPool 객체를 이용해 load() 메서드를 호출 합니다. 매개변수로는 context 와 음원파일, 우선순위를 전달합니다. 음원파일의 경우 res 폴더에 raw 폴더를 만든 뒤 음원파일을 넣어줍시다. 그리고 안드로이드에서는 우선순위를 음원을 등록할 때 넣어주는것 보다 음원을 play() 할 때 지정할것을 권장합니다. 그래서 등록 시에 우선순위는 0 을 전달해줍시다.

음원 재생하기

public class MainActivity extends AppCompatActivity {

    Button btn01, btn02, btn03, btn04;
    SoundPool sp;
    SoundPool.Builder builder;
    int sdStart, sdAgain, sdGoodJob, sdMusic; 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        builder = new SoundPool.Builder();
        builder.setMaxStreams(10); 
        sp = builder.build();

        sdStart = sp.load(this,R.raw.s_sijak,0); 
        sdAgain = sp.load(this,R.raw.s_again,0);
        sdGoodJob = sp.load(this,R.raw.s_goodjob,0);
        sdMusic = sp.load(this,R.raw.kalimba,0);
        
        btn01 = findViewById(R.id.btn01);
        btn02 = findViewById(R.id.btn02);
        btn03 = findViewById(R.id.btn03);
        btn04 = findViewById(R.id.btn04);

        btn01.setOnClickListener(listener );
        btn02.setOnClickListener(listener );
        btn03.setOnClickListener(listener );
        btn04.setOnClickListener(listener );
    }
        View.OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(view.getId() == btn01.getId()){
                sp.play(sdStart,0.9f,0.9f,3, 0 ,1.0f);
            }else if(view.getId() == btn02.getId()){
                sp.play(sdAgain,0.8f,0.8f,2, 0 ,1.0f);
            }else if(view.getId() == btn03.getId()){
                sp.play(sdGoodJob,0.7f,0.7f,1, 0 ,1.0f);
            } else {
                sp.play(sdMusic, 0.7f,0.7f,4,0,1.0f);
            }
        }
    };
}

버튼의 클릭 이벤트에서 play() 메서드를 호출하여 각각의 음원을 재생해줍니다. 메서드의 파라미터로, 음원의 ID 값, 왼쪽 볼륨 크기 , 오른쪽 볼륨 크기, 우선순위, 반복 횟수, 재생 속도 를 전달해주면 됩니다.

profile
Developer

0개의 댓글