Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions .idea/deploymentTargetSelector.xml

This file was deleted.

16 changes: 9 additions & 7 deletions app/src/main/java/com/example/realflo/Album.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import androidx.room.PrimaryKey

@Entity(tableName = "AlbumTable")
data class Album(
@PrimaryKey(autoGenerate = true)
var id: Int = 0,
var title: String = "",
var singer: String = "",
var coverImg: Int = 0,
var releaseDate: String = "",

var title: String? = "",
var singer: String? = "",
var coverImg: Int? = null,
var description: String? = null

)
var isLike: Boolean = false
) {
@PrimaryKey(autoGenerate = true)
var id: Int = 0
}
70 changes: 57 additions & 13 deletions app/src/main/java/com/example/realflo/AlbumFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ class AlbumFragment : Fragment() {

private lateinit var binding: FragmentAlbumBinding
private lateinit var db: FloDatabase
private val information = arrayListOf("수록곡","상세정보","영상")
private val information = arrayListOf("수록곡", "상세정보", "영상")

private var currentAlbum: Album? = null
private var albumId: Int = -1

override fun onCreateView(
inflater: LayoutInflater,
Expand All @@ -29,29 +32,70 @@ class AlbumFragment : Fragment() {

// 뒤로가기
binding.albumBackIv.setOnClickListener {
requireActivity().supportFragmentManager.popBackStack()
parentFragmentManager.popBackStack()
}

// 1) HomeFragment에서 넘긴 albumId 받기
val albumId = arguments?.getInt("albumId") ?: -1
// HomeFragment 에서 넘어온 앨범 ID
albumId = arguments?.getInt("albumId") ?: -1

// 1) 앨범 정보 로드
loadAlbumHeader()

// 2) ViewPager 설정
val albumAdapter = AlbumVPAdapter(this, albumId)
binding.albumContentVp.adapter = albumAdapter

// 2) 앨범 헤더 채우기 (제목/가수/커버)
TabLayoutMediator(binding.albumContentTb, binding.albumContentVp) { tab, position ->
tab.text = information[position]
}.attach()

return binding.root
}

private fun loadAlbumHeader() {
viewLifecycleOwner.lifecycleScope.launch {
val album = withContext(Dispatchers.IO) { db.albumDao().getAlbum(albumId) }
val album = withContext(Dispatchers.IO) {
db.albumDao().getAlbumById(albumId)
}

currentAlbum = album

album?.let {
binding.albumMusicTitleTv.text = it.title
binding.albumSingerNameTv.text = it.singer
it.coverImg?.let { resId -> binding.albumAlbumIv.setImageResource(resId) }

setLikeUI(it.isLike)

// 좋아요 버튼 동작
binding.albumLikeIv.setOnClickListener {
toggleAlbumLike()
}
}
}
}

// 3) ViewPager에 albumId 넘겨주기
val albumAdapter = AlbumVPAdapter(this, albumId)
binding.albumContentVp.adapter = albumAdapter
TabLayoutMediator(binding.albumContentTb, binding.albumContentVp) { tab, position ->
tab.text = information[position]
}.attach()
private fun toggleAlbumLike() {
val album = currentAlbum ?: return
val newValue = !album.isLike

return binding.root
// UI 즉시 변경
setLikeUI(newValue)

// DB 업데이트
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
db.albumDao().updateAlbumLike(album.id, newValue)
}

// 메모리도 갱신
album.isLike = newValue
}

private fun setLikeUI(isLike: Boolean) {
if (isLike) {
binding.albumLikeIv.setImageResource(R.drawable.ic_my_like_on)
} else {
binding.albumLikeIv.setImageResource(R.drawable.ic_my_like_off)
}
}
}
56 changes: 56 additions & 0 deletions app/src/main/java/com/example/realflo/AlbumLockerRVAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.example.realflo

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.realflo.databinding.ItemLockerAlbumBinding

