-
[Android] Custom Dialog Fragment 생성 & EditText 유효성 검사안드로이드 2024. 4. 23. 22:40
✏️ TIL(Today I Learned)
커스텀 다이얼로그 프래그먼트를 만들어서 새로운 연락처를 추가할 수 있게 만들었다.
프로필 이미지는 선택이며, 나머지 사항은 필수로 입력해야 하며 유효성을 만족해야지 Save 버튼이 활성화된다.
Save버튼 클릭 시, 새로운 연락처가 추가가 되면서 상세 정보 화면으로 넘어가야 하기 때문에 클릭리스너를 파라미터로 받게 만들었다. 이를 Save 버튼의 setOnClickListener() 안에 넣었다.
interface ButtonClickListener { fun onSaveButtonClick() }
class AddContactDialogFragment(private val buttonClickListener: ButtonClickListener) : DialogFragment() { private lateinit var binding: FragmentAddContactDialogBinding private lateinit var galleryResultLauncher: ActivityResultLauncher<Intent> private var selectedImageUri: Uri? = null private var image: Image? = null private val editTexts get() = with(binding) { listOf( etName, etNumber, etEmail ) } private var nameEnable = false private var phoneNumberEnable = false private var emailEnable = false override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = AlertDialog.Builder(requireActivity()) val inflater = requireActivity().layoutInflater binding = FragmentAddContactDialogBinding.inflate(inflater) builder.setView(binding.root) with(binding) { tvDialogTitle.setText("Add Contact") btnNegative.setOnClickListener { dismiss() } btnPositive.setOnClickListener { addNewUser() buttonClickListener.onSaveButtonClick() dismiss() } ivProfile.clipToOutline = true ivProfile.setOnClickListener { openGallery() } } galleryResultLauncher() editTexts.forEach { editText -> editText.addTextChangedListener { editText.checkValidElements() isConfirmButtonEnable() } } return builder.create() } // 생략 }
override fun onCreateDialog()하여, 다이얼로그를 빌드했다.
이때, 아래와 같이 적으면
val builder = AlertDialog.Builder(this) .setTitle("Add Contact") .setPositiveButton("Save") { dialog, which -> addNewUser() buttonClickListener.onSaveButtonClick() } .setNegativeButton("Cancel") { dialog, which -> dismiss() }
이처럼 Title과 Button이 생기므로, 주의해야한다...
그리고 onStart()에서 다이얼로그의 배경을 투명하게 해 줬다.
override fun onStart() { super.onStart() dialog?.window?.apply { setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) } }
이걸 안 하면 아래와 같이 흰 배경이 나오게 된다.
프로필 이미지 선택은 아래와 같이 함수로 만들어서 구현했다. 런처를 통해서 OK일 때만, 선택한 이미지를 등록하도록 했다.
private fun galleryResultLauncher() { galleryResultLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == Activity.RESULT_OK) { val uriData: Intent? = result.data uriData?.data?.let { selectedImageUri = it binding.ivProfile.setImageURI(selectedImageUri) image = Image.ImageUri(selectedImageUri!!) isConfirmButtonEnable() } } } } private fun openGallery() { val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) galleryResultLauncher.launch(intent) }
그리고 override fun onCreateDialog()에서 프로필 이미지뷰의 clipToOutline = true를 하지 않으면 원형으로 나오지 않으니 주의해야 한다.
ivProfile.clipToOutline = true
그다음은, EditText의 유효성 검사이다.
override fun onCreateDialog()에서 각 EditText마다 addTextChangedListener를 달아서 입력이 변할 때마다, 유효성과 버튼의 활성화 상태를 검사하도록 했다.
editTexts.forEach { editText -> editText.addTextChangedListener { editText.checkValidElements() isConfirmButtonEnable() } }
유효성 검사를 위한 패턴들은 따로 오브젝트로 빼주었다.
object ValidExtension { //이름 (성 이름) fun String.validName() = Regex("^[A-Za-z가-힣]+(?:[ '-][A-Za-z가-힣]+)*\$").containsMatchIn(this) //전화번호 (국가코드 포함) fun String.validPhoneNumber() = Regex("^\\s*(?:\\+?(\\d{1,3})[-. (]*)?\\s*((01[016789]{3})[-. )]*)?((\\d{3,4})[-. ]*)+(\\d{4})+\\s*\$").containsMatchIn( this ) //이메일 정규 표현식 fun String.validEmail() = Regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}\$").matches(this) // 영어나 한글이나 숫자로 1~20자 사이 fun String.validDate() = Regex("^[A-Za-zㄱ-ㅣ가-힣0-9]{1,20}\$").matches(this) // 리마인드 시간 (분 단위, 3자리까지) fun String.validRemindTime() = Regex("^[1-9]\\d{0,2}\$").matches(this) }
아래와 같은 함수들을 통해, EditText 밑의 경고문구가 유효성에 따라 사라지고 나타나게 했다.
private fun checkValidName(text: String) { nameEnable = text.validName() binding.tvNameWarning.visibility = if (nameEnable) View.GONE else View.VISIBLE } private fun checkValidPhoneNumber(text: String) { phoneNumberEnable = text.validPhoneNumber() binding.tvNumberWarning.visibility = if (phoneNumberEnable) View.GONE else View.VISIBLE } private fun checkValidEmail(text: String) { emailEnable = text.validEmail() binding.tvEmailWarning.visibility = if (emailEnable) View.GONE else View.VISIBLE } private fun EditText.checkValidElements() = with(binding) { when (this@checkValidElements) { etName -> checkValidName(etName.text.toString()) etNumber -> checkValidPhoneNumber(etNumber.text.toString()) etEmail -> checkValidEmail(etEmail.text.toString()) else -> Unit } } private fun isConfirmButtonEnable() { with(binding) { btnPositive.isEnabled = nameEnable && phoneNumberEnable && emailEnable if (btnPositive.isEnabled) { btnPositive.setBackgroundResource(R.drawable.shape_dialog_btn_dark_clicked) } else { btnPositive.setBackgroundResource(R.drawable.shape_dialog_btn_dark) } } }
isConfirmButtonEnable()를 통해, 유효한 입력을 마치면 버튼의 색상이 밝게 변하도록 했다.
📌 공식문서
'안드로이드' 카테고리의 다른 글