일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 스와이프
- 코틀린
- espresso
- 테마 아이콘
- ui test
- Android
- 안드로이드13
- 리사이클러뷰
- high order function
- 구분선
- searchview
- 안드로이드스튜디오
- fragment
- 생명주기
- 안드로이드
- recyclerview
- 재사용
- Fragment 수동 추가
- adapter
- viewholder
- Error:Execution failed for task ':app:mergeDebugResources'
- Fragment에서 Activity의 함수 사용하기
- ActivityTestRule
- 안드로이드개발레벨업교과서
- IntentTestRule
- 뷰변경 감지
- LayoutManger
- 고차함수
- binding adapter
- 코딜리티
Archives
- Today
- Total
룬아님의 취중코딩
Recyclerview + GridLayout + databinding + Multi Item ViewType 본문
이 예제는 구글 블루프린트의 recyclerview adapter를 여러 타입의 아이템으로 사용할 수 있도록 수정한 예제입니다.
1. recyclerview 아이템 타입
enum class ItemType(val typeInt: Int) {
NORMAL(0),
SECTION(1)
}
data class AttendeesItem(val type: ItemType, val item: Any)
2. NoticeAttendance
enum class VoteStatus {
@SerializedName("unselected")
UNSELECTED,
@SerializedName("attend")
ATTEND,
@SerializedName("absent")
ABSENT,
@SerializedName("late")
LATE,
NONE
}
data class NoticeAttendance(
val pk: Int,
val user: User,
var vote: VoteStatus,
val voteDisplay: String
)
3. bindingAdapter
@BindingAdapter("app:items")
fun setItems(listView: RecyclerView, items: List<AttendeesItem>) {
(listView.adapter as AttendeesDialogAdapter).submitList(items)
}
4. attendees_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.mashup.model.NoticeAttendance" />
</data>
<LinearLayout
android:background="@color/author"
android:orientation="vertical"
android:layout_width="100dp"
android:layout_height="wrap_content">
<TextView
tools:text="dsdssdsdds"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{item.user.name}" />
</LinearLayout>
</layout>
5. attendees_section_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="title"
type="String" />
</data>
<LinearLayout
android:background="@color/author"
android:orientation="vertical"
android:layout_width="300dp"
android:layout_height="wrap_content">
<TextView
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{title}" />
</LinearLayout>
</layout>
6. adapter
class AttendeesDialogAdapter :
ListAdapter<AttendeesItem, RecyclerView.ViewHolder>(TaskDiffCallback()) {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = getItem(position)
if (item.type == ItemType.NORMAL)
(holder as ItemViewHolder).bind(item.item as NoticeAttendance)
else
(holder as SectionViewHolder).bind(item.item as String)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == ItemType.NORMAL.typeInt)
ItemViewHolder.from(parent)
else
SectionViewHolder.from(parent)
}
override fun getItemViewType(position: Int): Int {
return getItem(position).type.typeInt
}
class ItemViewHolder private constructor(val binding: AttendeesItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: NoticeAttendance) {
binding.item = item
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): ItemViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = AttendeesItemBinding.inflate(layoutInflater, parent, false)
return ItemViewHolder(binding)
}
}
}
class SectionViewHolder private constructor(val binding: AttendeesSectionItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(title: String) {
binding.title = title
binding.executePendingBindings()
}
companion object {
fun from(parent: ViewGroup): SectionViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = AttendeesSectionItemBinding.inflate(layoutInflater, parent, false)
return SectionViewHolder(binding)
}
}
}
}
/**
* Callback for calculating the diff between two non-null items in a list.
*
* Used by ListAdapter to calculate the minimum number of changes between and old list and a new
* list that's been passed to `submitList`.
*/
class TaskDiffCallback : DiffUtil.ItemCallback<AttendeesItem>() {
override fun areItemsTheSame(oldItem: AttendeesItem, newItem: AttendeesItem): Boolean {
return oldItem.type == newItem.type && oldItem.item == newItem.item
}
override fun areContentsTheSame(oldItem: AttendeesItem, newItem: AttendeesItem): Boolean {
return false
}
}
7. ViewModel
class AttendeesDialogViewModel(
attendanceList: List<NoticeAttendance>
) : ViewModel() {
private val _items = MutableLiveData<List<AttendeesItem>>().apply { value = emptyList() }
val items: LiveData<List<AttendeesItem>> = _items
init {
val attendList = ArrayList<AttendeesItem>()
val absentList = ArrayList<AttendeesItem>()
val unselectedList = ArrayList<AttendeesItem>()
attendanceList.forEach {
when {
it.vote == VoteStatus.ATTEND -> attendList.add(AttendeesItem(ItemType.NORMAL, it))
it.vote == VoteStatus.ABSENT -> absentList.add(AttendeesItem(ItemType.NORMAL, it))
else -> unselectedList.add(AttendeesItem(ItemType.NORMAL, it))
}
}
val array = ArrayList<AttendeesItem>()
array.add(AttendeesItem(ItemType.SECTION, "참석 할게요 : ${attendList.size}"))
array.addAll(attendList)
array.add(AttendeesItem(ItemType.SECTION, "결석 할게요 : ${absentList.size}"))
array.addAll(absentList)
array.add(AttendeesItem(ItemType.SECTION, "미선택 : ${unselectedList.size}"))
array.addAll(unselectedList)
_items.value = array
}
}
8. fragment
class AttendeesDialogFragment(attendanceList: List<NoticeAttendance>) : DialogFragment() {
companion object {
fun newInstance(attendanceList: List<NoticeAttendance>) = AttendeesDialogFragment(attendanceList)
const val TAG_ATTENDEES_DIALOG = "tag_attendees_dialog"
}
private val viewModel: AttendeesDialogViewModel by viewModel { parametersOf(attendanceList) }
private lateinit var viewDataBinding: AttendeesDialogFragmentBinding
private lateinit var listAdapter: AttendeesDialogAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewDataBinding = AttendeesDialogFragmentBinding.inflate(inflater, container, false).apply {
viewmodel = viewModel
}
return viewDataBinding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewDataBinding.setLifecycleOwner(this.viewLifecycleOwner)
setupListAdapter()
}
private fun setupListAdapter() {
val viewModel = viewDataBinding.viewmodel
if (viewModel != null) {
val gridLayoutManager = GridLayoutManager(this.context, 3)
gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (listAdapter.getItemViewType(position) == ItemType.SECTION.typeInt) 3 else 1
}
}
viewDataBinding.attendanceList.layoutManager = gridLayoutManager
listAdapter = AttendeesDialogAdapter()
viewDataBinding.attendanceList.adapter = listAdapter
}
}
}
반응형
'개발 > 안드로이드 개발' 카테고리의 다른 글
Android Navigation Component save state on Bottom Navigation (0) | 2019.11.26 |
---|---|
RxJava + Retrofit2 Error 객체로 받아 커스텀 하기 (0) | 2019.11.25 |
(koin) inject 할때에 parameter 전달하기 (1) | 2019.11.21 |
Android Studio emulator custom density (0) | 2019.11.19 |
Android Navigation component 안전하게 데이터 넘기기 (0) | 2019.11.11 |
Comments