
강사님은 fun initToolbar() 함수로 따로 정의하심
툴바의 뒤로가기 버튼을 클릭할때는 setResult에 RESULT_CANCELED를 담고 finish()함
// 뒤로가기
setNavigationIcon(R.drawable.arrow_back_24px)
setNavigationOnClickListener {
setResult(RESULT_CANCELED)
finish()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.apply {
// textView의 컨텍스트 메뉴
// Context Menu가 등록된 View를 길게 누르면 호출되는 메서드
// menu : 메뉴를 구성하기 위해 필요한 객체
// v : 사용자가 길게 누른 View의 주소값
// menuInfo : 메뉴에 대한 부가 정보를 가지고 있는 객체. RecyclerView에서 사용할 때 사용자가 길게 누른 항목의 순서값을 가져올 수 있다.
textView.setOnCreateContextMenuListener { menu, view, menuInfo ->
// 메뉴의 헤더
menu?.setHeaderTitle("TextView의 메뉴입니다")
// res/menu/textview_menu.xml 을 통해 메뉴를 구성해준다.
menuInflater.inflate(R.menu.textview_menu, menu)
// 각 메뉴 아이템을 추출하여 리스너를 설정해준다.
menu?.findItem(R.id.textview_menu_item1)?.setOnMenuItemClickListener {
textView.text = "텍스트뷰의 메뉴 항목1 선택"
true
}
menu?.findItem(R.id.textview_menu_item2)?.setOnMenuItemClickListener {
textView.text = "텍스트뷰의 메뉴 항목2 선택"
true
}
}
}
}


// Context Menu의 항목을 눌렀을 때 호출되는 메서드
// 주의할점 : 이 메서드 내부에서는 어떤 뷰의 메뉴인지를 구분할 수가 없다.
// 메뉴 아이템의 아이디를 정해줄 때 모두 다르게 정해주고
// 이 메서드 내부에서는 꼭 주석으로 누구의 메뉴인지를 명시해준다
override fun onContextItemSelected(item: MenuItem): Boolean {
// 사용자가 선택한 메뉴의 아이디로 분기한다.
when(item.itemId){
// TextView의 메뉴
R.id.textview_menu_item1 -> activityMainBinding.textView.text = "TextView의 메뉴 1을 선택했습니다"
R.id.textview_menu_item2 -> activityMainBinding.textView.text = "TextView의 메뉴 2를 선택했습니다"
// RecyclerView의 메뉴
R.id.recyclerview_menu_item1 -> {
// AdapterContextMenuInfo 객체를 추출한다.
val menuInfo = item.menuInfo as AdapterContextMenuInfo
activityMainBinding.textView.text = "${menuInfo.position}번째 항목의 메뉴 항목1"
}
R.id.recyclerview_menu_item2 -> {
// AdapterContextMenuInfo 객체를 추출한다.
val menuInfo = item.menuInfo as AdapterContextMenuInfo
activityMainBinding.textView.text = "${menuInfo.position}번째 항목의 메뉴 항목2"
}
}
return super.onContextItemSelected(item)
}
리사이클러뷰의 항목에 대한 정보를 가져올 때 onCreateContextMenu의 menuInfo 매개변수를 사용한다
// RecyclerView의 어댑터
inner class RecyclerViewAdapter : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolderClass>(){
// ViewHolder
inner class ViewHolderClass(rowBinding: RowBinding) : RecyclerView.ViewHolder(rowBinding.root) {
val rowBinding:RowBinding
init {
this.rowBinding = rowBinding
rowBinding.root.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
// 항목에 컨텍스트 메뉴를 설정해준다.
rowBinding.root.setOnCreateContextMenuListener { menu, v, menuInfo ->
menu?.setHeaderTitle("${adapterPosition}번째 항목의 메뉴")
menuInflater.inflate(R.menu.recyclerview_menu, menu)
// RecyclerView 항목에 컨텍스트 메뉴를 설정해준다.
menu?.findItem(R.id.recyclerview_menu_item1)?.setOnMenuItemClickListener {
activityMainBinding.textView.text = "${adapterPosition}번째 항목의 메뉴1"
true
}
menu?.findItem(R.id.recyclerview_menu_item2)?.setOnMenuItemClickListener {
activityMainBinding.textView.text = "${adapterPosition}번째 항목의 메뉴2"
true
}
}
}
}
}

