수학적 접근

[Android/Kotlin] Custom Dialog 만들기 (NEW) 본문

개발/Android

[Android/Kotlin] Custom Dialog 만들기 (NEW)

평등수렴 2022. 3. 3. 16:06
반응형

이 글에서는 Custom Dialog (Custom Popup)을 만드는 방법을 알아볼 것입니다.

 

 

안드로이드에서 기본적으로 제공해주는 다이얼로그가 몇 있지만, 형식이 한정되어 있기 때문에

 

직접 Dialog를 만들어서 사용하는 것이 자유도가 높고 본인이 제작하고자 하는 앱과 잘 어울릴 것입니다.

 

 

이 글에서는 view binding을 이용하여 코드를 작성할 것입니다.

 

 

뷰 결합  |  Android 개발자  |  Android Developers

뷰 결합 뷰 결합 기능을 사용하면 뷰와 상호작용하는 코드를 쉽게 작성할 수 있습니다. 모듈에서 사용 설정된 뷰 결합은 모듈에 있는 각 XML 레이아웃 파일의 결합 클래스를 생성합니다. 바인딩

developer.android.com

 

위 공식문서에 따르면, view binding을 사용하기 위해서는 app 수준의 build.gradle 파일에 아래와 같은 내용을 추가해야 합니다.

 

android {
        ...
        viewBinding {
            enabled = true
        }
    }

위 내용을 추가한 다음 Sync를 해줍시다.

 

 

그러고 나서, activity_main.xml 에 다이얼로그를 열 수 있는 Button과, 다이얼로그에서 받아온 데이터를 적을 TextView를 만들겠습니다.

 

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="다이얼로그 띄우기"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:text="(Dialog에서 받아온 텍스트)"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />

</androidx.constraintlayout.widget.ConstraintLayout>

버튼의 id 는 button으로, 텍스트뷰의 id는 text라고 하였습니다.

 

그리고 activity_main.xml과 연결되어 있는 MainActivity.kt 파일에 버튼을 불러와서,

 

onClickListener까지 연결하는 것까지 우선적으로 해보겠습니다.

package me.dongmin.customdialog_new

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import me.dongmin.customdialog_new.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.button.setOnClickListener(this)
    }

    override fun onClick(view: View?) {
        when(view?.id) {
            binding.button.id -> {

            }
        }
    }
}

view binding을 이용하면, 위와 같이 레이아웃의 요소 id의 camel case 형태로 요소들을 호출할 수 있습니다.

(예: 레이아웃에서 id를 @+id/my_text로 하였다면, view binding에서는 binding.myText 로 호출)

 

* view binding 사용법을 더 자세히 보려면  더 보기 클릭

더보기

액티비티 코드 단에서 view binding을 활용하기 위해서는 아래와 같은  네 가지 구성 요소들을 넣으면 됩니다.

1. import 선언

2. binding 변수 선언

3. binding 인스턴스화

4. setContentView

. . .

import [패키지명].databinding.[레이아웃명의 Pascal Case. 이하 'MyLayout']Binding

. . .

class [클래스명] : AppCompatActivity() {

    . . .
    
    private lateinit var binding : MyLayoutBinding

    . . .
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
    	binding = MyLayoutBinding.inflate(layoutInflator)
        setContentView(binding.root)
        
        . . .
    
    }

}

 

 

또한, button에 직접 onClick() 메소드를 구현할 수 있지만, 저는 클래스 단에서 View.OnClickListener 인터페이스를 확장하여 onClick() 메소드를 implement하는 것을 선호합니다.

 

 

이제

binding.button.id -> {

}

내부에 Dialog를 띄우는 작업을 할 것입니다.

 

그 전에 우리가 띄울 Dialog를 xml 파일로 만들겠습니다.

 

여기서 만들 다이얼로그는 간단하지만 다음과 같은 핵심적인 기능들을 가진 다이얼로그입니다.

 

 

1. 부모 액티비티에서 전달한 내용을 다이얼로그에 표시한다.

