본문 바로가기

TIL

[TIL] Dialog, 알림 (Notification), Intent FLAG

[오늘 배운 내용]

-1- DIalog

  • 다이얼로그란 사용자에게 결정을 내리거나 추가 정보를 입력하라는 등 메세지를 표시하는 창이다.
  • 다이얼로그는 보통 화면을 가득 채우지 않고, 사용자가 다음으로 진행하기 전에 조치를 취해야 하는 이벤트에 사용된다.
  • 일반적으로 AlertDialog을 사용하며, AlertDialog Android 앱의 메세지, 옵션, 버튼이 포함된 Dialog를 표시할 수 있다.
    • 기본형식의 AlertDialog 는 제목한개, 버튼 최대 세개, 선택 가능한 항목의 목록 또는 맞춤 레이아웃 한 개를 표시할 수 있는 Dialog이다.
  • AlertDialog의 하위 클래스로 특정 날짜 및 시간 Dialog 창을 띄워주는 DatePickerDialog, TimePickerDialog가 있다. 각각 날짜, 시간 Dialog를 띄워주는 클래스이다.
  • AlertDialog및 하위 클래스로 만들 수 있는 Dialog 버튼을 클릭하면 해당 Dialog창이 나오며, Dialog의 상호작용이 MainActivity 상단 TextView에 반영이 되도록 지정.

 

 

 

  • xml
<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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv_Title"
        android:layout_width="200dp"
        android:gravity="center"
        android:layout_height="wrap_content"
        android:layout_marginTop="40dp"
        android:text="TextView"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <Button
        android:id="@+id/btn_dialog"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:text="dialog"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btn_custom"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="custom dialog"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn_dialog" />

    <Button
        android:id="@+id/btn_data"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="60dp"
        android:text="Date picker dialog"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn_custom" />

    <Button
        android:id="@+id/btn_time"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="Time picker dialog"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn_data" />

    <Button
        android:id="@+id/btn_grogress"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:text="progress dialog"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/btn_time" />


</androidx.constraintlayout.widget.ConstraintLayout>

 

 

 

  • 기본 Dialog
binding.btnDialog.setOnClickListener {
            var builder = AlertDialog.Builder(this)
            builder.setTitle("기본 다이얼로그 타이틀")
            builder.setMessage("기본 다이얼로그 메세지")
            builder.setIcon(R.drawable.ic_android_black_24dp)

            val listener = object : DialogInterface.OnClickListener {
                override fun onClick(p0: DialogInterface?, p1: Int) {
                    when (p1) {
                        DialogInterface.BUTTON_POSITIVE ->
                            binding.tvTitle.text = "BUTTON_POSITIVE"
                        DialogInterface.BUTTON_NEGATIVE ->
                            binding.tvTitle.text = "BUTTON_NEGATIVE"
                        DialogInterface.BUTTON_NEUTRAL ->
                            binding.tvTitle.text = "BUTTON_NEUTRAL"
                    }
                }
            }
            builder.setPositiveButton("Positive",listener)
            builder.setNegativeButton("Negative",listener)
            builder.setNeutralButton("Neutral",listener)

            builder.show()
        }

 

 

  • Dialog에서 Positive 선택 시 상단 TextView 텍스트 변경

 

 

 

  • 커스텀 Dialog
    • 커스텀 Dialog는 내가 Dialog에 나타날 UI를 직접 만들어서 Dialog 창을 띄울 수 있다.
binding.btnCustom.setOnClickListener {
            val builder = AlertDialog.Builder(this)
            builder.setTitle("Add Contact")
            builder.setIcon(R.drawable.ic_android_black_24dp)

            val v1 = layoutInflater.inflate(R.layout.dialog, null)
            builder.setView(v1)

            val lister = DialogInterface.OnClickListener { p0, p1 ->
                val alert = p0 as AlertDialog
                val edit1: EditText? = alert.findViewById<EditText>(R.id.edit_InputName)
                val edit2: EditText? = alert.findViewById<EditText>(R.id.edit_InputNumber)
                edit2?.addTextChangedListener(PhoneNumberFormattingTextWatcher())


                binding.tvTitle.text = "이름 : ${edit1?.text}"
                binding.tvTitle.append(" / 번호 : ${edit2?.text}")
            }

            builder.setPositiveButton("확인",lister)
            builder.setNegativeButton("취소", null)

            builder.show()
        }

 

 

  • 날짜 및 시간 Dialog 
    • 각각 DatePickerDialog, TimePickerDialog를 사용하지만 Calendar 를 getInstance() 해서 사용하는 방법이 같다.
  • DatePickerDialog
// 날짜 Dialog
binding.btnData.setOnClickListener {
            val calendar = Calendar.getInstance()
            val year = calendar.get(Calendar.YEAR)
            val month = calendar.get(Calendar.MONTH)
            val day = calendar.get(Calendar.DAY_OF_MONTH)

            val listener = DatePickerDialog.OnDateSetListener { datePicker, i, i2, i3 ->
                binding.tvTitle.text = "$i 년 ${i2 + 1}월 $i3 일"
            }

            val picker = DatePickerDialog(this,listener,year,month,day)
            picker.show()
        }

 

 

  • TimePickerDialog
