본문 바로가기

[TIL][내일배움캠프]

[내일배움캠프][TIL] 24.02.01 (목) - Android 앱개발 심화 개인과제 : 좋아요 기능 추가

1. Android 앱개발 심화 개인과제

 

어제는 드디어 Retrofit을 연결하여 API를 불러오는 데에 성공했다.

 

오늘은 이제 남은 기능인 좋아요 기능과 SharedPreference를 완성하여 필수 구현 항목들을 전부 완성시키는 것이 목표다.

 

 

좋아요 기능

 

우선 이 좋아요 기능을 자세하게 설명하자면

 

  • 리스트에서 특정 이미지를 선택하면 특별한 표시를 보여주도록 구현(좋아요/별표/하트 등)
  • 선택된 이미지는 MainActivity의 ‘선택된 이미지 리스트 변수’에 저장
  • MainActivity의 ‘선택된 이미지 리스트 변수’에서 데이터를 받아오도록 구현
  • 내보관함에 보관된 이미지를 선택하면 보관함에서 제거할 수 있도록 구현

 

즉, 다시 말해서 검색 Fragment에서 이미지를 선택하면 좋아요 표시를 껐다 킬 수 있고, 좋아요 표시가 된 이미지들만 따로 MyPage Fragment에 따로 표시되게 만들어야하며, MyPage Frament에 있는 이미지들을 클릭하면 목록에서 삭제시킬 수 있어야 한다.

 

 

그렇다면 이제 좋아요 기능을 한 번 만들어보자.

 

우선, 좋아요 기능을 사용하려면 Boolean 자료형의 변수가 추가되어야 한다.

하지만, 그렇다고 현재 API에서 JSON 형태로 받아오는 data class에 변수를 추가해서는 안 된다. 그러면 JSON을 제대로 받지 못 하기 때문.

 

그렇기 때문에, 또다른 data class를 만들어서 거기에 Boolean 자료형 변수를 추가해야 한다.

data class SearchItem (var title: String, var dateTime: String, var url: String, var isLike: Boolean = false)

 

여기서 true일 때 이벤트를 발생시켜야 하므로, 기본값에 false를 집어넣는다.

 

 

그 다음, Adapter에도 이 추가된 자료형을 집어넣어서 만들어야 한다.

holder.itemLike.visibility = if(items[position].isLike) View.VISIBLE else View.INVISIBLE

기본적으로 좋아요 표시가 안 보이게 만들어야 하므로 true이면 VISIBLE, false이면 INVISIBLE로 설정한다.

 

init {
	itemLike.visibility = View.GONE
	itemImage.setOnClickListener(this)
	clItem.setOnClickListener(this)
}

초기 설정을 설정하는 부분이다.

 

        override fun onClick(v: View?) {
            val position = adapterPosition.takeIf { it != RecyclerView.NO_POSITION } ?: return
            val item = items[position]

            item.isLike = !item.isLike

            if (item.isLike) {
                (mContext as MainActivity).addLikedItems(item)
            } else {
                (mContext as MainActivity).removeLikedItems(item)
            }

            notifyItemChanged(position)
        }

이미지를 클릭했을 때 발생하는 이벤트를 나타내는 함수다.

 

우선, 이미지를 클릭하면 isLike 변수를 반전시킨다. true면 false로, 그 반대도 마찬가지.

그리고 그 isLike가 true면 MainActivity에 있는 addLikedItems 메소드를 실행시키고, false면 removeLikedItems를 실행시킨다.

 

 

그러면 저 안에 있는 addLikedItems와 removeLikedItems는 어떤 식일까?

저 함수가 있는 MainActivity로 넘어가보자.

 

MainActivity.kt

var likedItems: ArrayList<SearchItem> = ArrayList()

우선, 좋아요한 이미지의 데이터들을 보관하는 리스트를 하나 만든다.

 

    fun addLikedItems (item: SearchItem) {
        if (!likedItems.contains(item)) likedItems.add(item)
    }

    fun removeLikedItems (item: SearchItem) {
        likedItems.remove(item)
    }

그리고 addLikedItems는 저 likedItems 안에 같은 것이 없으면 리스트에 추가하는 메소드이고, removeLikedItems는 반대로 likedItems에 있는 리스트를 제거하는 메소드이다.

 

 

마지막으로, 좋아요가 찍힌 이미지를 받아오는 MyPage 작업을 진행해보자.

일단 recyclerView는 SearchFragment와 MyPageFragment가 같이 공유하므로 따로 더 만들 필요는 없다.

 

우리가 필요로 하는 건 Adapter와 Fragment.

 

MyPageAdapter.kt

class MyPageAdapter(var mContext: Context) : RecyclerView.Adapter<MyPageAdapter.Holder>() {

    var items = ArrayList<SearchItem>()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        val binding = ItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return Holder(binding)
    }
    override fun onBindViewHolder(holder: MyPageAdapter.Holder, position: Int) {

        //Glide를 통한 Image URI 형식 받기
        Glide.with(mContext)
            .load(items[position].url)
            .into(holder.itemImage)

        holder.itemTitle.text = items[position].title

        // 시간 포맷 설정
        holder.itemDate.text = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(
            OffsetDateTime.parse(items[position].dateTime))

        holder.itemLike.visibility = View.GONE
    }
    override fun getItemCount(): Int {
        return items.size
    }
    inner class Holder (binding: ItemBinding) : RecyclerView.ViewHolder(binding.root) {
        val itemImage = binding.searchImage
        val itemTitle = binding.searchTitle
        val itemDate = binding.searchDate
        val itemLike = binding.searchLike
        val clItem: ConstraintLayout = binding.constRecyclerview

        init {
            itemLike.visibility = View.GONE

            clItem.setOnClickListener {
                val position = adapterPosition
                if (position != RecyclerView.NO_POSITION) {
                    items.removeAt(position)
                    notifyItemRemoved(position)
                }
            }
        }
    }
}

 

Adapter 역시 SearchAdapter와 크게 차이는 없다.

차이점은 이미지를 클릭하면 제거하는 기능만 있는 것뿐.

 

MyPageFragment.kt

class MyPageFragment : Fragment() {
    private var binding: FragmentMyPageBinding? = null
    private lateinit var mContext: Context
    private lateinit var adapter: MyPageAdapter
    private var likedItems: List<SearchItem> = listOf()

    override fun onAttach(context: Context) {
        super.onAttach(context)
        mContext = context
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        val mainActivity = activity as MainActivity
        likedItems = mainActivity.likedItems

        adapter = MyPageAdapter(mContext).apply {
            items = likedItems.toMutableList() as ArrayList<SearchItem>
        }

        binding = FragmentMyPageBinding.inflate(inflater, container, false).apply {
            myrecyclerview.layoutManager = GridLayoutManager(context, 2)
            myrecyclerview.adapter = adapter
        }
        return binding?.root
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
    }

}

MainActivity에서 저장된 likedItems를 불러와서 보여준다.

그리고 Adapter 역시 불러와서 이미지를 클릭하면 삭제해주는 기능까지 추가되었다.

 

 

그리고 이것이 바로 좋아요 기능을 포함한 모든 기능을 완성시킨 앱 동영상이다.