금요일, 11월 11, 2016

안드로이드 개발노트3: 메뉴바 만들기 (Android Developer's Note: Implementing Menu Bar)


이번 포스트에서는 원하는대로 메뉴바를 구성하고, 명시적 인텐트(Explicit Intent), 또는 알림창(Alert Dialog)를 사용해서 유저와 값을 효과적으로 주고받는 방법에 대해 살펴보겠다.

우선, 메뉴를 만드려면 메뉴를 위한 layout 파일(.xml)이 있어야 하고 이것을 Activity에서 불러오기만 하면 된다.

블루투스 기능을 사용하고 싶은데, 블루투스 버튼을 홈 화면에다가 크게 위치시키면 보기 안좋기 때문에 메뉴바에 아이콘 형태로 넣어보자.

우선 블루투스 아이콘 이미지를 미리 저장해두고, layout 파일의 코드를 다음과 같이 사용하면 된다.
<menu 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" tools:context=".MainActivity">
    <item
        android:id="@+id/scan"
        android:icon="@drawable/bluetooth"
        android:showAsAction="ifRoom"
        android:title="scan"/>
    <item android:id="@+id/touch_short"
        android:title="@string/touch_short"
        android:orderInCategory="100" />
    <item android:id="@+id/touch_twice"
        android:title="@string/touch_multiple"
        android:orderInCategory="200" />
    <item android:id="@+id/touch_long"
        android:title="@string/touch_long"
        android:orderInCategory="300" />
</menu>
코드를 보면 알 수 있듯이, 블루투스 버튼의 경우에만 android:showAsAction="ifRoom"이라는 속성을 주었다. 이 속성을 사용하면 해당 메뉴 아이템이 별도로 메뉴 버튼을 누르지 않아도 메뉴바 자체에 노출되게 된다.

이제 layout이 준비가 완료되었으므로, Activity 쪽에서 이를 View로 적용시키자. 그리고 각 아이템을 클릭했을 시 동작을 정의해주기 위해 onOptionsItemSelected 함수도 추가한다.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch(id) {
        //cases
        }
        return super.onOptionsItemSelected(item);
}
이제 남은 것은 이 메뉴가 사용자와 상호작용을 하도록 만드는 것이다. 우선 크게 두 가지 방법을 살펴볼텐데, 첫 번째 방식은 명시적 인텐트(Explicit Intent)라고 하는 방법이고, 두 번째 방식은 알림창(Alert Dialog)을 이용한 방식이다.

명시적 인텐트는 쉽게 말해서 Activity 간에 이동을 통해 값을 전달하는 방식이다. Activity A, B가 있다고 가정할 경우, A에서 B로 값을 넘겨주고 B에서 A로 값을 넘겨줄 수도 있다. 단, A에서 B를 호출할 경우, B가 실행되는 동안 A는 잠시 정지된 상태가 되기 때문에 복잡한 앱을 만들 때는 주의해서 사용해야 한다.

명시적 인텐트를 사용하기 위해서는 준비 과정이 필요하다. 우선 호출할 Activity 자체를 만들어야 한다. java 파일로 Activity 클래스를 하나 정의하면 된다.
그리고, AndroidManifest 파일에 가서
<activity android:name=".B"
    android:label="@string/app_name">
</activity>
이렇게 새로운 Activity가 추가되었음을 알려줘야 한다. (B는 자신이 새로 만든 Activity의 이름으로 바꾸면 된다. 맨 앞의 점을 빼먹지 않도록 유의한다.)

이제 새로 만든 Activity B를 호출할 A의 java 파일로 가자. B를 호출하기 위해서는 세 단계를 거쳐야 하는데, 첫 번째가 바로 A와 B를 연결시켜주는 Intent 하나를 만드는 것이다.
Intent i = new Intent(A.this, B.class);
이렇게 선언하면 된다. 두 인자 중 앞에 것은 호출하는 Activity이고 .this가 붙는다는 점과 뒤의 인자는 호출 당하는 Activity이고 .class가 붙는 다는 점을 주의해야 한다.

그 다음은 B로 보낼 데이터를 Intent에 싣는 것이다. 다음과 같이 Extra라는 것을 통해 key와 value 형태로 넣어주면 된다.
i.putExtra("myKey", "myValue");
이렇게 하면 B쪽에서 myKey라는 key 값으로 데이터를 읽을 수 있게 된다.