// Context Menu : View를 길게 누르면 나타나는 메뉴
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.apply {
recyclerView.apply {
adapter = RecyclerViewAdapter()
layoutManager = LinearLayoutManager(this@MainActivity)
}
// textView의 컨텍스트 메뉴
// Context Menu가 등록된 View를 길게 누르면 호출되는 메서드
// menu : 메뉴를 구성하기 위해 필요한 객체
// v : 사용자가 길게 누른 View의 주소값
// menuInfo : 메뉴에 대한 부가 정보를 가지고 있는 객체. RecyclerView에서 사용할 때 사용자가 길게 누른 항목의 순서값을 가져올 수 있다.
textView.setOnCreateContextMenuListener { menu, view, menuInfo ->
// 메뉴의 헤더
menu?.setHeaderTitle("TextView의 메뉴입니다")
// res/menu/textview_menu.xml 을 통해 메뉴를 구성해준다.
menuInflater.inflate(R.menu.textview_menu, menu)
// 각 메뉴 아이템을 추출하여 리스너를 설정해준다.
menu?.findItem(R.id.textview_menu_item1)?.setOnMenuItemClickListener {
textView.text = "텍스트뷰의 메뉴 항목1 선택"
true
}
menu?.findItem(R.id.textview_menu_item2)?.setOnMenuItemClickListener {
textView.text = "텍스트뷰의 메뉴 항목2 선택"
true
}
}
}
}
// RecyclerView의 어댑터
inner class RecyclerViewAdapter : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolderClass>(){
// ViewHolder
inner class ViewHolderClass(rowBinding: RowBinding) : RecyclerView.ViewHolder(rowBinding.root) {
val rowBinding:RowBinding
init {
this.rowBinding = rowBinding
rowBinding.root.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
// 항목에 컨텍스트 메뉴를 설정해준다.
rowBinding.root.setOnCreateContextMenuListener { menu, v, menuInfo ->
menu?.setHeaderTitle("${adapterPosition}번째 항목의 메뉴")
menuInflater.inflate(R.menu.recyclerview_menu, menu)
// RecyclerView 항목에 컨텍스트 메뉴를 설정해준다.
menu?.findItem(R.id.recyclerview_menu_item1)?.setOnMenuItemClickListener {
activityMainBinding.textView.text = "${adapterPosition}번째 항목의 메뉴1"
true
}
menu?.findItem(R.id.recyclerview_menu_item2)?.setOnMenuItemClickListener {
activityMainBinding.textView.text = "${adapterPosition}번째 항목의 메뉴2"
true
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolderClass {
val rowBinding = RowBinding.inflate(layoutInflater)
val viewHolderClass = ViewHolderClass(rowBinding)
return viewHolderClass
}
override fun getItemCount(): Int {
return 20
}
override fun onBindViewHolder(holder: ViewHolderClass, position: Int) {
holder.rowBinding.textViewRow.text = "항목 $position"
}
}
}

개발자가 코드를 통해 원하는 View에 띄우는 메뉴
popup_menu.xml에서 팝업메뉴 만든 후
버튼을 클릭하면 텍스트뷰 밑에 팝업메뉴가 나타나게 코드 작성
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.apply {
// 팝업 메뉴
button.setOnClickListener {
// 팝업 메뉴를 생성한다.
// 두번째 매개 변수 : 메뉴를 띄울 View를 지정한다.
val popupMenu = PopupMenu(this@MainActivity, textView)
// 메뉴를 구성한다.
menuInflater.inflate(R.menu.popup_menu, popupMenu.menu)
// 메뉴의 항목을 눌렀을 때 동작할 리스너를 지정해준다.
popupMenu.setOnMenuItemClickListener {
// 메뉴 항목의id로 분기한다.
when(it.itemId){
R.id.popup_menu_item1 -> textView.text = "팝업 메뉴1을 선택했습니다"
R.id.popup_menu_item2 -> textView.text = "팝업 메뉴2을 선택했습니다"
}
true
}
// 메뉴를 보여준다.
popupMenu.show()
}
}
}



요새는 context,popup 대신 sheets를 사용한다고 함
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.apply {
buttonToast.setOnClickListener {
// Toast 객체를 생성한다.
// 두 번째 : 메시지
// 세 번째 : 표시할 시간
val t1 = Toast.makeText(this@MainActivity, "토스트 메시지 입니다", Toast.LENGTH_SHORT)
// 메시지를 보여준다.
t1.show()
}
}
}
}

