/*
* 작성자: 서정한
* 내용: DetailPage에서 북마크로 지정한 item들을 보여주고
* 선택한 아이템을 삭제하는 기능이 있습니다.
* */
class BookmarkFragment : Fragment() {
private var _binding: FragmentBookmarkBinding? = null
private val binding get() = _binding!!
private val adapter by lazy {
BookmarkListAdapter(onItemChecked = { position, item ->
updateItemChecked(position, item)
})
}
private val viewModel: BookmarkViewModel by viewModels()
private val sharedViewModel: MainSharedViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentBookmarkBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initView()
initViewModel()
}
private fun initView() = with(binding) {
bookmarkRecyclerview.adapter = adapter
// Edit
bookmarkEditTextview.setOnClickListener {
val type: BookmarkViewType = BookmarkViewType.Edit
changeVisibleForType(type)
updateBookmarkListType(type)
}
// Close
bookmarkCloseButton.setOnClickListener {
val type: BookmarkViewType = BookmarkViewType.Normal
changeVisibleForType(type)
updateBookmarkListType(type)
}
// Remove
bookmarkRemoveButton.setOnClickListener {
removeDialog(requireContext())
}
}
private fun initViewModel() {
with(viewModel) {
list.observe(viewLifecycleOwner) { list ->
adapter.submitList(list)
}
}
with(sharedViewModel) {
bookmarkEvent.observe(viewLifecycleOwner) { event ->
when (event) {
is MainSharedEventForBookmark.BookmarkItemForAdd -> {
viewModel.addBookmarkItem(event.item)
}
is MainSharedEventForBookmark.BookmarkItemForRemove -> {
viewModel.removeSelectedBookmarkItem(event.item)
}
}
}
homeEvent.observe(viewLifecycleOwner) {event ->
when(event) {
is MainSharedEventForHome.UpdateHomeItem -> {
if(event.item.isBookmarked) {
viewModel.addBookmarkItem(event.item.toBookmarkModel())
} else {
viewModel.removeSelectedBookmarkItem(event.item.toBookmarkModel())
}
}
}
}
}
}
// Edit Mode Selector. Edit버튼을 클릭할경우 EditMode로 View를 업데이트하여 보여줍니다.
// Close 버튼을 클릭할경우 Edit모드가 종료되고 UI를 업데이트 합니다.
private fun changeVisibleForType(type: BookmarkViewType) = with(binding) {
when (type) {
BookmarkViewType.Edit -> {
bookmarkCloseButton.visibility = View.VISIBLE
bookmarkRemoveButton.visibility = View.VISIBLE
bookmarkEditTextview.visibility = View.INVISIBLE
}
BookmarkViewType.Normal -> {
bookmarkEditTextview.visibility = View.VISIBLE
bookmarkCloseButton.visibility = View.INVISIBLE
bookmarkRemoveButton.visibility = View.INVISIBLE
}
}
}
// Edit모드 여부에 따라 item의 viewType 데이터를 변경해줍니다.
private fun updateBookmarkListType(type: BookmarkViewType) {
viewModel.updateBookmarkListType(type)
}
// 체크박스상태 업데이트
private fun updateItemChecked(position: Int, item: BookmarkModel) {
viewModel.updateItemChecked(position, item)
}
// 삭제버튼 Dialog
private fun removeDialog(context: Context) {
val current = viewModel.list.value.orEmpty().toMutableList()
val removes = ArrayList<BookmarkModel>()
removes.addAll(current.filter { it.isChecked })
if (removes.isEmpty()) {
return
}
val builder = AlertDialog.Builder(context)
builder.setTitle("삭제")
builder.setMessage("정말 삭제하시겠습니까?")
builder.setIcon(R.mipmap.ic_launcher)
// 버튼 클릭시에 무슨 작업을 할 것인가!
val listener = DialogInterface.OnClickListener { _, p1 ->
when (p1) {
DialogInterface.BUTTON_POSITIVE -> {
for (i in removes.indices) {
Util.removeBookmarkItemForSharedPrefs(requireContext(), removes[i])
viewModel.removeSelectedBookmarkItem(removes[i])
sharedViewModel.updateHomeItems(
removes[i].copy(
isBookmarked = false
)
)
}
}
DialogInterface.BUTTON_NEGATIVE -> {}
}
}
builder.setPositiveButton("확인", listener)
builder.setNegativeButton("취소", listener)
builder.show()
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
}
/*
* 작성자: 서정한
* 내용: RecyclerView Adapter.
* viewType에 따라 Holder or EditHolder를 띄워줌.
* */
class BookmarkListAdapter(private val onItemChecked: (Int, BookmarkModel) -> Unit) :
ListAdapter<BookmarkModel, RecyclerView.ViewHolder>(
object : DiffUtil.ItemCallback<BookmarkModel>() {
override fun areItemsTheSame(oldItem: BookmarkModel, newItem: BookmarkModel): Boolean {
return oldItem.imgUrl == newItem.imgUrl
}
override fun areContentsTheSame(
oldItem: BookmarkModel,
newItem: BookmarkModel
): Boolean {
return oldItem == newItem
}
}
) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
BookmarkViewType.Normal.INT -> {
val view = BookmarkRecyclerItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
Holder(view)
}
BookmarkViewType.Edit.INT -> {
val view = BookmarkRecyclerItemCheckboxBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
EditHolder(view, onItemChecked)
}
else -> {
val view = BookmarkRecyclerItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
Holder(view)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is Holder -> {
holder.bind(getItem(position))
}
is EditHolder -> {
holder.bind(getItem(position))
}
}
}
override fun getItemViewType(position: Int): Int {
return when (getItem(position).viewType) {
BookmarkViewType.Edit -> {
BookmarkViewType.Edit.INT
}
BookmarkViewType.Normal -> {
BookmarkViewType.Normal.INT
}
}
}
class Holder(private val binding: BookmarkRecyclerItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: BookmarkModel) = with(binding) {
Glide.with(itemView.context)
.load(item.imgUrl)
.into(bookmarkItemImage)
bookmarkItemTitle.text = item.title
bookmarkItemContent.text = item.description
}
}
class EditHolder(
private val binding: BookmarkRecyclerItemCheckboxBinding,
private val onItemChecked: (Int, BookmarkModel) -> Unit
) : RecyclerView.ViewHolder(binding.root) {
fun bind(item: BookmarkModel) = with(binding) {
Glide.with(itemView.context)
.load(item.imgUrl)
.into(bookmarkItemImage)
bookmarkItemTitle.text = item.title
bookmarkItemContent.text = item.description
bookmarkItemCheckbox.isChecked = item.isChecked
// 체크박스 체크되어있을때만 ClickEvent 동작
bookmarkItemCheckbox.setOnClickListener {
onItemChecked(adapterPosition, item.copy(
isChecked = bookmarkItemCheckbox.isChecked
))
}
}
}
}
/*
* 작성자: 서정한
* 내용: RecyclerView Adapter에서 ViewType 분기처리를 위한 sealed Class
* */
@Parcelize
sealed class BookmarkViewType : Parcelable {
data object Normal : BookmarkViewType() {
// adapter의 onCreateViewHolder에서 viewType 분기처리시 사용
const val INT = 0
}
data object Edit: BookmarkViewType() {
// adapter의 onCreateViewHolder에서 viewType 분기처리시 사용
const val INT= 1
}
보통 400번 오류는 클라이언트에 서버가 정보를 주려고했는데 클라이언트쪽에 오류가발생해 정보를 받을 수 없을경우 발생하는 오류라고 한다.
private val logging by lazy {
HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
}
private val okHttpClient by lazy {
OkHttpClient.Builder()
.addInterceptor(AuthorizationInterceptor())
.addInterceptor(logging)
.build()
}
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(getDateFormatGsonBuilder()))
.build()
}