마지막 단계로 이 Intent를 통해 B를 실행시킨 뒤, 결과 값이 돌아올 때까지 A를 정지하고 B를 실행시키면 된다.
startActivityForResult(i, 1);
두 번째 인자인 1은 Request Code를 나타낸다. 임의의 값을 지정해주면 된다.

그리고 B로 부터 결과 값이 돌아왔을 때 이를 받아서 처리해주기 위한 함수를 정의해준다.
public void onActivityResult(int requestCode, int resultCode, Intent i) {
    super.onActivityResult(requestCode, resultCode, i);
    if(requestCode == 1) {
        if(resultCode == Activity.RESULT_OK) {
            String resultString = i.getStringExtra("yourKey");
            //do something
        }
    }
}
이렇게 정의를 해두면 B가 종료되고 A로 다시 돌아왔을 때 자동으로 이 함수가 발동되면서 필요한 처리를 해준다. 중간에 requestCode를 처음에 보낼 때의 숫자로 맞춰주고, resultCode가 RESULT_OK인지 체크해주어야 한다. 이 RESULT_OK는 B쪽에서 결과를 정상적으로 보낸다고 신호를 주는 것이다. 그리고 yourKey라는 key 값으로 Extra data를 참조하는 것을 볼 수 있는데, 예상하다시피 B쪽에서 yourKey라는 key 값으로 i라는 Intent에 값을 넣어서 A쪽으로 다시 되돌려 보낸 것이다.

그럼 A쪽은 완료됐고, B.java로 가보자.

onCreate 함수 내부에 다음과 같은 코드를 추가해준다.
Intent intent = getIntent();
String queryString = intent.getString("myKey");
이렇게 하면 A로부터 넘겨받은 데이터를 확인할 수 있다. 그 다음, 다시 A쪽으로 결과를 return해주려면 다음과 같은 코드를 쓰면 된다.
Uri data = Uri.parse("content://A/B");
Intent i = new Intent(null, data);
i.putExtra("yourKey", "yourValue");
setResult(RESULT_OK, i);
finish();
URI를 통해 Intent를 만들어주고, 동일한 과정으로 Intent에 Extra data를 넣어서 보내는 것을 확인할 수 있다. 끝 부분에 RESULT_OK로 설정하는 부분과 finish()를 통해 B를 종료하고 A로 i를 넘겨주는 것까지 하면 명시적 인텐트 과정이 완료된다.

두 번째 상호작용 방법인 알림창(Alert Dialog)을 이용하는 방법은 좀 더 간편하고 Activity 간에 이동이 없기 때문에 좀더 가벼운(?) 방법이다. Android 자체에서 여러가지 기본적인 템플릿들을 제공하기 때문에 긴 설명은 하지 않고, 가장 많이 활용할 수 있는 두 가지 예제를 간단하게 살펴보겠다. 첫 번째는 리스트형 알림창이다. 리스트가 팝업되고, 그 중 하나의 아이템을 클릭할 시에 특정 동작을 수행하도록 만들 수 있다.
AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this);
alertBuilder.setIcon(R.drawable.logo); //아이콘 설정
alertBuilder.setTitle("Title"); //제목 설정
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.select_dialog_item);
adapter.add("1");
adapter.add("2");
adapter.add("3");
//취소 버튼
alertBuilder.setNegativeButton("Cancel",
    new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            dialog.dismiss();
        }
    });
alertBuilder.setAdapter(adapter, new DialogInterface.OnClickListener() {
    String strName = adapter.getItem(id);
    //do something
});
alertBuilder.show(); //팝업창을 띄운다.
그리고 두 번째는 text input을 받아서 처리할 수 있는 예제이다. 여러 옵션을 통해 받고 싶은 값만 받을 수도 있다. 이 예제에서는 양수 또는 음수인 정수만 값으로 받도록 제한한다.
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle(title);
alert.setMessage("Score per touch");
final EditText input = new EditText(this);
alert.setView(input);
input.setInputType(InputType.TYPE_CLASS_NUMBER|InputType.TYPE_NUMBER_FLAG_SIGNED);
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int whichButton) {
 
    String value = input.getText().toString().trim(); //EditText내용을 가져오기
    if(value.getBytes().length <= 0) return; //null인지 체크
    int result = Integer.parseInt(value);
    //do something
    }
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int whichButton) {
    //취소 버튼
    }
});
alert.show();

댓글 없음:

댓글 쓰기