class AlbumLockerRVAdapter :
ListAdapter<Album, AlbumLockerRVAdapter.ViewHolder>(AlbumDiffCallback()) {

private var onMoreClickListener: ((Album) -> Unit)? = null

fun setOnMoreClickListener(listener: (Album) -> Unit) {
onMoreClickListener = listener
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding = ItemLockerAlbumBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return ViewHolder(binding)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
}

inner class ViewHolder(private val binding: ItemLockerAlbumBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(album: Album) {
binding.itemAlbumTitleTv.text = album.title
binding.itemAlbumSingerTv.text = album.singer
binding.itemAlbumImgIv.setImageResource(album.coverImg)

// (…) 버튼 클릭 시
binding.itemAlbumMoreIv.setOnClickListener {
onMoreClickListener?.invoke(album)
}
}
}
}

class AlbumDiffCallback : DiffUtil.ItemCallback<Album>() {
override fun areItemsTheSame(oldItem: Album, newItem: Album): Boolean {
return oldItem.id == newItem.id
}

override fun areContentsTheSame(oldItem: Album, newItem: Album): Boolean {
return oldItem == newItem
}
}
29 changes: 26 additions & 3 deletions app/src/main/java/com/example/realflo/BannerFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,18 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.realflo.databinding.FragmentBannerBinding

class BannerFragment(val imgRes : Int) : Fragment(){
lateinit var binding: FragmentBannerBinding
class BannerFragment : Fragment() {

private lateinit var binding: FragmentBannerBinding

// onCreate에서 arguments 받기
private var imgRes: Int = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
imgRes = arguments?.getInt(ARG_IMG_RES) ?: 0
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand All @@ -18,4 +28,17 @@ class BannerFragment(val imgRes : Int) : Fragment(){
binding.bannerImageIv.setImageResource(imgRes)
return binding.root
}
}

companion object {

private const val ARG_IMG_RES = "imgRes"

fun newInstance(imgRes: Int): BannerFragment {
val fragment = BannerFragment()
val args = Bundle()
args.putInt(ARG_IMG_RES, imgRes)
fragment.arguments = args
return fragment
}
}
}
5 changes: 1 addition & 4 deletions app/src/main/java/com/example/realflo/DetailFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,13 @@ class DetailFragment : Fragment() {

viewLifecycleOwner.lifecycleScope.launch {
val album = withContext(Dispatchers.IO) {
db.albumDao().getAlbum(albumId)
db.albumDao().getAlbumById(albumId)
}

album?.let {
binding.detailTitleTv.text = it.title
binding.detailSingerTv.text = it.singer
// 설명은 별도 필드가 없으니 임시로 구성
binding.detailDescTv.text = "이 앨범은 ${it.singer}의 \"${it.title}\" 입니다."
// 만약 fragment_detail.xml에 cover 이미지뷰가 있다면 주석 해제
// it.coverImg?.let { resId -> binding.detailCoverIv.setImageResource(resId) }
}
}
}
Expand Down
9 changes: 4 additions & 5 deletions app/src/main/java/com/example/realflo/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,24 @@ class HomeFragment : Fragment() {
}
})

// 배너(그대로 유지)
// 배너
val bannerAdapter = BannerVPAdapter(this).apply {
addFragment(BannerFragment(R.drawable.img_home_viewpager_exp))
addFragment(BannerFragment(R.drawable.img_home_viewpager_exp2))
addFragment(BannerFragment.newInstance(R.drawable.img_home_viewpager_exp))
addFragment(BannerFragment.newInstance(R.drawable.img_home_viewpager_exp2))
}
binding.homeBannerVp.apply {
adapter = bannerAdapter
orientation = ViewPager2.ORIENTATION_HORIZONTAL
}

// 🔥 더미 add(...) 제거하고 DB에서 로드
loadAlbumsFromDb()

return binding.root
}