// 시간 Dialog
binding.btnTime.setOnClickListener {
            val calendar = Calendar.getInstance()
            val hour = calendar.get(Calendar.HOUR)
            val minute = calendar.get(Calendar.MINUTE)

            val listener = TimePickerDialog.OnTimeSetListener { timePicker, i, i2 ->
                binding.tvTitle.text = "$i 시 $i2 분"
            }

            val timePicker = TimePickerDialog(this,listener,hour,minute,true) // 17시 등 24시제
            timePicker.show()

        }

 

 

  • PrgressBar Dialog
    • progressbar.xml 을 생성해서 직접 progressbar UI를 생성해주고 해당 진행바를 Dialog에 띄워주는 것
  • progressbar.xml 의 palette에서 ProgressBar Item을 기본으로 생성해도 나쁘지 않은 progressbar가 생성된다.
<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">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:indeterminate="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
        
        
    ...
    
    
</androidx.constraintlayout.widget.ConstraintLayout>

 

 

  • progressDialog main.kt
binding.btnProgress.setOnClickListener {
            val builder = AlertDialog.Builder(this)
            builder.setTitle("프로그래스바")
            builder.setIcon(R.drawable.ic_android_black_24dp)

            val v1 = layoutInflater.inflate(R.layout.progressbar,null)
            builder.setView(v1)
            builder.show()
        }

 

 

 

 


-2- 알림 (Notification)

  • 알림은 알림음, 진동, 화면내의 별도의 이벤트(예시 : 카카오톡의 화면 상단에 알림창)등을 통해 사용자에게 앱과 관련한 정보를 보여주는 기능이다.
  • 이 알림들은 상단 상태바(Statusbar)에 표시되기도 하고, 없애고 싶은 앱 아이콘의 뱃지, 상태바를 내리면 보이는 화면을 통해 알림창에 표시되며 간단한 작업 또는 알림을 터치하여 해당 앱을 들어갈 수도 있다.

 

 

 

  • 알림을 생성하는 방법
  • Android 8.0 이상에서는 알림채널을 먼저 생성해야한다.
    • 알림채널은 알림을 그룹화하여 중요도를 설정하거나, 알림의 활성화 상태( ON / OFF ) 등을 변경할 수 있다.

 

 

  • 알림채널 생성
fun notification() {
        val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager

        val builder: NotificationCompat.Builder
        // 8.0 버전인 Oreo Code의 O 이상일 경우 알림채널 생성.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            val channelId = "one-channel"
            val channelName = "My Channel One"
            val channel = NotificationChannel(
                channelId,
                channelName,
                NotificationManager.IMPORTANCE_DEFAULT
            ).apply {
                description = "My Channel One Description"
                // 앱 아이콘 뱃지에 표시.
                setShowBadge(true) 
                val uri: Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION) // RingtonManager로 효과음 가져오기
                val audioAttributes = AudioAttributes
                	// 빌더 클래스의 인스턴스를 가져와 속성을 설정.
                    .Builder() 
                    
                     // 알림이 어떤식으로 사용되는지 설정. - sonification 타입은 버튼,이벤트 등으로 사용자가 직접 상호작용하는 타입
                    .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                    
                    // 알림(Notification) 소리의 크기를 Alarm 소리의 크기로 설정
                    .setUsage(AudioAttributes.USAGE_ALARM) 
                    .build() // 설정한 속성을기반으로 AudioAttributes 객체를 빌드하여 반환함.
                setSound(uri, audioAttributes) // uri로 설정한 알림음, audioAtrributes의 볼륨설정 적용.
                // 진동 설정
                enableVibration(true) 
            }
            manager.createNotificationChannel(channel)

            builder = NotificationCompat.Builder(this, channelId)
        } else {
            builder = NotificationCompat.Builder(this)
        }

 

 

 

  • 알림 내용 설정 및 기능 설정
// 이미지 리소스를 알림창에 들어갈 사이즈로 만듦
        val bitMap = BitmapFactory.decodeResource(resources, R.drawable.cavempt)

        // 특정 화면으로 넘어가게 하기 위해 Intent 설정.
        val intent = Intent(this, SecondActivity::class.java)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        val pendingIntent = PendingIntent.getActivity(
            this,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // 알림에 보여질 내용 설정.
        builder.run {
            setSmallIcon(R.drawable.ic_android_black_24dp)
            setWhen(System.currentTimeMillis())
            setContentTitle("새로움 알림입니다")
            setContentText("알림 내용입니다")
            setStyle(
                NotificationCompat.BigTextStyle()
                    .bigText(
                        "긴 텍스트 내용 아무거나 적는다. 오늘 점심은 돈까스를 먹고 점심 먹고 난 뒤로 아메리카노 500ml 2잔 마셨다 " +
                                "긴 텍스트 내용 아무거나 적는다. 오늘 점심은 돈까스를 먹고 점심 먹고 난 뒤로 아메리카노 500ml 2잔 마셨다"
                    )
            )
            setLargeIcon(bitMap)
            addAction(R.mipmap.ic_launcher, "Action", pendingIntent)
        }
        manager.notify(11, builder.build()) // 위에서 작성한 코드를 알림에 적용시키고, 알림 띄우기
    }
}

 

 

  • 알림창 화면

