기본 콘텐츠로 건너뛰기

[안드로이드] DialogFragment로 Custom Dialog 만들기


이번 포스팅에서는 안드로이드에서 기본 제공하는 다이얼로그가 아닌 커스텀 다이얼로그를 적용하면서 삽질 했던 내용에 대해서 써 보려고 한다.

만들고자 하는 모양은 아래와 같다. 이전에 포스팅한 라운드 및 그라데이션 처리된 나인패치 이미지가 적용된 다이얼로그이다.











하단의 스샷 처럼 라디오 버튼 [동의], [미동의] 선택값을 다이얼로그에 뿌려주는 것 까지 다루려고 한다. 하지만 목적은 커스텀 다이얼로그의 구현이고, 또 귀차니즘으로 인해 결과물의 폰트나 컬러, 버튼 등은 똑같이 구현 하지는 않으려고하니 많은 양해를 바란다. ㅠㅠ















참, 이 블로그의 포스팅 목적은 개발 초보인 본인의 삽질 방지를 위한 기록에 초점이 맞춰져 있으니, 잘못된 부분이 분명 있을 수 있고, 더 심플하고, 효율적이고 방법들이 얼마든지 있을 수 있다.그러한 부분들을 번거로움을 무릅쓰고 공유를 해 주신다면 감사히 받겠습니다.(편의상 반말로 쭉~ 갈 건데.. 왠지 여기서는 존댓말을 써야....)



여튼 이제 본론으로 들어가자.

다이얼로그를 커스텀하려니 막막함에 자연스레 검색을 하게 됐다. android custom dialog 라는 키워드로 구글에 검색을 해보면..

1. Dialog를 이용하는 방법
2. AlertDialog.Builder를 이용하는 방법
3. DialogFragment를 이용하는 방법

이렇게 3가지를 주로 찾을 수 있었다. 뭐야.. 뭐가 더 좋은거야...
검색을 계속 해보니 Android Developer 공식 홈에서 대화상자 페이지를 보니 뭘 써야 할지 알 수 있었다.

해당 내용은...

Dialog 클래스가 대화상자의 기본 클래스이지만, Dialog를 직접 인스턴스화하는 것은 삼가야 합니다. 대신 다음 서브클래스 중 하나를 사용하세요.
AlertDialog : 제목 하나, 최대 세 개의 버튼, 선택 가능한 품목 목록 또는 사용자 지정 레이아웃을 표시할 수 있는 대화상자입니다.
DatePickerDialog 또는 TimePickerDialog : 미리 정의된 UI가 있는 대화상자로 사용자로 하여금 날짜 또는 시간을 선택할 수 있게 해줍니다.
이러한 클래스가 대화상자의 스타일과 구조를 정의하지만, 대화상자의 컨테이너로는 DialogFragment를 사용해야 합니다. DialogFragment 클래스는 대화상자를 만들고 그 외관을 관리하는 데 필요한 모든 컨트롤을 제공합니다. Dialog 객체에서 메서드를 호출하는 것 대신입니다.
대화상자를 관리하기 위해 DialogFragment를 사용하면 사용자가 Back 버튼을 누르거나 화면을 돌릴 때 등 수명 주기 이벤트를 올바르게 처리하도록 보장할 수 있습니다. DialogFragment 클래스를 사용하면 대화상자의 UI를 더 큰 UI에 포함시킬 수 있는 구성 요소로 재사용할 수도 있습니다. 이것은 기존의 Fragment와 똑같습니다(대화상자 UI를 크고 작은 화면에서 서로 다르게 나타나도록 하고자 하는 경우 등).

이렇게나 추천하니 써봐야겠다.
그래서 작성해 본 코드다.

- MainActivity에서 동의/미동의 선택에 따른 CustomDialogFragment를 실행하는 부분이다.

RadioGroup radioGroup = (RadioGroup)findViewById(R.id.radio_group);radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
    @Override    public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
        String agreeOrDisagree = null;
        if (checkedId == R.id.radio_group) {
            agreeOrDisagree = getString(R.string.radio_agree);        } else {
            agreeOrDisagree = getString(R.string.radio_disagree);        }

        CustomDialogFragment dialog = CustomDialogFragment.newInstance(
                getString(R.string.custom_dialog_msg, agreeOrDisagree)
        );        dialog.show(getSupportFragmentManager(), "dialog");    }
});

CustomDialogFragment의 instance를 생성할때 resource의 string을 전달해줬는데,
<string name="radio_agree">동의</string><string name="radio_disagree">미동의</string><string name="custom_dialog_msg">    마케팅 정보 수신 %1$s 처리가 완료되었습니다.
</string>

이런식으로 구성되어 있으며, %1$s 부분을 변수 처럼 getString을 통해 넘기는 인자값을 매핑해서 사용 할 수 있다. 만약 더 추가 적인 변수가 필요하다면, %1$s, %2$s, %3$s 이런식으로 string값에 넣어두고, 그 갯수에 맞게 인자값도 콤마로 구분지어서 던져 주면 된다.


