2012년 6월 30일 토요일

Using DialogFragments


이 포스트는 구글 안드로이드 개발자 블로그의 포스트를 번역한 내용입니다. 원문과 표현상의 차이는 있을 수 있으나 전체적인 맥락은 동일함을 미리 말씀드리며, 한국 안드로이드 개발자들에게 도움이 되었으면 좋겠습니다.
허니컴은 앱에서 여러 액티비티 사이에서 재사용 할 수 있는 UI와 로직을 지원하기 위해 Fragments라는 것을 소개했습니다. 동시에 액티비티의 showDialog와 dismissDialog 메서드 대신 DialogFragments를 사용하길 권장하고 있습니다.
이 포스트에서는 v4 지원 라이브러리 (허니콤 이전 버전 호환을 위한)를 이용해 DialogFragments를 어떻게 사용하는지 간단한 대화창을 통해 보여드릴 것입니다. Dialogs와 관련한 디자인 가이드라인은 Android Design사이트를 참고하시기 바랍니다.

레이아웃

여기에 fragment_edit_name.xml라는 이름을 가진 대화창의 레이아웃이 있습니다.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/edit_name"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:layout_gravity="center" android:orientation="vertical"  >

    <TextView
        android:id="@+id/lbl_your_name" android:text="Your name"
        android:layout_width="wrap_content" android:layout_height="wrap_content" />

    <EditText
        android:id="@+id/txt_your_name"
        android:layout_width="match_parent"  android:layout_height="wrap_content"
        android:inputType=”text”
        android:imeOptions="actionDone" />
</LinearLayout>
두 개의 선택적인 속성을 주의해 보시기 바랍니다.  android:inputType=”text” 속성과 함께 설정된 android:imeOptions=”actionDone” 속성은 소프트 키보드가 엔터키 대신 완료키를 보이도록 설정합니다.

대화창 코드

대화창은 DialogFragment를 상속하며, 하위버전 호환성을 위해 v4 지원 라이브러리를 참조할 것입니다. (Eclipse 프로젝트에 라이브러리를 추가하려면, 프로젝트에서 오른쪽 마우스 버튼을 누르고 Android Tools | Add Support Library를 선택하면 됩니다.)
import android.support.v4.app.DialogFragment;
// ...

public class EditNameDialog extends DialogFragment {

    private EditText mEditText;

    public EditNameDialog() {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_edit_name, container);
        mEditText = (EditText) view.findViewById(R.id.txt_your_name);
        getDialog().setTitle("Hello");

        return view;
    }
}
대화창은 DialogFragment를 상속하고 반드시 필요한 빈 constructor를 포함합니다.  Fragments는 onCreateView() 메서드가 제공된 LayoutInflater를 이용해 실제로 뷰를 로딩하도록 구현합니다.

대화창 보이기

이제 Activity에서 대화창을 보여주는 코드가 필요합니다. 여기에 사용자의 이름을 입력할 EditNameDialog를 즉시 보여주는 간단한 예제가 있습니다. 작업 완료 후, 입력된 텍스트를 Toast로 보여줍니다.
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
// ...

public class FragmentDialogDemo extends FragmentActivity implements EditNameDialogListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        showEditDialog();
    }

    private void showEditDialog() {
        FragmentManager fm = getSupportFragmentManager();
        EditNameDialog editNameDialog = new EditNameDialog();
        editNameDialog.show(fm, "fragment_edit_name");
    }

    @Override
    public void onFinishEditDialog(String inputText) {
        Toast.makeText(this, "Hi, " + inputText, Toast.LENGTH_SHORT).show();
    }
}
여기에 주의깊에 봐야 할 부분이 몇 가지 더 있습니다.  먼저, Fragment API와 하위 버전과의 호환성을 위해 지원 라이브러리를 사용하고 있기 때문에, 샘플 코드의 Activity는 지원 라이브러리의 FragmentActivity를 상속합니다.  때문에, getFragmentManager() 대신 getSupportFragmentManager()를 호출합니다.
초기 뷰를 로딩한 후, 액티비티는 곧바로 show() 메서드를 호출하여 EditNameDialog를 보여줍니다.  이를 통해 DialogFragment가 Dialog에 어떤 일이 일어나고 있는지 알 수 있게 하며, Fragment 상태도 일관성을 유지하게 됩니다.  기본적으로 뒤로가기 버튼은 추가적인 코딩이 없어도 대화창을 사라지게 합니다.

