안드로이드 앱 개발
(8) 바디와이짐 커뮤니티 앱 개발 - 식단 운동일지 화면
vitamin3000
2024. 9. 20. 15:47
지난 채팅화면에 이어서 식단 운동일지 구성이다.
구현하고자 하는 기능
1. 각 날짜에 저장되는 메모 기능
2. '관장님과 메모 공유' 버튼 클릭시 '관장님'의 닉네임을 가지고 있는 사용자가 해당 사용자의 메모를 확인할 수 있고
그 메모에 대한 코멘트를 작성할 수 있음
이는 다른 사용자는 확인할 수 없다.
3. 메모를 작성한 날짜와 작성하지 않은 날짜 구분
위 기능을 구현한 코드
CalendarActivity.kt
package com.example.bodygym
import android.graphics.Color
import android.os.Build.VERSION_CODES.R
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.CalendarView
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
class CalendarActivity : AppCompatActivity() {
private lateinit var memoAdapter: MemoAdapter
private val memoList = ArrayList<Memo>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.calendar_detail)
val calendarView: CalendarView = findViewById(R.id.calendarView)
val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
val closeButton: Button = findViewById(R.id.close_button)
memoAdapter = MemoAdapter(memoList)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = memoAdapter
calendarView.setOnDateChangeListener { view, year, month, dayOfMonth ->
val date = "$year-${month + 1}-$dayOfMonth"
FirebaseDatabase.getInstance().getReference("Memos/$date")
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
memoList.clear()
val memos = snapshot.children.iterator()
while (memos.hasNext()) {
val memoSnapshot = memos.next()
val memo = memoSnapshot.child("memo").getValue(String::class.java)
val userName = memoSnapshot.child("userName").getValue(String::class.java)
val shared = memoSnapshot.child("shared").getValue(Boolean::class.java)
val comment = memoSnapshot.child("comment").getValue(String::class.java) ?: ""
if (shared == true) {
memoList.add(Memo(memo!!, userName!!, comment, memoSnapshot))
}
}
val dialogView = LayoutInflater.from(this@CalendarActivity).inflate(R.layout.dialog_recycler_view, null)
val builder = AlertDialog.Builder(this@CalendarActivity).setView(dialogView)
val alertDialog = builder.show()
val dateText: TextView = dialogView.findViewById(R.id.dateText)
dateText.text = date
val recyclerView: RecyclerView = dialogView.findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this@CalendarActivity)
recyclerView.adapter = memoAdapter
val closeButton: Button = dialogView.findViewById(R.id.closeButton)
closeButton.setOnClickListener { alertDialog.dismiss() }
}
}
override fun onCancelled(error: DatabaseError) {
// 로그를 출력하거나 사용자에게 오류 상황을 알리는 코드를 추가합니다.
}
})
}
closeButton.setOnClickListener { finish() }
}
inner class MemoAdapter(private val memoList: ArrayList<Memo>) : RecyclerView.Adapter<MemoAdapter.MemoViewHolder>() {
inner class MemoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val memoText: TextView = itemView.findViewById(R.id.memoText)
val userName: TextView = itemView.findViewById(R.id.userName)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MemoViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.memo_item, parent, false)
return MemoViewHolder(itemView)
}
override fun onBindViewHolder(holder: MemoViewHolder, position: Int) {
val memo = memoList[position]
holder.memoText.text = memo.memo
holder.userName.text = memo.userName
if (memo.comment == "") {
// 코멘트가 빈 문자열인 경우
holder.memoText.setTextColor(Color.BLACK) // 기본 글자색을 검은색으로 설정
} else {
// 코멘트가 빈 문자열이 아닌 경우
holder.memoText.setTextColor(Color.RED) // 글자색을 빨간색으로 변경
}
holder.itemView.setOnClickListener {
val builder = AlertDialog.Builder(this@CalendarActivity)
builder.setTitle("공유된 메모")
builder.setMessage("메모: ${memo.memo}\n작성자: ${memo.userName}")
val inputComment = EditText(this@CalendarActivity)
builder.setView(inputComment)
builder.setPositiveButton("확인") { dialog, which ->
val comment = inputComment.text.toString()
if (comment.isNotEmpty()) {
// 메모의 comment 필드를 업데이트합니다.
memo.memoSnapshot.ref.child("comment").setValue(comment).addOnCompleteListener {
if (it.isSuccessful) {
Toast.makeText(this@CalendarActivity, "코멘트가 저장되었습니다.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@CalendarActivity, "코멘트 저장에 실패했습니다.", Toast.LENGTH_SHORT).show()
}
}
}
}
builder.show()
}
}
override fun getItemCount() = memoList.size
}
}
data class Memo(val memo: String, val userName: String, val comment: String, val memoSnapshot: DataSnapshot)
CalendarFragment.kt
package com.example.bodygym
import SettingFragment
import android.content.Intent
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.DatabaseReference
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.CalendarView
import android.widget.CheckBox
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.GenericTypeIndicator
class CalendarFragment : Fragment() {
private lateinit var bottomNavigationView: BottomNavigationView
private lateinit var database: FirebaseDatabase
private lateinit var myRef: DatabaseReference
private lateinit var calendarView: CalendarView
private lateinit var auth: FirebaseAuth
private var selectedDate: String = ""
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Initialize Firebase
database = FirebaseDatabase.getInstance()
myRef = database.getReference("memos")
auth = FirebaseAuth.getInstance()
val view = inflater.inflate(R.layout.calendar_main, container, false)
// 뷰를 찾아 변수에 할당
calendarView = view.findViewById(R.id.calendar)
val exerciseLogButton: Button? = view.findViewById(R.id.button_exercise_log)
if (exerciseLogButton != null) {
exerciseLogButton.setOnClickListener {
Log.d("CalendarFragment", "Button clicked") // 로그 출력
val intent = Intent(activity, ExerciseLogActivity::class.java)
intent.putExtra("selected_date", selectedDate)
startActivity(intent)
}
}
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val textViewMemo: TextView = view.findViewById(R.id.text_view_memo)
val textViewComment: TextView = view.findViewById(R.id.text_view_comment)
val checkBox = view.findViewById<CheckBox>(R.id.checkBox)
val editTextMemo = EditText(context)
val editTextComment = EditText(context)
calendarView.setOnDateChangeListener { _, year, month, dayOfMonth ->
val date = "$year-${month + 1}-$dayOfMonth"
val userId = auth.currentUser?.uid
userId?.let {
FirebaseDatabase.getInstance().getReference("Memos/$date/$userId").get()
.addOnSuccessListener { snapshot ->
val typeIndicator : GenericTypeIndicator<HashMap<String, Any>> = object: GenericTypeIndicator<HashMap<String, Any>>() {}
val memoData = snapshot.getValue(typeIndicator)
// 메모 정보를 불러옵니다.
val memo = memoData?.get("memo")?.toString() ?: ""
// 코멘트 정보를 불러옵니다.
val comment = memoData?.get("comment")?.toString() ?: ""
// 메모 정보와 코멘트 정보를 TextView에 설정합니다.
textViewMemo.text = "$memo"
textViewComment.text = "$comment"
}
.addOnFailureListener { e ->
// 데이터 불러오기 실패
e.printStackTrace()
}
}
checkBox.setOnCheckedChangeListener { _, isChecked ->
val user = auth.currentUser
user?.let { user ->
val userId = user.uid
val memoMap = HashMap<String, Any>()
memoMap["shared"] = isChecked
memoMap["memo"] = textViewMemo.text.toString()
// Users 아래에서 자신의 닉네임 값을 얻어옵니다
val nicknameRef = FirebaseDatabase.getInstance().getReference("Users/$userId/nickname")
nicknameRef.get().addOnSuccessListener { snapshot ->
val nickname = snapshot.getValue(String::class.java)
memoMap["userName"] = nickname ?: "Unknown User" // 닉네임이 없는 경우 "Unknown User"로 저장
FirebaseDatabase.getInstance().getReference("Memos/$date/$userId").updateChildren(memoMap)
}.addOnFailureListener {
// 닉네임 불러오기 실패
it.printStackTrace()
}
}
}
context?.let { context ->
AlertDialog.Builder(context)
.setTitle("메모를 입력하시겠습니까?")
.setPositiveButton("예") { dialog, _ ->
val editText = EditText(context)
AlertDialog.Builder(context)
.setTitle("$date 메모 추가")
.setView(editText)
.setPositiveButton("저장") { innerDialog, _ ->
val memo = editText.text.toString()
val user = auth.currentUser
user?.let {
val userId = user.uid
val memoMap = HashMap<String, Any>()
memoMap["shared"] = checkBox.isChecked
memoMap["memo"] = memo
memoMap["comment"] = "" // 'comment' 필드를 빈 문자열로 초기화
val nicknameRef = FirebaseDatabase.getInstance().getReference("Users/$userId/nickname")
nicknameRef.get().addOnSuccessListener { snapshot ->
val nickname = snapshot.getValue(String::class.java)
memoMap["userName"] = nickname ?: "Unknown User"
FirebaseDatabase.getInstance().getReference("Memos/$date/$userId").setValue(memoMap)
// 새로운 메모 저장 후 textViewMemo 업데이트
textViewMemo.text = memo
}.addOnFailureListener {
it.printStackTrace()
}
}
innerDialog.dismiss()
}
.setNegativeButton("취소") { innerDialog, _ ->
innerDialog.cancel()
}
.show()
dialog.dismiss()
}
.setNegativeButton("아니요") { dialog, _ ->
dialog.cancel()
}
.show()
}
}
bottomNavigationView = view.findViewById(R.id.nav_view)
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
// 홈 화면 프래그먼트로 전환
val fragment = HomeFragment()
replaceFragment(fragment)
true
}
R.id.navigation_post -> {
// 게시글 프래그먼트로 전환
val fragment = BoardFragment()
replaceFragment(fragment)
true
}
R.id.navigation_chat -> {
// 채팅 프래그먼트로 전환
val fragment = ChatListFragment()
replaceFragment(fragment)
true
}
R.id.navigation_calendar -> {
// 캘린더 프래그먼트로 전환
val fragment = CalendarFragment()
replaceFragment(fragment)
true
}
R.id.navigation_settings -> {
val fragment = SettingFragment()
replaceFragment(fragment)
true
}
else -> false
}
}
}
private fun replaceFragment(fragment: Fragment) {
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment).commit()
}
private fun saveMemo(date: String, memo: String) {
val userId = auth.currentUser?.uid
userId?.let {
val memoData = mapOf("memo" to memo, "shared" to false)
myRef.child(it).child(date).setValue(memoData)
.addOnSuccessListener {
// 데이터 저장 성공
Toast.makeText(context, "데이터 저장에 성공하였습니다.", Toast.LENGTH_SHORT).show()
}
.addOnFailureListener { e ->
// 데이터 저장 실패
e.printStackTrace()
e.message?.let { it1 -> Log.d("Firebase Error", it1) }
Toast.makeText(context, "데이터 저장에 실패하였습니다: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
}
}
메모 확인에 대한 코드
class SettingFragment : Fragment() {
private lateinit var bottomNavigationView: BottomNavigationView
private lateinit var memoCheckButton: Button
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.setting_main, container, false)
}
/*
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bottomNavigationView = view.findViewById(R.id.nav_view)
memoCheckButton = view.findViewById(R.id.checkMemoButton)
val user = FirebaseAuth.getInstance().currentUser
val userId = user?.uid
userId?.let {
val ref = FirebaseDatabase.getInstance().getReference("Users").child(userId)
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val userNickname = dataSnapshot.child("nickname").value.toString()
// 특정 닉네임을 가진 사용자만 '메모 확인' 버튼을 볼 수 있습니다.
if ((userNickname == "관장님") || (userNickname == "관리자")) {
memoCheckButton.visibility = View.VISIBLE
} else {
memoCheckButton.visibility = View.GONE
}
}
override fun onCancelled(databaseError: DatabaseError) {
// 데이터베이스 에러 처리
Log.w(TAG, "getUser:onCancelled", databaseError.toException())
}
})
}
memoCheckButton.setOnClickListener {
// AlertDialog로 "admin1234" 입력 받기
val editText = EditText(requireActivity())
AlertDialog.Builder(requireActivity())
.setTitle("관리자 확인")
.setMessage("관리자 코드를 입력하세요.")
.setView(editText)
.setPositiveButton("확인") { _, _ ->
if (editText.text.toString() == "admin1234") {
val intent = Intent(requireActivity(), CalendarActivity::class.java)
startActivity(intent)
} else {
Toast.makeText(requireActivity(), "관리자 코드가 틀렸습니다.", Toast.LENGTH_SHORT).show()
}
}
.setNegativeButton("취소", null)
.show()
}
*/
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bottomNavigationView = view.findViewById(R.id.nav_view)
memoCheckButton = view.findViewById(R.id.checkMemoButton)
val user = FirebaseAuth.getInstance().currentUser
val userId = user?.uid
userId?.let {
val ref = FirebaseDatabase.getInstance().getReference("Users").child(userId)
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val userNickname = dataSnapshot.child("nickname").value.toString()
// 특정 닉네임을 가진 사용자만 '메모 확인' 버튼을 볼 수 있습니다.
if ((userNickname == "관장님") || (userNickname == "관리자")) {
memoCheckButton.visibility = View.VISIBLE
} else {
memoCheckButton.visibility = View.GONE
}
}
override fun onCancelled(databaseError: DatabaseError) {
// 데이터베이스 에러 처리
Log.w(TAG, "getUser:onCancelled", databaseError.toException())
}
})
}
memoCheckButton.setOnClickListener {
val intent = Intent(requireActivity(), CalendarActivity::class.java)
startActivity(intent)
}
bottomNavigationView = view.findViewById(R.id.nav_view)
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
val fragment = HomeFragment()
replaceFragment(fragment)
true
}
R.id.navigation_post -> {
val fragment = BoardFragment()
replaceFragment(fragment)
true
}
R.id.navigation_chat -> {
val fragment = ChatListFragment()
replaceFragment(fragment)
true
}
R.id.navigation_calendar -> {
val fragment = CalendarFragment()
replaceFragment(fragment)
true
}
R.id.navigation_settings -> {
val fragment = SettingFragment()
replaceFragment(fragment)
true
}
else -> false
}
}
val logoutButton: Button = view.findViewById(R.id.logoutButton)
logoutButton.setOnClickListener {
val intent = Intent(activity, LoginActivity::class.java)
startActivity(intent)
activity?.finish()
}
}
private fun replaceFragment(fragment: Fragment) {
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commitNow()
}
}
[결과화면]