본문 바로가기
Programming/Android Java

[Fragment]DialogFragment와 onDismissListner

by 개Foot/Dog발?! 2011. 9. 19.
간만이지만, 여전이 개발을 하면서 낙서하면서 정리하고자 한다.

아직 직접적으로 HoneyComb환경에서 작동가능한 App을 개발해보지 않았기때문에 DialogFragment는 좀 더 공부를 해봐야 할듯 하다.

이번에 요약하고자 하는것은 Google에서 Compatibility Library(이하 Fragment Lib)로 HoneyComb이 아니여도 Fragment를 사용가능하게 했다는 것이다. 새로이 개발하는데 있어서 HoneyComb style의 UI를 구현해 볼 수 있다.

그러나 기존 App을 HoneyComb style로 하려고 하면 도중 문제에 부딪히는것이 있다.
그 중 하나가 Activity에서 지원하였던 showDialog()/onCreateDialog() 이다.

HoneyComb이전 버전에서 Fragment Lib를 사용하려면 기존의 Activity가 아닌 FragmentActivity이여야 하고
이 안에서 영역별로 Fragment들을 전환해가며 바꾼다. 즉, Frame은 FragmentActivity이다. 이러하기 때문에 FragmentActivity는 startAcitivity()등을 통하여 Activity가 전환되지 아니하면 계속 유지 된다.

기존 Activity instance에 의존적인 ContentResolver, Intent Filter, Dialog 등등이 있는데 
위에서 설명하였다시피 FragmentActivity의 경우 계속 유지되는 frame이기 때문에 
특히 구현부에 대해서 구현이 필요한 Dialog가 자칫 쉽게하려면 FragmentActivity에 모조리 다 넣어야 하는 
이상한 구조가 생겨버린다. 코드상으로도 더욱 꼬여버릴 수 밖에 없는 입장이다.

그래서 사용되는 Fragment에서 각각의 Dialog를 구현해야 하는데.
기본적으로 AlertDialog.Builder를 사용하여 AlertDialog Object를 만들어서 직접 show()함수를 불러서 보여줄 수 도 있지만.
showDialog()/onCreateDialog()/onPrepareDialog()의 경우는 이렇게 할 수가 없다.
이 경우에는 FragmentDialog를 사용할 수 밖에 없다.

위 경우에서 처음부터 만들면 어떠한 방법으로 만들든 크게 문제가 되지 않지만,

기존에 있던 Apps을 구조적 변경을 적용하여 하려고 하면 크게 개인적으로 2가지 방법을 써 볼 수가 있다.

1. Dialog에 필요한 코드를 Activity에 구현시키고 이러한 것들을 Interface나, 이것들을 포함시킬 수 있는 Object를 넘겨주는 방법이다.
2. Dialog에 관련된 모든 코드를 DialogFragment에 이전시킨다.

위에서 1번의 경우 Dialog에서 각종 Listener들이 있는데, 특히 onCreateDialog()에서 Dialog에 Listener를 set하는 코드를 그대로 Dialog에 넣고 DialogFragment에 Dialog Object를 넣게 되기도 하는데, 이렇게 하게 되면 
불행하게도
작동을 하지 않게 된다.

왜냐하면 DialogFragment의 내부코드를 보면 그 답이 나온다.
 


@Override
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
    if (!mShowsDialog) {
        return super.getLayoutInflater(savedInstanceState);
    }

    mDialog = onCreateDialog(savedInstanceState);
    mDestroyed = false;
    switch (mStyle) {
        case STYLE_NO_INPUT:
            mDialog.getWindow().addFlags(
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
            // fall through...
        case STYLE_NO_FRAME:
        case STYLE_NO_TITLE:
            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    }
    return (LayoutInflater)mDialog.getContext().getSystemService(
            Context.LAYOUT_INFLATER_SERVICE);
}


@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    if (!mShowsDialog) {
        return;
    }

    View view = getView();
    if (view != null) {
        if (view.getParent() != null) {
            throw new IllegalStateException("DialogFragment can not be attached to a container view");
        }
        mDialog.setContentView(view);
    }
    mDialog.setOwnerActivity(getActivity());
    mDialog.setCancelable(mCancelable);
    mDialog.setOnCancelListener(this);
    mDialog.setOnDismissListener(this);
    if (savedInstanceState != null) {
        Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
        if (dialogState != null) {
            mDialog.onRestoreInstanceState(dialogState);
        }
    }
}


DialogFragment에서 getDialog()를 하면 자체적으로 가지고 있는 Dialog이다..
위 두함수에서 보면 Dialog가 onCreateDialog를 통해 만들어질 기본 Dialog Object를 가져오는 코드가 존재하며
onAcitivityCreated()에서 보면 FragmentDialog에서 implements하고 있는 Inteface들을 지정되어 있음을 볼 수 있다.

그래서 만약 Dialog의 onDismiss()/onCancel() 등을 onCreateDialog()구현부에 있는 Dialog의 Object에 
아무리 setOnCancelListener()/setOnDismissListener()를 해봤자 소용이 없는 것이다.

onCancel()/onDismiss()는 DialogFragment의 onCancel()과 onDismiss()의 것이 호출이 되며
이것을 상속하여서 구현해야 한다.

만약 기존 Dialog에서 onCancel()과 onDismiss()에서 구현된것들이 아직 Fragment등에 남아 있는부분과 연결이 된다면,
Inteface등을 통해 연결을 해주는 것을
개인적으로 추천한다.