상태바 내부 알림
상단 상태바 알림

 

 

 

 


-3- Intent FLAG

  • Intent 의 동작을 제어하기 위해 FLAG가 있다.
  • Intent flag를 왜 사용하는가?
    • 안드로이드에서 Activity를 호출하다보면 발생하는 Activity의 중복문제나 흐름을 제어해주고 싶을 때 사용
  • Task
    • Task는 Stack 구조로 되어 있으며 앱에서 Activity들을 보관하고 관리한다.

https://seosh817.tistory.com/14

  • ACTIVITIY_NEW_TASK
    • 해당 플래그는 시작되는 액티비티에 대한 새 작업(백 스택)을 생성하는 데 사용된다. 동일한 동일한 앱을 가진 작업이 존재하는 경우 대상 액티비티가 해당 작업 내에서 포그라운드로 이동된다. 해당 작업이 없으면 액티비티에 대한 새 작업이 생성된다.
  • ACTIVITY_CLEAR_TASK
    • ACTIVITY_NEW_TASK와 함께 사용되며 새 액티비티가 작업의 루트에 있어야하고, 스택에서 그 위에 있는 모든 액티비티가 제거되어야 함을 나타낸다. 이는 백 스택에서 이전 화면을 모두 제거하면서 사용자를 앱의 초기 화면으로 이동시키는 로그아웃 기능을 구현하는데 사용 등. 
  • ACTIVITY_CLEAR_TOP
    • 호출하는 액티비티가 스택에 존재할 경우에, 해당 액티비티를 최상위로 올리면서, 그 위에 존재하던 액티비티 들을 모두 삭제하는 FLAG
  • ACTIVITY_SINGLE_TOP
    • 호출되는 액티비티가 최상위에 존재할 경우, 해당 액티비티를 다시 생성하지 않고, 존재하던 액티비티를 다시 사용하는 FLAG

 

 

 

 


 

[오늘 복습한 내용]

정신없이 강의 듣고 실습하느라 따로 복습으로 생각되는 행위는 하지 못함

 


[오류,에러 등등]

1. EditText 자동 하이픈 ' - ' 이 안들어감

  • addTextChangedListener{PhoneNumberFormattingTextWatcher()} 를 해도 적용이 안되는데 일단 과제들이랑 학습해야하는 부분이 많이 남아서 메모해두고 추후에 수정할 것.
binding.btnCustom.setOnClickListener {
            val builder = AlertDialog.Builder(this)
            builder.setTitle("Add Contact")
            builder.setIcon(R.drawable.ic_android_black_24dp)

            val v1 = layoutInflater.inflate(R.layout.dialog, null)
            builder.setView(v1)

            val lister = DialogInterface.OnClickListener { p0, p1 ->
                val alert = p0 as AlertDialog
                val edit1: EditText? = alert.findViewById<EditText>(R.id.edit_InputName)
                val edit2: EditText? = alert.findViewById<EditText>(R.id.edit_InputNumber)
                edit2?.addTextChangedListener(PhoneNumberFormattingTextWatcher())


                binding.tvTitle.text = "이름 : ${edit1?.text}"
                binding.tvTitle.append(" / 번호 : ${edit2?.text}")
            }

            builder.setPositiveButton("확인",lister)
            builder.setNegativeButton("취소", null)

            builder.show()
        }

 

 

 


[느낀 점]

1. 어렵다.

 

2. 코드는 책 읽듯이 좌측 상단부터 천천히 읽어나가자. 요즘 긴 코드를 만나면 읽기 어려운 느낌인데, 윗 줄 부터 천천히 읽어 나가면 금방 이해할 수 있을 것 같다.

 

3. 학습 속도가 너무 느리다

 

 


[Reference]

 

 

// 권한요청

https://eunoia3jy.tistory.com/145

// Notification

https://notepad96.tistory.com/205

// FLAG

https://seosh817.tistory.com/14

// BitmapFactory Android.developer

https://developer.android.com/topic/performance/graphics/load-bitmap?hl=ko 

// AudioAttributes Android.developer

https://developer.android.com/reference/android/media/AudioAttributes.html#CONTENT_TYPE_SONIFICATION

// RingtoneManager

https://curryyou.tistory.com/380

// AudioAttributes

https://m.blog.naver.com/shwoghk14/222877186177