private fun loadAlbumsFromDb() {
viewLifecycleOwner.lifecycleScope.launch {
val albums = withContext(Dispatchers.IO) { db.albumDao().getAlbums() }
val albums = withContext(Dispatchers.IO) { db.albumDao().getAllAlbums() }
albumDatas.clear()
albumDatas.addAll(albums)
albumRVAdapter.notifyDataSetChanged()
Expand Down
19 changes: 10 additions & 9 deletions app/src/main/java/com/example/realflo/LockerAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,19 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.realflo.databinding.ItemSongBinding
import java.util.Locale

class LockerAdapter(
private val onSongClicked: (Song) -> Unit
private val onSongClicked: (Song) -> Unit,
private val onLikeClicked: (Song) -> Unit // 보관함에서 하트 클릭 콜백
) : ListAdapter<Song, LockerAdapter.SongViewHolder>(SongDiffCallback()) {

inner class SongViewHolder(private val binding: ItemSongBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(song: Song, order: Int) {
// 순번, 제목, 가수
binding.songOrderTv.text = String.format("%02d", order)
binding.songOrderTv.text = String.format(Locale.US, "%02d", order)
binding.songTitleTv.text = song.title
binding.songSingerTv.text = song.singer

Expand All @@ -26,17 +28,16 @@ class LockerAdapter(
else R.drawable.ic_my_like_off
)

// 클릭 동작
// 곡 클릭
binding.root.setOnClickListener { onSongClicked(song) }
binding.songPlayIv.setOnClickListener { onSongClicked(song) }

// 하트 토글(화면상만 변경; DB 반영은 이후 단계에서 처리)
// 하트 클릭 → Fragment 로 이벤트만 전달
binding.likeIv.setOnClickListener {
// Note: This only changes the UI state, not the database.
// The database update should be handled in the Fragment/ViewModel.
val currentSong = getItem(bindingAdapterPosition)
currentSong.isLike = !currentSong.isLike
notifyItemChanged(bindingAdapterPosition)
val pos = adapterPosition
if (pos != RecyclerView.NO_POSITION) {
onLikeClicked(song)
}
}
}
}
Expand Down
41 changes: 33 additions & 8 deletions app/src/main/java/com/example/realflo/LockerFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,56 @@ class LockerFragment : Fragment() {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

setupRecyclerView()
setupSavedAlbumButton()
loadLikedSongs()
}

override fun onResume() {
super.onResume()
// SongActivity에서 하트 토글하고 돌아왔을 때 갱신
loadLikedSongs()
}

private fun setupRecyclerView() {
adapter = LockerAdapter { song ->
val intent = Intent(requireContext(), SongActivity::class.java)
intent.putExtra("songId", song.id)
startActivity(intent)
private fun setupSavedAlbumButton() {
// 저장앨범 버튼 클릭 시 → 저장앨범 목록 Fragment 로 이동
binding.lockerSavedAlbumBtn.setOnClickListener {
parentFragmentManager.beginTransaction()
.replace(R.id.main_frm, SavedAlbumFragment())
.addToBackStack(null)
.commit()
}
}

private fun setupRecyclerView() {
adapter = LockerAdapter(
onSongClicked = { song ->
// 곡 클릭하면 SongActivity 이동
val intent = Intent(requireContext(), SongActivity::class.java)
intent.putExtra("songId", song.id)
startActivity(intent)
},

onLikeClicked = { song ->
// 좋아요 해제
viewLifecycleOwner.lifecycleScope.launch {
withContext(Dispatchers.IO) {
db.songDao().updateLike(song.id, false)
}
loadLikedSongs()
}
}
)

binding.lockerRecyclerview.adapter = adapter
binding.lockerRecyclerview.layoutManager = LinearLayoutManager(requireContext())
binding.lockerRecyclerview.layoutManager =
LinearLayoutManager(requireContext())
}

private fun loadLikedSongs() {
viewLifecycleOwner.lifecycleScope.launch {
val likedSongs = withContext(Dispatchers.IO) {
db.songDao().getLikedSongs() // 좋아요만 조회
db.songDao().getLikedSongs()
}

adapter.submitList(likedSongs)
Expand Down
Loading