DialogFragment로 Custom Dialog 만들기

프로그래밍/Android (Java)|2019. 5. 17. 08:45
반응형

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

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


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



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



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

다이얼로그를 커스텀하려니 막막함에 자연스레 검색을 하게 됐다. 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를 통해 올려놨고, 아래에 첨부하니 참고하시기 바란다.



<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent">

 

<LinearLayout

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

android:paddingLeft="20dp"

android:paddingRight="20dp"

android:paddingTop="30dp">

 

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

 

<TextView

android:id="@+id/terms_tv_sub_title"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_vertical"

android:text="서브타이틀 표시 영역"

android:textSize="12dp" />

 

<RadioGroup

android:id="@+id/radio_group"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginLeft="20dp"

android:orientation="horizontal"

android:textSize="12dp"

android:visibility="visible">

 

<RadioButton

android:id="@+id/radio_agree"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/radio_agree" />

 

<RadioButton

android:id="@+id/radio_disagree"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/radio_disagree" />

</RadioGroup>

</LinearLayout>

 

<TextView

android:id="@+id/terms_tv_detail_contents"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_marginTop="30dp"

android:text="약관 전문 표시 영역"

android:textSize="12dp" />

</LinearLayout>

</RelativeLayout>

view rawactivity_main.xml hosted with ❤ by GitHub

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 void onResume() {

super.onResume();

 

int dialogWidth = getResources().getDimensionPixelSize(R.dimen.dialog_fragment_width);

int dialogHeight = ActionBar.LayoutParams.WRAP_CONTENT;

getDialog().getWindow().setLayout(dialogWidth, dialogHeight);

}

 

@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);

Dialog dialog = builder.create();

dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

dialog.setCanceledOnTouchOutside(false);

 

return dialog;

}

 

/*

@Override

public 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;

}

*/

 

private void dismissDialog() {

this.dismiss();

}

 

@Override

public void onClick(View v) {

 

switch (v.getId()) {

case R.id.dialog_confirm_btn:

dismissDialog();

break;

}

}

}

view rawCustomDialogFragment.java hosted with ❤ by GitHub

<?xml version="1.0" encoding="utf-8"?>

 

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@drawable/ninepatch_common_box_gray"

android:padding="20dp">

 

<TextView

android:id="@+id/dialog_confirm_msg"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentTop="true"

android:gravity="center_horizontal"

android:textSize="13sp"

android:textColor="#111111"

android:textStyle="bold" />

 

<Button

android:id="@+id/dialog_confirm_btn"

android:layout_width="66dp"

android:layout_height="wrap_content"

android:layout_below="@+id/dialog_confirm_msg"

android:layout_centerHorizontal="true"

android:layout_marginTop="15dp"

android:background="@null"

android:text="확인"

android:textSize="12dp" />

 

</RelativeLayout>

view rawlayout_custom_dialog.xml hosted with ❤ by GitHub

public class MainActivity extends AppCompatActivity {

 

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

 

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");

}

});

}

}

view rawMainActivity.java hosted with ❤ by GitHub

공유

 

[출처] http://darrenkwon.blogspot.com/2017/04/dialogfragment-custom-dialog.html

반응형

댓글()