Intro
- 오늘도 계속해서 과제과제 중이다...! 현재 Fragment를 두개 만들어놓고 한쪽에 서버와 API통신해온 결과를 가져오면 그 중 내가 원하는걸 선택해서 다른 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"
}
dttm.text = changeDttmFormat(item.dateTime)
Glide.with(context)
.load(item.thumbnail)
.into(image)
like.setOnClickListener {
if(!item.isLike) {
onLikeClickListener(
item.copy(
isLike = true
)
)
like.setBackgroundResource(R.drawable.baseline_star_24)
} else {
onLikeClickListener(
item.copy(
isLike = false
)
)
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가 붙어있지않다고 애러를 뱉으며 앱이 죽어버린다.. 이건 내일 알아봐야지..!