액티비티 위에서 동작하는 모듈화된 사용자 인터페이스이다.
-> 액티비티와 분리되어 독립적으로는 동작할 수 없다.
장점 : 여러개의 프래그먼트를 하나의 액티비티에 조합하여 창이 여러개인 UI를 구축할 수 있다, 하나의 프래그먼트를 여러 액티비티에서 재사용 할 수 있다.
onCreate() 메서드가 완료된 후 호출됩니다.fragment3Btn.setOnClickListener {
setFragment(ThirdFragment())
}
어떠한 버튼을 클릭시 setFragment함수를 사용해서 해당 프래그먼트로 이동을 시도한다.
private fun setFragment(frag : Fragment) {
supportFragmentManager.commit {
replace(R.id.frameLayout, frag)
setReorderingAllowed(true)
addToBackStack("") // 뒤로가기 버튼 누르면 이전 프래그먼트로 이동할 수 있음. -> 빽 스택에 추가
}
}
이렇게 하면 해당 프래그먼트로 이동이 된다. 여기서 replace는 어디에, 어떤 프래그먼트를 설정할지 보여주는곳이다. id가 frameLayout인 레이아웃에 fragment를 띄울거라는 말이다.
setReorderingAllowd(true)는 화면이 전환될때 최적화? 된다는 느낌이다.
addToBackStack은 여러개의 프래그먼트를 왔다갔다 하면 stack에 쌓이게 해주는 기능으로, 뒤로가기버튼을 누르면 이전에 방분했던 프래그먼트로 이동한다. 해당 부분을 지워주면 stack에 쌓이지 않게된다.
메인 액티비티에서 버튼을 클릭시 string값을 fragment로 전달하는 상황이다.
binding.run {
fragment1Btn.setOnClickListener {
//[1] Activity -> Fragment
val dataToSend = "Hello First Fragment! \n From Activity"
val fragment = FirstFragment.newInstance(dataToSend)
setFragment(fragment)
}
}
그후 이 값을 받는 Fragment에서는 dataToSend라는 값을 newInstance를 생성해서 같이 보내준다.
private const val ARG_PARAM1 = "param1" // 전역변수 선언
..
private var param1: String? = null // 받아오는값이 null일수도 있으니 ?붙여서.
..
companion object {
@JvmStatic
fun newInstance(param1: String) =
//[1] Activity -> Fragment
FirstFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}//Fragment로 넘어오면 가장먼저 companion object부분이 실행되는데,
//arguments안에 bundle객체로 받아온 strting값을 가져와준다.
..
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
}
} // 생성될때 arguments안에있는 값을 받아온다.
..
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//[1] Activity -> Fragment
binding.tvFrag1Text.text = param1
} // fragment에 선언된 textView를 받아온 값으로 업데이트 해준다.
대부분은 fragment를 처음 생성할때 자동으로 오버라이딩 되어있었다. 해당 부분을 살짝만 수정해주면 된다.
1번프래그먼트에 버튼을 하나 만들고, 2번 프래그먼트로 string값과 함께 넘겨보겠다. 먼저 1번 프래그먼트에 버튼하나를 생성해준다. 다음으로 onViewCreated() 안에서 뷰바인딩을 이용해서 해당 버튼이 클릭됐을시의 이벤트를 처리해준다.
binding.btnGofrag2.setOnClickListener {
val dataToSend = "Hello Fragment2! \n From Fragment1"
val fragment2 = SecondFragment.newInstance(dataToSend)
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.frameLayout, fragment2)
.addToBackStack(null)
.commit()
}
액티비티에서 프래그먼트로 값을 보내는것과 마찬가지로 newInstance를 생성해서 값과 함께 보내고, 새로운 프래그먼트를 화면에 나타나게 코드를 작성해준다.
private const val ARG_PARAM1 = "param1"
..
private var param1: String? = null
..
companion object {
@JvmStatic
fun newInstance(param1: String) =
//[2] Fragment -> Fragment
SecondFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
..
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
param1 = it.getString(ARG_PARAM1)
}
}
..
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//[2] Fragment -> Fragment
binding.tvFrag2Text.text = param1
}
마찬가지로 값을 받아오면 companion object에서 newInstance를 받아오고, 해당 string값을 같은 방식으로 받아와서 화면 textView에 띄워준다.
2번 프래그먼트에 버튼을 하나 만들어서 해당 버튼값을 액티비티에 가져와서 Toast메세지로 띄워보려한다.
Fragment에서 Activity로 값을 보내줄때는 다른상황과는다르게 interface를 이용해야한다.
먼저 FragmentDataLister인터페이스를 하나 만들어준다.
interface FragmentDataListener {
fun onDataReceived(data: String)
}
그 후에 해당 인터페이스를 메인 액티비티에서 상속받아준다.
class MainActivity : AppCompatActivity(), FragmentDataListener {}
준비는 끝났다. 2번 액티비티에서 listener변수 선언뒤에, onViewCreated()부분에서 버튼이 클릭됐을때의 이벤트를 실행해준다.
//[3] SecondFragment -> Activity
private var listener: FragmentDataListener? = null
..
binding.btnSendToActivity.setOnClickListener {
val dataToSend = "Hello From SecondFragment!"
listener?.onDataReceived(dataToSend)
}
다음으로 프래그먼트가 액티비티에 연결될때 호출되는 onAttach()를 오버라이딩 해주고,
override fun onAttach(context: Context) {
super.onAttach(context)
//[3] SecondFragment -> Activity
if(context is FragmentDataListener){
listener = context
} else{
throw RuntimeException("$context must implement FragmentDataListener")
}
}
이 값을 받아주는 메인액티비티에서 데이터를 받을때 호출되는 onDataReceived()를 오버라이딩 해준다.
//[3] SecondFragment -> Activity
override fun onDataReceived(data: String) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
}
그리고 해당 값을 Toast메세지로 띄워줬다.
액티비티, 프래그먼트가 서로 데이터를 주고 받는 부분은 좀 어려웠다.. 많이 써보고, 다양한값을 보내보면서 연습을 해야할거같다.
작은 대화상자? 대화창? 이라고 생각하면 될거같다.
가장 기본적인 모양의 Dialog이다.
binding.btn1Alert.setOnClickListener {
var builder = AlertDialog.Builder(this)
builder.setTitle("기본 다이얼로그 타이틀")
builder.setMessage("기본 다이얼로그 메세지")
builder.setIcon(R.mipmap.ic_launcher)
// 버튼 클릭시에 무슨 작업을 할 것인가!
val listener = object : DialogInterface.OnClickListener {
override fun onClick(p0: DialogInterface?, p1: Int) {
when (p1) {
DialogInterface.BUTTON_POSITIVE ->
binding.tvTitle.text = "BUTTON_POSITIVE"
DialogInterface.BUTTON_NEUTRAL ->
binding.tvTitle.text = "BUTTON_NEUTRAL"
DialogInterface.BUTTON_NEGATIVE ->
binding.tvTitle.text = "BUTTON_NEGATIVE"
}
}
}
builder.setPositiveButton("Positive", listener)
builder.setNegativeButton("Negative", listener)
// builder.setNeutralButton("Neutral", listener)
builder.show()
}
위 코드에서는 확인버튼/취소버튼이 눌렸을때 메인화면에 있던 textView의 text를 업데이트 시켜줬다.