- CustomDialogFragment 부분이다.

public class CustomDialogFragment extends DialogFragment implements View.OnClickListener{
    private static final String TAG = "CustomDialogFragment";    private static final String ARG_DIALOG_MAIN_MSG = "dialog_main_msg";
    private String mMainMsg;
    public static CustomDialogFragment newInstance(String mainMsg) {
        Bundle bundle = new Bundle();        bundle.putString(ARG_DIALOG_MAIN_MSG, mainMsg);
        CustomDialogFragment fragment = new CustomDialogFragment();        fragment.setArguments(bundle);        return fragment;    }

    @Override    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        if (getArguments() != null) {
            mMainMsg = getArguments().getString(ARG_DIALOG_MAIN_MSG);        }
    }

    @Override    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());        View view = getActivity().getLayoutInflater().inflate(R.layout.layout_custom_dialog, null);        ((TextView)view.findViewById(R.id.dialog_confirm_msg)).setText(mMainMsg);        view.findViewById(R.id.dialog_confirm_btn).setOnClickListener(this);
        builder.setView(view);        return builder.create();    }

    private void dismissDialog() {
        this.dismiss();    }

    @Override    public void onClick(View v) {

        switch (v.getId()) {
            case R.id.dialog_confirm_btn:
                dismissDialog();                break;        }
    }
}

잉... 코드를 여기다 붙여 넣으니 원하는 만큼 잘 표시해 주지 못하는 것 같다. gist나 그런걸로 별도로 빼야 할 거 같다. 뭐여튼..

실행 시켜 보면 아래와 같이 보인다.



또 검색 검색.. 이것저것 해보면서 xml에서 속성이나 style도 건드려 봤지만, 자바 코드로 하는게 가장 확실한거 같다. 그래서 onCreateDialog에서 Dialog를 리턴하기 전에 아래와 같이 수정 해준다.

Dialog dialog = getDialog();dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));dialog.setCanceledOnTouchOutside(false);

setBackgroundDrawable로 전에 모서리 부분에 표시되던 기본 백그라운드를 투명으로 바꿔 표시되지 않게 했고, setCanceledOnTouchOutside는 말 그대로 다이얼로그 바깥부분 터치를 통한 닫기 여부의 설정이다.

그 뒤의 결과물은 아래와 같다.








다 됐다~ ..  됐나? 싶었다. 근데 좀 이상하다. width값이 내가 지정한대로 먹지 않는 듯한 느낌적인 느낌.
그래서 10dp를 때리고 실행 시켜봤더니, 역시나... width값이 먹질 않는다.ㅠㅠ

또 검색 검색.. 미천한 영어와 실력으로 인해 삽질삽질.... 그러다 발견한 .
1. onResume()에 width와 height를 resource의 dimes값을 자바 코드로 불러와 설정을 하고,
2. 다이얼로그의 layout을 RelativeLayout으로 설정한 뒤, width, height를 match_parent로 줘야한다.

라는 내용이었다. 그래서 작성한 onResume()과 그 결과다.

@Overridepublic void onResume() {
    super.onResume();
    int dialogWidth = getResources().getDimensionPixelSize(R.dimen.dialog_fragment_width);    int dialogHeight = ActionBar.LayoutParams.WRAP_CONTENT;    getDialog().getWindow().setLayout(dialogWidth, dialogHeight);}











허허 잘된다.
매번 느끼지만 스택오버플로우 없으면 개발을 어찌하나 싶다..ㅋㅋ

끝.


* 추가적으로 onCreateDialog에서 AlertDialog.Builder를 사용하지 않고 onCreateView를 사용해서 같은 결과를 낼 수도 있다.

@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.layout_custom_dialog, container, false);    ((TextView)view.findViewById(R.id.dialog_confirm_msg)).setText(mMainMsg);    view.findViewById(R.id.dialog_confirm_btn).setOnClickListener(this);
    Dialog dialog = getDialog();    dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));    dialog.setCanceledOnTouchOutside(false);
    return view;}

onCreateDialog 부분을 지우고 위의 코드를 넣어도 잘 나온다. 무슨 차이가 있는지는 정확하게 파악은 못 했다.


* 해당 소스를 깃헙의 gist를 통해 올려놨고, 아래에 첨부하니 참고하시기 바란다.



댓글

  1. 감사합니다 :D
    덕분에 삽질을 줄였습니다~~

    답글삭제
  2. 혹시 스크린샷에 사용된 폰트이름 알 수 있을까요?

    답글삭제
  3. I admire your point; a PHP website developer’s salary is almost equal to a freelance developer’s pay. You just need to be a responsible employee. You will be making money pretty easily if you develop your programming skills and experience professionally. Eiliana.com can help you make good money and balance your professional and personal life. It is a new yet great platform for freelancers.

    답글삭제