2. '확인' 버튼을 눌렀을 때에는 다이얼로그가 닫히면서 다이얼로그에서 처리한 결과를 부모 액티비티로 돌려준다.

3. '취소' 버튼을 눌렀을 때에는 아무 동작 없이 다이얼로그가 닫힌다.

 

 

 

my_dialog.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="300dp"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_margin="8dp"
        >
        <TextView
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAlignment="center"
            android:textColor="#000000"
            android:text="텍스트"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <Button
                android:id="@+id/ok"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:layout_weight="1"
                android:text="확인"/>

            <Button
                android:id="@+id/cancel"
                android:layout_width="0dp"
                android:layout_height="40dp"
                android:layout_weight="1"
                android:text="취소"/>


        </LinearLayout>


    </LinearLayout>

</LinearLayout>

그리고 이 다이얼로그를 나타낼 MyDialog.kt 파일을 작성합니다.

 

 

 

MyDialog.kt

package me.dongmin.customdialog_new

import android.app.Dialog
import android.content.Context
import android.view.Window
import androidx.appcompat.app.AppCompatActivity
import me.dongmin.customdialog_new.databinding.MyDialogBinding

class MyDialog(private val context : AppCompatActivity) {
    
    private lateinit var binding : MyDialogBinding
    private val dlg = Dialog(context)   //부모 액티비티의 context 가 들어감

    fun show(content : String) {
        binding = MyDialogBinding.inflate(context.layoutInflater)
        
        dlg.requestWindowFeature(Window.FEATURE_NO_TITLE)   //타이틀바 제거
        dlg.setContentView(binding.root)     //다이얼로그에 사용할 xml 파일을 불러옴
        dlg.setCancelable(false)    //다이얼로그의 바깥 화면을 눌렀을 때 다이얼로그가 닫히지 않도록 함

        
        binding.content.text = content //부모 액티비티에서 전달 받은 텍스트 세팅
        
        //ok 버튼 동작
        binding.ok.setOnClickListener {

            //TODO: 부모 액티비티로 내용을 돌려주기 위해 작성할 코드

            dlg.dismiss()
        }

        //cancel 버튼 동작
        binding.cancel.setOnClickListener {
            dlg.dismiss()
        }

        dlg.show()
    }
}

이렇게까지 구현하면 위에서 말한 1번, 3번의 기능은 구현한 것입니다.

 

부모 액티비티에서 전달한 텍스트가 다이얼로그에 표시되며, 취소 버튼을 누르면 다이얼로그가 닫힙니다.

 

하지만 아직 확인 버튼을 눌렀을 때 부모 액티비티로 결과를 전달하는 것은 구현하지 않았습니다.

 

이것을 위해서는 다이얼로그에 콜백 인터페이스를 구현하여야 합니다.

 

MyDialog 클래스 내부의 아래쪽에 다음과 같이 작성합니다.

 

 

 

MyDialog.kt 에 MyDialogOKClickedListener 인터페이스 작성

. . .

class MyDialog(private val context : AppCompatActivity) {
    . . .

    fun show(content : String) {
       . . .
    }

    interface MyDialogOKClickedListener {
        fun onOKClicked(content : String)
    }
}

 

 

 

그리고 아래와 같은 함수를 작성하여, 부모 액티비티에서 부모 액티비티가 실행할 메소드를 등록할 수 있게 합니다.

 

MyDialog.kt 에 listener 선언 및 setOnOKClickedListener 메서드 작성

. . .

class MyDialog(private val context : AppCompatActivity) {
    . . .
    
    private lateinit var listener : MyDialogOKClickedListener

    fun show(content : String) {
        . . .
    }


    fun setOnOKClickedListener(listener: (String) -> Unit) {
        this.listener = object: MyDialogOKClickedListener {
            override fun onOKClicked(content: String) {
                listener(content)
            }
        }
    }


    interface MyDialogOKClickedListener {
        fun onOKClicked(content : String)
    }
}

 

 

 

그리고 확인 버튼을 클릭했을 때, onOKClicked() 메소드를 호출할 수 있도록 합니다.

 