class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.apply {
buttonSnackBar.setOnClickListener {
// SnackBar 객체를 생성한다.
// 마지막 매개변수 : 메시지가 보여질 시간
// LENGTH_SHORT : 적당히 짧은 시간
// LENGTH_LONG : 적당히 긴 시간
// LENGTH_INDEFINITE : 개발자가 코드로 없애거나 다른 스낵바 메시지가 뜰 때까지 보여준다.
// 보통 Action을 줄 경우 사용한다.
// val snackbar = Snackbar.make(it,"SnackBar", Snackbar.LENGTH_SHORT)
val snackbar = Snackbar.make(it,"SnackBar", Snackbar.LENGTH_INDEFINITE)
// 메시지 색상
snackbar.setTextColor(Color.RED)
// 배경색
snackbar.setBackgroundTint(Color.BLUE)
// 애니메이션 종류
// snackbar.animationMode = Snackbar.ANIMATION_MODE_FADE // 기본값임
snackbar.animationMode = Snackbar.ANIMATION_MODE_SLIDE
// Action설정
snackbar.setAction("Action"){
textView.text = "Action을 눌렀습니다"
}
// SnackBar를 보여준다.
snackbar.show()
}
}
}
}


버튼을 최대 세개까지 배치 가능
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.apply {
buttonBasicDialog.setOnClickListener {
val builder = MaterialAlertDialogBuilder(this@MainActivity).apply{
// 타이틀
setTitle("기본 다이얼로그")
// 메시지
setMessage("기본 다이얼로그 입니다")
// 중립 버튼
setNeutralButton("Neutral"){ dialogInterface: DialogInterface, i: Int ->
textView.text = "기본 다이얼로그 : Neutral"
}
// 긍정 버튼
setPositiveButton("Positive"){ dialogInterface: DialogInterface, i: Int ->
textView.text = "기본 다이얼로그 : Positive"
}
// 부정 버튼
setNegativeButton("Negative"){ dialogInterface: DialogInterface, i: Int ->
textView.text = "기본 다이얼로그 : Negative"
}
}
// 다이얼로그를 띄운다.
builder.show()
}
}
}
}

class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
activityMainBinding.apply {
buttonCustomDialog.setOnClickListener {
var builder = MaterialAlertDialogBuilder(this@MainActivity).apply {
setTitle("커스텀 다이얼로그")
// 뷰를 설정한다.
val customDialogBinding = CustomDialogBinding.inflate(layoutInflater)
setView(customDialogBinding.root)
setNegativeButton("취소",null)
setPositiveButton("확인"){ dialogInterface: DialogInterface, i: Int ->
textView.text = "입력1 : ${customDialogBinding.editTextDialog1.text}\n"
textView.append("입력2 : ${customDialogBinding.editTextDialog2.text}")
}
}
builder.show()
}
}
}
}