댓글 쓰기

이 블로그의 인기 게시물

[안드로이드] 라운드 처리 & 그라데이션이 들어간 나인패치 이미지 사용하기

- 안드로이드 스튜디오에서 라운드 처리 & 그라데이션이 들어간 나인패치 이미지 사용하기 현재 수행하고 있는 안드로이드 프로젝트의 디자이너가 적용을 원하는 이미지를 보내줬다. 이미지는 그라데이션에 라운드 처리가 된 이미지였다. 기존에 적용 방법만 대충 알아보고 사각형의 단일 색의 나인패치 이미지만 적용해봤던터라 그 방법 그대로 적용을 해봤다. - 기존에 나인패치에 대해 알고 있었던 것. 1. 그냥 drawable 폴더에 넣으면 되겠지.. 2. 아무 해상도 이미지로 asset studio에서 나인패치 적용하면 알아서 키우고 줄이고 해 주겠지 무지한 것을 인지하지 못한 본인은 디자이너에게 자신있게  Android asset studio 를 이용해 나인 패치를 해 달라하고, 그걸 받아서 적용했다. 그런데 왠걸.. 제대로 되지 않는다. - 증상 1. 라운드 처리된 코너 부분이 뭉개져 나옴 2. 그라데이션이 엉망임 3. 원본 이미지의 굵기가 제대로 표시가 안됨 4. 완벽한 반원 형태가 아니고 늘어남 ...........할거 많은데.. 온갖 삽질을 하며 시간을 낭비했다. 맘 같아선 그냥 네모에 초록색이나 보라색으로 shape로 그려 버리고 싶었다... 하지만 디자이너님이 라운드 수치는 좀 줄인다 쳐도 그라데이션은 포기 할 수 없다고....ㅠㅠ 결국 다시 삽질 삽질 삽질.. 그러다 Stack Overflow에서 글 하나를 발견하게 되었다. 1. 단순한 사각형 테두리 이미지라면 기본 drawable 폴더에 넣어도 상관없다. 2. 만약, 라운드 처리가 된 이미지라면 drawable-xxhdpi 한 곳에 넣어두고 그 보다 낮은 해상도의 경우 스케일 다운이 되면서 대부분의 경우에 잘 나타난다.  오.... 뭔가 실마리를 잡은거 같아서 또 폭풍 검색.. 다른 글에서는.. 1. 나인패치 이미지는 기본 drawable에 두면 안된다. 2. d

[안드로이드] 툴바(toolbar)

- 툴바 사용자가 할 수 있는 액션을 포함하며, 디자인의 일관성을 제공 명칭 : 롤리팝 부터 툴바 등장, 롤리팝 이전에는 액션 바. 툴바가 더 유연한 사용자 인터페이스를 제공함. AppCompat 라이브러리의 툴바를 사용하면 API 7까지 커버가 가능하다. 툴바 안에 일반 뷰(ImageView, TextView 등을 적적할게 추가하여 사용할 수 있다. 하지만 기본적으로 들어가 있는 패딩과 마진이 있기 때문에 사용할때 주의해야 한다.(툴바를 적용시키고 백그라운드 색을 확 티나게 적용하였을 경우 확인 할 수 있다.) - 툴바 VS 액션바 툴바의 시각적인 디자인이 변경된 것이 가장 큰 차이점. 액션바는 제약을 많이 갖고 있고, 항상 화면의 위에 나타나야 하며, 하나만 둘 수 있다. 액션바는 크기가 정해져 있어 변경될 수 없다. 툴바를 직접 포함시키는게 가능하고 원하는 곳에 위치시킬 수 있으며, 여러개가 포함 가능. 내부에 뷰를 둘 수 있고, 높이 조절도 가능하다. - 툴바에 메뉴 추가하기 res -> New -> Android resource file -> Resource type 을 menu로 변경 후 파일 생성 <item> 태그 추가 후 속성에 showAsAction 속성 부여 showAsAction : 액션 항목이 툴바에 나타날 것인지, 아니면 오버 플로 메뉴에 나올것인지를 나타낸다. ifRoom|withText 이렇게 값을 주면, 툴바에 공간이 있다면 액션항목의 아이콘과 텍스트가 모두 툴바에 나타난다. 만일 아이콘을 보여줄 공간은 있지만 텍스트의 공간은 없다면 아이콘만 나타난다. 또한 둘다 보여줄 공간이 없다면 오버플로 메뉴에 들어간다. 기타 옵션 always : 사용은 권장하지 않음, ifRoom을 사용해서 안드로이드 OS가 결정하게 하는 것이 좋음. never : 자주 사용하지 않는 액션의 경우 never로 지정해서 오버플로에 두는 것이 좋다. 사용 예 메뉴