import android.util.Log
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class SharedViewModel : ViewModel() {
private val _likedDataList = MutableLiveData<List<KakaoImageData>>()
val likedDataList: LiveData<List<KakaoImageData>> get() = _likedDataList
fun filterDataList(dataList: MutableList<KakaoImageData>) {
val likedList = dataList.filter { it.isliked }
_likedDataList.value = likedList.toMutableList()
Log.d("뷰모델검사","${likedDataList.value}")
Log.d("뷰모델사이즈검사","${likedDataList.value?.count()}")
}
fun getFilterDataList(): MutableList<KakaoImageData> = likedDataList.value?.toMutableList() ?: mutableListOf()
}
getFilterDataList( )로 가져갈 수 있게 했다.class SearchFragment : Fragment() {
private lateinit var adapter: SharedAdapter
private lateinit var searchWord : String
private var _binding: FragmentSearchBinding? = null
private val binding get() = _binding!!
private val sharedViewModel : SharedViewModel by activityViewModels()
private var dataList = mutableListOf<KakaoImageData>()
...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = SharedAdapter(dataList)
binding.rvImgData.adapter = adapter
binding.rvImgData.layoutManager = GridLayoutManager(context, 2)
loadSearchWord()
sharedViewModel.likedDataList.observe(viewLifecycleOwner, Observer {
adapter.setData(dataList)
})
searchClick()
clickLiked()
}
private fun getSearchImg(searchWord: String) {
RetrofitInstance.api.getImgData(query = searchWord).enqueue(object : Callback<ImageResponse> {
override fun onResponse(call: Call<ImageResponse>, response: Response<ImageResponse>) {
Log.d("레트로핏검색어검사", searchWord)
val body = response.body()
body?.let {
dataList.clear()
dataList.addAll(it.documents)
adapter.notifyDataSetChanged()
}
Log.d("api검사", "$dataList")
}
override fun onFailure(call: Call<ImageResponse>, t: Throwable) {
Log.d("api검사", "네트워크오류/데이터변환오류.")
}
})
}
private fun searchClick() {
binding.btnSearch.setOnClickListener {
searchWord = binding.etSearch.text.toString()
getSearchImg(searchWord)
Log.d("검색어검사", "$searchWord")
hideKeyboard()
saveSearchWord(searchWord)
}
}
private fun clickLiked() {
adapter.itemClick = object : SharedAdapter.ItemClick {
override fun onClick(view: View, position: Int) {
val data = dataList.getOrNull(position) ?: return
data.isliked = !data.isliked
sharedViewModel.filterDataList(dataList)
adapter.notifyItemChanged(position)
}
}
}
...
private val sharedViewModel : SharedViewModel by activityViewModels()를 통해 뷰모델을 사용할 수 있도록 연결해줬다.
[트러블슈팅] 처음 검색한 후, 두번째 검색어를 넣으면 데이터가 바뀌지 않는 오류가 생겼다. 처음에는 searchWord가 안바뀌는게 문제인가 싶어서(아마 로그를 찍어봤을 때 이것도 문제였던게 맞는 것 같다.)
=> 클래스변수로 private lateinit var searchWord : String빼서 공유했는데도 데이터가 안바뀌어서 확인해보니까 api로 들어오는 값은 바뀌는 것 같길래 dataList에서 못받는 것 같아서 찾아보니까
=> 새로운 값이 들어올 때 dataList를 한 번 비워줘야, 거기에 인덱스 0 번부터 새로운 값이 들어올 수 있는 거였다. 그래서 요청 후에 새로운 응답을 datalist에 add 하기전에 dataList.clear()를 통해 데이터를 비운 후 새 값을 담아서 리사이클러뷰에 0번부터 나올 수 있도록 했다.
onViewCreated에 LiveData의 변화를 감지해줄 sharedViewModel.likedDataList.observe를 걸어준다. observe안에는 submitList()를 넣어줘야하지만, 아직 일반 어댑터라서 setData함수를 새로 만들어서 데이터의 변화를 감지하도록 만들었다.
//SharedAdapter 클래스
fun setData(newDataList: MutableList<KakaoImageData>) {
kakaoItemData = newDataList
notifyDataSetChanged()
}
+보관함 기능을 만들면서 Adapter를 공유해서 사용하기 때문에 SearchAdapter에서 SharedAdapter로 이름을 바꿔주었다.
아이템을 클릭하면, 좋아요로 넘어가야하기 때문에 clickLiked 함수를 만들었다. 리사이클러뷰 아이템 클릭은 어댑터에서 인터페이스를 통해 가능하도록 만들었다. 그래서, 클릭하면 dataList가 IndexOutOfBoundsException 때문에 강제 종료 당하지 않도록 dataList.getOrNull(position) ?: return를 data에 담아주고, data의 isliked값이 반전 될 수 있도록 data.isliked = !data.isliked를 설정해줬다. 그 다음에 sharedViewModel.filterDataList(dataList)로 isliked가 true인 값들을 거를 수있도록 값을 넘기고, 변경 사항을 바로 알 수 있도록 adapter.notifyItemChanged(position)를 걸어주었다.
class StorageFragment : Fragment() {
private val sharedViewModel: SharedViewModel by activityViewModels()
private lateinit var adapter: SharedAdapter
private var _binding: FragmentStorageBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = FragmentStorageBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val likedData = sharedViewModel.getFilterDataList()
adapter = SharedAdapter(likedData ?: mutableListOf())
binding.rvLikedData.adapter = adapter
binding.rvLikedData.layoutManager = GridLayoutManager(context, 2)
sharedViewModel.likedDataList.observe(viewLifecycleOwner, Observer {
adapter.setData(it.toMutableList())
})
adapter.itemClick = object : SharedAdapter.ItemClick {
override fun onClick(view: View, position: Int) {
if (position < likedData.size) { // 인덱스가 유효한 범위 내에 있는지 확인
likedData[position].isliked = false
likedData.removeAt(position)
sharedViewModel.filterDataList(likedData)
adapter.notifyDataSetChanged()
}
Log.d("보관함인덱스검사", "${likedData.size},$position")
}
}
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
StorageFragment에도 마찬가지로 private val sharedViewModel: SharedViewModel by activityViewModels()를 통해 뷰모델을 연결해준다.
변화를 감지할 observe도 만들어준다.
StorageFragment도 SharedAdapter를 사용하기 때문에, 그 안에 있는 ItemClick 인터페이스를 통해서 onClickd 메소드를 override하여 클릭이벤트를 만들었다. 보관함페이지에는 isliked= true인 상태의 데이터만 있기 때문에, islked=false로 만들어주고, likedData에서 해당 인덱스값을 삭제해주고, sharedViewModel을 통해서 공유해주고 adapter에도 변경 사실을 알려준다.
StorageFragment에서 ShareViewModel의 likedDataList.value가 바뀌면 다시 인식을 안해준다.. ... 그래서 IndexOutOfBoundsException폭탄을 맞았다.ㅎㅎ 지금 생각하기엔. 일단 어댑터가 리스트어댑터가 아닌게 문제라고 생각해서 내일은 리스터어댑터로 SharedAdapter를 변경해볼 생각이다.
이걸 다하면 얼추 필수과제가 끝난 것이므로, 보관한 이미지 리스트가 앱 재시작 후 다시 보일 수 있도록 SharedPreference에 저장하는 것을 해볼 것이다.
최신정렬도 어렵지 않게 할 수 있을 것 같다.
스크롤 최상단 이동 플로팅 액션버튼은.. 애플마켓때 만들었던 것 같은데,, 거기서 따올 수 있으면 간단하게 따오기로..
목요일 오전 제출이라서 목요일 오전은 깃허브에 정리하기 바쁘고,, 벌써 내일 하기엔 빠듯한 계획인 것 같은데, 아주아주만약에 시간이 남는다면, 검색어입력창을 clear시켜주는 아이콘도 하나 걸어볼 생각이다.
벌써 내일이면 수요일인데, 어제 뷰모델로 시간만 덜 버렸어도 좀 더 이것저것 도전할 수 있지 않았을까 하는 생각이든다. 그래도 어제 버린 시간 덕분에 오늘 이만큼 할 수 있었던걸까? 싶기도하다 .. 어제 오늘 이렇게 했어도 솔직히 좀 긴가민가하다.. 팀프로젝트때 뭘 시킬지.. 너무 걱정된다.. 어쨌든 이번에 할 일을 잘 마무리하고.. 나중일은 나중에 생각해야겠다!