MyDialog.kt 의 btnOK.setOnClickListener 수정

binding.ok.setOnClickListener {
    listener.onOKClicked("확인을 눌렀습니다")
    dlg.dismiss()
}

 

 

 

마지막으로,MainActivity.ktonClick() 메소드의 button.id 부분을 변경합니다.

 

MainActivity.kt 의 binding.button.id 부분 수정

binding.button.id -> {
    val dlg = MyDialog(this)
    dlg.setOnOKClickedListener{ content ->
        binding.text.text = content
    }
    dlg.show("메인의 내용을 변경할까요?")
}

 

 

 

 

이상을 종합한 MainActivity.kt 코드와 MyDialog.kt 코드는 아래와 같습니다.

 

MainActivity.kt

package me.dongmin.customdialog_new

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import me.dongmin.customdialog_new.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var binding : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        binding.button.setOnClickListener(this)
    }

    override fun onClick(view: View?) {
        when(view?.id) {
            binding.button.id -> {
                val dlg = MyDialog(this)
                dlg.setOnOKClickedListener{ content ->
                    binding.text.text = content
                }
                dlg.show("메인의 내용을 변경할까요?")
            }
        }
    }
}

MyDialog.kt

package me.dongmin.customdialog_new

import android.app.Dialog
import android.content.Context
import android.view.Window
import androidx.appcompat.app.AppCompatActivity
import me.dongmin.customdialog_new.databinding.MyDialogBinding

class MyDialog(private val context : AppCompatActivity) {

    private lateinit var binding : MyDialogBinding
    private val dlg = Dialog(context)   //부모 액티비티의 context 가 들어감

    private lateinit var listener : MyDialogOKClickedListener

    fun show(content : String) {
        binding = MyDialogBinding.inflate(context.layoutInflater)

        dlg.requestWindowFeature(Window.FEATURE_NO_TITLE)   //타이틀바 제거
        dlg.setContentView(binding.root)     //다이얼로그에 사용할 xml 파일을 불러옴
        dlg.setCancelable(false)    //다이얼로그의 바깥 화면을 눌렀을 때 다이얼로그가 닫히지 않도록 함


        binding.content.text = content //부모 액티비에서 전달 받은 텍스트 세팅

        //ok 버튼 동작
        binding.ok.setOnClickListener {

            listener.onOKClicked("확인을 눌렀습니다.")

            dlg.dismiss()
        }

        //cancel 버튼 동작
        binding.cancel.setOnClickListener {
            dlg.dismiss()
        }

        dlg.show()
    }

    fun setOnOKClickedListener(listener: (String) -> Unit) {
        this.listener = object: MyDialogOKClickedListener {
            override fun onOKClicked(content: String) {
                listener(content)
            }
        }
    }


    interface MyDialogOKClickedListener {
        fun onOKClicked(content : String)
    }

}

 

이렇게 아래와 같이 반응하는 다이얼로그를 완성했습니다.

 

 

이상의 내용을 담은 프로젝트를 github에 올려두겠습니다.

 

 

GitHub - dmseo1/dm-tistory-android-customdialog-new: tistory - '[Android/Kotlin] Custom Dialog 만들기 (NEW)' 게시글에 첨

tistory - '[Android/Kotlin] Custom Dialog 만들기 (NEW)' 게시글에 첨부할 프로젝트입니다. - GitHub - dmseo1/dm-tistory-android-customdialog-new: tistory - '[Android/Kotlin] Custom Dialog 만들기 (NEW)' 게시...

github.com

 

 

+++++

 

내가 마시고 있는 생수는 안전한 생수일까?

15년도부터 생수 수질기준 부적합 판정을 받은 이력을 확인할 수 있는 앱, <바른생수>를 출시하였습니다.

 

관심 한번씩만 부탁드립니다 ^^

 

바로가기!

 

 

반응형

'개발 > Android' 카테고리의 다른 글

[Android/Kotlin] XML Data Binding  (0) 2022.03.03
[Android/Kotlin] Custom Dialog 만들기  (3) 2020.02.07
Comments