내가 원하는 모양으로 만들수 있는 custom Dialog이다.
기본과는 다르게 다이얼로그를 커스텀으로 원하는 위젯을 넣어줘야 해서 res/layout아래에 새로운 dialog.xml파일을 하나 만들어준다.(원하는 형식으로)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:orientation="vertical">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="Name" />
<EditText
android:id="@+id/editText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="AGE" />
</LinearLayout>
</LinearLayout>
그 후에 사용법은
binding.btn2Custom.setOnClickListener {
val builder = AlertDialog.Builder(this)
builder.setTitle("커스텀 다이얼로그")
builder.setIcon(R.mipmap.ic_launcher)
val v1 = layoutInflater.inflate(R.layout.dialog, null)
builder.setView(v1)
// p0에 해당 AlertDialog가 들어온다. findViewById를 통해 view를 가져와서 사용
val listener = DialogInterface.OnClickListener { p0, p1 ->
val alert = p0 as AlertDialog
val edit1: EditText? = alert.findViewById<EditText>(R.id.editText)
val edit2: EditText? = alert.findViewById<EditText>(R.id.editText2)
binding.tvTitle.text = "이름 : ${edit1?.text}"
binding.tvTitle.append(" / 나이 : ${edit2?.text}")
}
builder.setPositiveButton("확인", listener)
builder.setNegativeButton("취소", null)
builder.show()
}
위 코드에서는 입력받은 이름/나이를 붙여서 메인화면에 있던 textView의 text를 업데이트 시켜줬다.
날짜를 고를 수 있는 Dialog이다.
binding.btn3Date.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 ->
// i년 i2월 i3일
binding.tvTitle.text = "${i}년 ${i2 + 1}월 ${i3}일"
}
var picker = DatePickerDialog(this, listener, year, month, day)
picker.show()
}
여기서는 메인화면에 있는 textView에 선택된 년,월,일을 가져와 업데이트 시켜주었다.

