[TIL] 230919 회고

서정한·2023년 9월 19일
0

내일배움캠프 7기

목록 보기
49/66

Intro

  • 오늘도 계속해서 과제과제 중이다...! 현재 Fragment를 두개 만들어놓고 한쪽에 서버와 API통신해온 결과를 가져오면 그 중 내가 원하는걸 선택해서 다른 Fragment(보관함)에 추가하거나 삭제하는 기능을 구현중이다.(이것을 우리는 좋아요 기능이라 부르기로했어요...)

ViewPager2에서의 Fragment 생명주기

  • 일단 좋아요를 구현하기까지의 작업 중간과정을 설명하자면 우선 MainActivity에서 각 Fragment에 접근이 가능하므로 보관함 fragment에 추가 혹은 삭제를 위한 메서드를 구현해놓는다.
class MainActivity : AppCompatActivity() {
    companion object {
        fun newIntent(context: Context): Intent = Intent(context, MainActivity::class.java)
    }

    private val binding by lazy {
        MainActivityBinding.inflate(layoutInflater)
    }
    private val mainViewPagerAdapter by lazy {
        MainViewPagerAdapter(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        initView()
    }

    private fun initView()=with(binding) {
        viewPagerMain.adapter = mainViewPagerAdapter
        TabLayoutMediator(tabLayoutMain, viewPagerMain) { tab, position ->
            tab.setText(mainViewPagerAdapter.getTabName(position))
        }.attach()
    }

    fun AddItemForMyPage(item: ContentData) {
        val fragment = mainViewPagerAdapter.getMyPageFragment()
        fragment?.addMyPageItem(item)
    }

    fun RemoveItemForMyPage(item: ContentData){
        val fragment = mainViewPagerAdapter.getMyPageFragment()
        fragment?.removeMyPageItem(item)
    }
}
  • 이제는 보관함 fragment에서 Activity에 사용할 Add, Remove 메서드를 구현해준다.
class MyPageFragment : Fragment() {
    companion object {
        fun newInstance() = MyPageFragment()
    }

    private var _binding: MyPageFragmentBinding? = null
    private val binding get() = _binding!!

    private val listAdapter by lazy {
        MyPageListAdapter(requireContext())
    }

    private val viewModel: MyPageViewModel by lazy {
        ViewModelProvider(this)[MyPageViewModel::class.java]
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = MyPageFragmentBinding.inflate(inflater, container, false)
        return binding.root
    }

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

    private fun initView()=with(binding) {
        recyclerView.adapter = listAdapter
        viewModel.list.observe(viewLifecycleOwner) {list ->
            listAdapter.submitList(list)
        }
    }

    fun addMyPageItem(item: ContentData) {
        viewModel.addItem(item)
    }

    fun removeMyPageItem(item: ContentData) {
        viewModel.removeItem(item)
    }

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}
  • 실제 메서드의 동작은 ViewModel에서 처리해주고 있기때문에 ViewModel에서 로직을 구현해주자.
class MyPageViewModel: ViewModel() {
    private var _list = MutableLiveData<List<ContentData>>()
    val list get() = _list

    fun addItem(item: ContentData) {
        val currentList = list.value.orEmpty().toMutableList()
        currentList.add(item)
        _list.value = currentList
    }

