val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// 여기는 잘 모르겠지만 builder 생성하는 코드로 보인다.
val builder: NotificationCompat.Builder
// 여기서부터 채널 생성
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 안드로이드 8.0 버전 이상인지 검사
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_NTIFICATION) // 알림이 오면 소리가 나는데 그 소리를 기본 소리로 설정
// 그 소리에 대한 자세한 설정. 자세하게는 모르겠다.. ㅋㅋㅋ
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
// 아무튼 그렇게 만든 소리를 최종적으로 알림의 소리로 설정한다.
setSound(uri, audioAttributes)
enableVibration(true) // 진동 발생 설정. 지금은 true
}
// 체널 등록 및 builder 설정 부분인 것 같다.
manager.createNotificationChannel(channel)
builder = NotificationCompat.Builder(this, channelId)
} else { // 8.0 버전 이하이면 여기를 실행
builder = NotificationCompat.Builder(this)
}
// 그림 및 큰 사진에 대한 설정
val bitmap = BitmapFactory.decodeResource(resource, R.drawable.이미지 이름) // 사진을 넣겠다고 하면 이 사진을 넣는다.
val intent = Intent(this, 액티비티이름::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.이미지이름)
setWhen(System.currentTImeMillis()) // 알림 시간을 현재 시간으로 설정
setContentTitle(타이틀)
setContentText(내용)
// 긴 텍스트 쓸 때 이 부분 쓴다.
setStyle(NotificationCompat.BigTextStyle()
.bigText("이것은 긴텍스트 샘플입니다. 아주 긴 텍스트를 쓸때는 여기다 하면 됩니다.이것은 긴텍스트 샘플입니다. 아주 긴 텍스트를 쓸때는 여기다 하면 됩니다.이것은 긴텍스트 샘플입니다. 아주 긴 텍스트를 쓸때는 여기다 하면 됩니다."))
setLargeIcon(bitmap)
// setStyle(NotificationCompat.BigPictureStyle()
// .bigPicture(bitmap)
// .bigLargeIcon(null))
addAction(R.mipmap.ic_launcher, "Action", pendingIntent) // 여기도 아직 공부가 필요하다.
}
// 알림을 최종적으로 실행하여 보여줌
manager.notify(11, builder.build())
}
자! 전체 코드는 이렇다! 되게 복잡해 보이지만 전체적인 흐름만 파악하면 된다.
package com.example.fragmentexample
import android.os.Bundle
import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.fragment.app.replace
import com.example.fragmentexample.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity(), FragmentDataListener {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
binding.run {
fragment1Btn.setOnClickListener { // 1번 버튼 누르면
val dataToSend = "텍스트" // 이 string 타입 문자열을
val fragment = FirstFragment.newInstance(dataToSend) // FirstFragment의 newInstance에 넣는다.
setFragment(fragment) // 그리고 FirstFragment를 화면에 보인다.
}
// 여기는 1번 버튼 눌렀을 때와 버튼만 다르고 동작은 똑같으니 생략
fragment2Btn.setOnClickListener {
val dataToSend = "텍스트2"
val fragment = SecondFragment.newInstance(dataToSend)
setFragment(fragment)
}
}
// 기본적으로 시작할 때 보일 Fragment는 FirstFragment
set Fragment(FirstFragment())
}
// 이건 지난번에 했던 Fragment 화면에 보여주는 코드
private fun setFragment(frag: Fragment) {
supportFragmentManager.commit {
repalce(R.id.frameLayout, frag) // 이제 알았다. 이건 frameLayout 안에 Fragment 넣겠다는 뜻이었다... ㅋㅋㅋ
setReorderingAllowed(true)
addToBackStack("")
}
}
// interface를 실행하기 위한 코드
// Fragment에서 액티비티로 데이터 전달받아 토스트 메세지를 띄운다.
// 그게 맨 위에서 FragmentDataListener를 상속받은 이유이다.
override fun onDataReceived(data: String) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
}
package com.example.fragmentexample
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.fragmentexample.databinding.FragmentFirstBinding
private const val ARG_PARAM1 = "param1" // 여기는 익숙한 부분이다. 마치 인텐트 받아오듯이 상수 선언 파트
clas FirstFragment : Fragment() { // Fragment 상속 받기.. 여기 진짜 중요하다!
private var param1: String? = null // 받아온 값 넣을 변수 선언. 클래스 밖에도 선언해 봤는데 결과는 정상적으로 나옴.
private val binding by lazy { FragmentFirstbinding.inflate(layoutInflater) } // 이건 뭐.. 익숙하니 생략
// 여기는 받아온 값을 param1이라는 변수에 넣는 부분
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
}
}
// 여긴 여전히 뭔지 모르겠다.
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 최종적으로 Fragment의 텍스트에 받아온 값을 넣는다.
binding.tvFrag1Text.text = param1
// Fragment -> Fragment
// 이 부분은 뭐.. 똑같다... 설명 생략
binding.btnGoFrag2.setOnClickListener {
val dataToSend = "텍스트3"
val fragment2 = SecondFragment.newInstance(dataToSend)
// 여기는 데이터를 전달함과 동시에 fragment2를 화면에 보여달란 의미이다.
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, fragment2)
.addToBackStack(null)
.commit()
}
}
// MainActivity에서 string을 받아와서 아까 맨 위에 선언한 변수에 넣는다.
// 위에 코드 보면 알다시피 클래스 및 다른 함수에서 써야 되니 전역변수로 선언한 것이다.
// param1도 마찬가지.
companion object {
@JvmStatic
fun newInstance(param1: String) = FirstFragment().apply {
argments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
}
package com.example.fragmentexample
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.fragmentexample.databinding.FragmentSecondBinding
// 이건 뭐... 설명 생략
private const val ARG_PARAM1 = "param1"
class SecondFragment : Fragment() { // 역시 Fragment를 상속받는다.
private var listener: FragmentDataListener? = null // interface 관련 변수
private var param1: String? = null // 이것은 first와 똑같다.
// view binding 관련 변수
private var _binding: FragmentSecondBinding? = null
private val binding get() = _binding!!
// SecondFragment와 MainActivity 연결해서 Fragment에서 Activity로 데이터 전달
override fun onAttach(context: Context) {
super.onAttch(context)
if (context is FragmentDataListener) {
// MainActivity에서 context가 들어온다.
// 그러면서 FragmentDataListener 구현 여부를 물어본다.
// True이면 listener에 context 대입
listener = context
} else {
throw RuntimeException("$context must implement fragmentDataListener")
}
}
// 여기는 first에서 했던 Activity -> Fragment의 SecondFragment 버전
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstance: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvFrag2Text.text = param1
// Fragment -> Activity
binding.btnSendActivity.setOnClickListener {
val dataToSend = "텍스트4"
// MainActivity의 context가 listener로 들어갔으니 listener에 보낼 데이터 싣는다.
// interface를 호출해서 그걸 이용한다. 그럼 거기에 데이터가 들어간다.
// 근데 그 interface를 MainActivity가 상속받고 있으므로 이 데이터는 MainActivity로 넘어간다.
listener?.onDateReceived(dataToSend)
}
}
// 여기도 설명 생략
companion object {
@JvmStatic
fun newInstance(param1 : String) = SecondFragment().apply {
arguments = Bundle().apply {
putString(ARGPARAM1, param1)
}
}
}
override fun onDestoryView() {
super.onDestroyView()
_binding = null
listener = null
}
}
interface
코드interface FragmentDataListener {
// interface 안의 onDataaReceived에 Fragment에서 Data를 넣으면 그걸 MainActivity가 상속 받아서 토스트 메세지를 띄운다.
fun onataReceived(data: String)
}