AndroidManifest.xml에 권한 추가
<?xml version="1.0" encoding="utf-8"?>
...
...
<!-- 알림 메시지를 사용하기 위한 권한 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
</manifest>
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var activityMainBinding: ActivityMainBinding
// Notification 메시지 사용을 위한 권한
val permissionList = arrayOf(
Manifest.permission.POST_NOTIFICATIONS
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(activityMainBinding.root)
// 알림 메시지 사용을 위한 권한 확인
requestPermissions(permissionList, 0)
// Notification Message 사용을 위한 채널을 등록
addNotificationChannel("c1", "메시지 채널 1")
addNotificationChannel("c2", "메시지 채널 2")
activityMainBinding.apply {
buttonNotification1.setOnClickListener {
// NotificationBuilder를 가져온다.
// 채널 id를 지정한다.
val builder = getNotificationBuilder("c1")
// 작은 아이콘
builder.setSmallIcon(android.R.drawable.ic_menu_add)
// 큰 이미지
val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
builder.setLargeIcon(bitmap)
// 숫자 설정
builder.setNumber(100)
// 큰 이미지와 숫자 설정은 OS버전에따라 나올수도 있고 안나올수도 있다
// 타이틀 설정
builder.setContentTitle("content title 1")
// 내용
builder.setContentText("content text 1")
// 메시지 객체를 생성한다.
val notification = builder.build()
// 알림 메시지를 관리하는 객체를 추출
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// 메시지를 표시한다.
// 첫 번째 매개변수 : 메시지 번호
// 메시지 번호를 통해 표시할 메시지를 지정할 수 있다.
// 지정해준 메시지 번호의 메시지가 이미 보여지고 있는 상태라면 그 메시지를 없애고 새롭게 보여준다.
// 하나의 메시지를 갱신할 때 같은 정수를 넣어주고 새로운 메시지를 보여줄 때는 다른 정수를 넣어준다.
notificationManager.notify(10, notification)
}
buttonNotification2.setOnClickListener {
val builder = getNotificationBuilder("c2")
builder.setSmallIcon(android.R.drawable.ic_menu_add)
val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
builder.setLargeIcon(bitmap)
builder.setNumber(200)
builder.setContentTitle("content title 2")
builder.setContentText("content text 2")
val notification = builder.build()
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(20, notification)
}
}
}
// Notification Channel 등록
// 첫 번째 매개 변수 : 코드에서 채널을 관리하기 위한 이름
// 두 번째 매개 변수 : 사용자에게 보여줄 채널의 이름
fun addNotificationChannel(id:String, name:String){
// 안드로이드 8.0 이상일 때만 동작하게 한다.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
// 알림 메시지를 관리하는 객체를 가져온다.
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// 해당 채널이 등록되어 있는지 확인한다.
// 채널이 등록되어 있지 않으면 null을 반환한다.
val channel = notificationManager.getNotificationChannel(id)
// 등록된 채널이 없다면
if(channel == null){
// 채널 객체를 생성한다.
val newChannel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH)
// 진동을 사용할 것인가.
newChannel.enableVibration(true)
// 채널을 등록한다.
notificationManager.createNotificationChannel(newChannel)
}
}
}
// Notification 메시지를 생성하기 위한 객체를 반환하는 메서드
fun getNotificationBuilder(id:String) : NotificationCompat.Builder{
// 안드로이드 8.0 이상이면 마지막 매개변수에 채널 id를 설정해줘야 한다.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
val builder = NotificationCompat.Builder(this, id)
return builder
}else{
val builder = NotificationCompat.Builder(this)
return builder
}
}
}







학생 정보 입력 액티비티에서 입력완료 후 메인액티비티로 넘어가기 전에 스낵바를 사용했더니 입력액티비티가 종료되면서 스낵바도 같이 화면이 넘어가버리는 것 같다.
그래서 메인액티비티에 계약부분에서 입력값을 받아오고 리사이클러뷰 새로고침할때 스낵바를 띄우는 방법으로 수정해보았다.
※ 출처 : 멋쟁이사자 앱스쿨 2기, 소프트캠퍼스