Dialog에서 시간을 고를수 있다.
binding.btn4Time.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 picker = TimePickerDialog(this, listener, hour, minute, false) // true하면 24시간 제
picker.show()
}
여기서는 메인화면에 있던 textView에다가 선택된 시간을 업데이트 해주는 형식으로 사용했다.

커스텀 다이얼로그와 사용법은 비슷하다. 먼저 프로그래스바를 선언한 xml파일을 만들어줘야한다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="match_parent"
android:layout_height="76dp"
android:layout_gravity="center"
android:layout_marginTop="48dp"
android:indeterminate="false"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="loading..."
android:textSize="16sp"
android:layout_marginTop="16dp"/>
</LinearLayout>
그 뒤에 사용법은
binding.btn5Progress.setOnClickListener {
val builder = AlertDialog.Builder(this)
builder.setTitle("프로그래스바")
builder.setIcon(R.mipmap.ic_launcher)
val v1 = layoutInflater.inflate(R.layout.progressbar, null)
builder.setView(v1)
builder.show()
}
이렇게 해주면 버튼 클릭시 아래와 같이 프로그래스바가 나타나게 된다. 흔히보던 로딩? 화면인거같다.
함수를 만들어주고, 예를 들어 버튼 클릭시 알림이 울리게 하고싶다 -> 해당 부분에서 함수를 호출해주면 된다.
fun notification() {
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val builder: NotificationCompat.Builder
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 26 버전 이상
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)
//알림음 소리. 다른소리를 원하면 다른 mp3파일을 넣어서 실행시키면 그 소리가 알림소리로 남.
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
setSound(uri, audioAttributes)
enableVibration(true)
}
// 채널을 NotificationManager에 등록
manager.createNotificationChannel(channel)
// 채널을 이용하여 builder 생성
builder = NotificationCompat.Builder(this, channelId)
} else {
// 26 버전 이하, 사용권장X
builder = NotificationCompat.Builder(this)
}
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.flower)
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.mipmap.ic_launcher)
setWhen(System.currentTimeMillis())
setContentTitle("새로운 알림입니다.")
setContentText("알림이 잘 보이시나요.")
//더보기? 를 클릭하면 작았던 알림창이 커지며 BigText가 다 보이게된다.
// setStyle(
// NotificationCompat.BigTextStyle()
// .bigText(
// "이것은 긴텍스트 샘플입니다. 아주 긴 텍스트를 쓸때는 여기다 하면 됩니다.이것은 긴텍스트 샘플입니다. " +
// "아주 긴 텍스트를 쓸때는 여기다 하면 됩니다.이것은 긴텍스트 샘플입니다. 아주 긴 텍스트를 쓸때는 여기다 하면 됩니다."
// )
// )
setLargeIcon(bitmap)
//사진을 메인으로 크게 보여줌
setStyle(NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.bigLargeIcon(null)) // hide largeIcon while expanding
addAction(R.mipmap.ic_launcher, "Action", pendingIntent)
}
manager.notify(11, builder.build())
}
부가 설명을 해주자면 아래에 setStyle부분은 두가지 종류가 있다.

위 함수를 잘 사용해주면 필요할때 코드만 가져가서 쓸 수 있을거같다.