대화창 사용하기

다음으로 EditNameDialog가 결과 문자열을 Activity로 리턴하도록 수정해 봅시다.
import android.support.v4.app.DialogFragment;
// ...
public class EditNameDialog extends DialogFragment implements OnEditorActionListener {

    public interface EditNameDialogListener {
        void onFinishEditDialog(String inputText);
    }

    private EditText mEditText;

    public EditNameDialog() {
        // Empty constructor required for DialogFragment
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_edit_name, container);
        mEditText = (EditText) view.findViewById(R.id.txt_your_name);
        getDialog().setTitle("Hello");

        // Show soft keyboard automatically
        mEditText.requestFocus();
        getDialog().getWindow().setSoftInputMode(
                LayoutParams.SOFT_INPUT_STATE_VISIBLE);
        mEditText.setOnEditorActionListener(this);

        return view;
    }

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (EditorInfo.IME_ACTION_DONE == actionId) {
            // Return input text to activity
            EditNameDialogListener activity = (EditNameDialogListener) getActivity();
            activity.onFinishEditDialog(mEditText.getText().toString());
            this.dismiss();
            return true;
        }
        return false;
    }
}
사용자 편의를 위해 mEditText.requestFocus()를 사용해 프로그램적으로 EditText로 포커스를 이동합니다.  다른 방법으로는 레이아웃 XML에 <requestFocus/> 태그를 사용할 수도 있습니다. 하지만, 경우에 따라서는 프로그램적으로 포커스를 호출하는 것이 더 좋을 수도 있습니다.  예를 들어, 레이아웃 XML에서 포커스를 호출할 경우, Fragment의 onCreateView() 메서드에 추가된 OnFocusChangeListener가 호출되지 않습니다.
사용자가 EditText에 포커스를 두면, 소프트 키보드가 자동으로 나타납니다.  프로그램적인 포커스로 동일한 이벤트를 발생시키려면, callgetDialog().getWindow().setSoftInputMode()를 호출합니다.  이전에 Dialog에서 사용하던 Window 관련 작업들도 동일하게 DialogFragment에서 사용할 수 있지만, getWindow() 대신에 getDialog().getWindow()를 호출해야만 합니다.  결과 대화창은 휴대폰과 태블릿에 동일하여 보여집니다:
onEditorAction() 메서드는 사용자가 완료키를 누를 때, 콜백을 처리하는데, 이는 EditText에 OnEditorActionListener를 설정했기 때문입니다. 입력된 텍스트를 전달하기 위해 Activity로 호출됩니다. 이를 위해 EditNameDialog에서 Activity에 의해 구현된 EditNameDialogListener 인터페이스를 선언합니다.  이것은 대화창이 많은 액티비티에서 재사용될 수 있게 해줍니다. onFinishEditDialog()에서 콜백 메서드를 발생시키기 위해 getActivity()를 호출하여 대화창을 실행하는 Activity의 참조키를 획득합니다.  이것은 MVCarchitecture에서 뷰가 컨테이너와 통신할 수 있게 하는 일반적인 패턴입니다.
대화창을 두 가지 방법 중 한 가지로 없앨 수 있습니다.  여기서는 Dialog 클래스 자체에 있는 dismiss()를 호출하고 있습니다.  이것은 show() 메서드처럼 Activity에서 호출 될 수도 있습니다.
이 글이 Dialogs와 관련이 있는 Fragments에 대한 이해하는데 어느 정도 도움이 되었으면 합니다.  이 글에 사용된 샘플 코드는 Google Code에서 찾으실 수 있습니다.
Fragments에 대한 추가적인 자료:

댓글 없음:

댓글 쓰기