    fun removeItem(item: ContentData) {
        val currentList = list.value.orEmpty().toMutableList()
        val findItem = currentList.find { it.id == item.id } ?: return
        val position = currentList.indexOf(findItem)
        currentList.removeAt(position)
        _list.value = currentList
    }
}
  • 그러면 이제 실제 View에서 좋아요버튼을 클릭했을때의 클릭이벤트를 처리해주는일이 남았다. 그부분을 구현해보자.(RecyclerView의 ListAdapter)
  • 처리하는 방식은 고차함수 하나를 받도록 한 후 클릭시 해당함수를 실행하도록 구현했다.
class SearchListAdapter(
    private val context: Context,
    private val onLikeClickListener: (ContentData) -> Unit
) : ListAdapter<ContentData, SearchListAdapter.SearchHolder>(
    object : DiffUtil.ItemCallback<ContentData>() {
        override fun areItemsTheSame(oldItem: ContentData, newItem: ContentData): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: ContentData, newItem: ContentData): Boolean {
            return oldItem == newItem
        }
    }
) {

    class SearchHolder(
        private val binding: SearchItemBinding,
        private val context: Context,
        private val onLikeClickListener: (ContentData) -> Unit) :
        RecyclerView.ViewHolder(binding.root) {
        fun bind(item: ContentData) = with(binding) {
            // 날짜 포멧변경
            fun changeDttmFormat(dttm: String): String {
                val old_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.000Z", Locale.KOREAN)
                val old_date = old_format.parse(dttm)
                val formatter = SimpleDateFormat("yyyy-MM-dd", Locale.KOREAN)
                val new_date = old_date?.let { formatter.format(it) }

                return new_date ?: "invalid Format"
            }

            // DateTime
            dttm.text = changeDttmFormat(item.dateTime)

            // Image
            Glide.with(context)
                .load(item.thumbnail)
                .into(image)

            // 좋아요 처리
            like.setOnClickListener {
                if(!item.isLike) {
                    onLikeClickListener(
                        item.copy(
                            isLike = true
                        )
                    )
                    // src 교체(Filled)
                    like.setBackgroundResource(R.drawable.baseline_star_24)
                } else {
                    onLikeClickListener(
                        item.copy(
                            isLike = false
                        )
                    )
                    // src 교체(not Filled)
                    like.setBackgroundResource(R.drawable.baseline_star_border_24)
                }
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchHolder {
        val view = SearchItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return SearchHolder(view, context, onLikeClickListener)
    }

    override fun onBindViewHolder(holder: SearchHolder, position: Int) {
        holder.bind(getItem(position))
    }
}
  • 이제 찐막..! 위에서 구현한 Adapter를 사용하는 Fragment에서 Adapter에 사용할 메서드를 구현해서 집어넣기만 하면된다. 내가 의도한 구현은 버튼을 클릭할때 메서드 내에서 좋아요가 true인지 false인지 체크하여 해당 Boolean값에 맞게 분기처리해주고 싶었지만..
class SearchFragment() : Fragment() {
    companion object {
        fun newInstance() = SearchFragment()
    }

    private var _binding: SearchFragmentBinding? = null
    private val binding get() = _binding!!

    private val adapter by lazy {
        SearchListAdapter(
            requireContext(),
            onLikeClickListener = { item ->
                myPageItemAddOrRemove(item)
            }
        )
    }


    private val repository by lazy {
        ContentRepository()
    }

    private val viewModel: SearchViewModel by viewModels {
        MainViewModelFactory(repository)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        _binding = SearchFragmentBinding.inflate(inflater, container, false)
        return binding.root
    }

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

    private fun initView() = with(binding) {
        recyclerView.adapter = adapter

        searchView.setOnQueryTextListener(object :
            androidx.appcompat.widget.SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(p0: String?): Boolean {
                if (p0 != null) {
                    viewModel.resultImageAndVideo(p0)
                }
                return false
            }

            override fun onQueryTextChange(p0: String?): Boolean {
                return true
            }
        })

        viewModel.contentList.observe(requireActivity()) { result ->
            adapter.submitList(result)
        }
    }

    /*
    * 좋아요 여부에 따라 MyPage의 Item 생성 혹은 삭제
    * */
    private fun myPageItemAddOrRemove(item: ContentData) {
        if(item.isLike) {
            (activity as MainActivity).RemoveItemForMyPage(item)
        } else {
            (activity as MainActivity).AddItemForMyPage(item)
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

Outro

  • 인생은 호락호락하지않다. 의도한대로 돌아가지않는다. 일단 좋아요버튼을 클릭하면 true에서 안바뀐다.. - 또한가지 문제가있는데 fragment를 탭에서 한번이라도 클릭하지않은상태에서 접근하면 fragment가 붙어있지않다고 애러를 뱉으며 앱이 죽어버린다.. 이건 내일 알아봐야지..!
profile
잘부탁드립니다!

0개의 댓글