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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
/*
* Copyright © 2017-2023 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package com.wireguard.android.databinding
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ObservableList
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import com.wireguard.android.BR
import java.lang.ref.WeakReference
/**
* Helper class for binding an ObservableList to the children of a ViewGroup.
*/
internal class ItemChangeListener<T>(private val container: ViewGroup, private val layoutId: Int, private val fragment: Fragment?) {
private val callback = OnListChangedCallback(this)
private val layoutInflater: LayoutInflater = LayoutInflater.from(container.context)
private var list: ObservableList<T>? = null
private fun getView(position: Int, convertView: View?): View {
var binding = if (convertView != null) DataBindingUtil.getBinding<ViewDataBinding>(convertView) else null
if (binding == null) {
binding = DataBindingUtil.inflate(layoutInflater, layoutId, container, false)
}
require(list != null) { "Trying to get a view while list is still null" }
binding!!.setVariable(BR.collection, list)
binding.setVariable(BR.item, list!![position])
binding.setVariable(BR.fragment, fragment)
binding.executePendingBindings()
return binding.root
}
fun setList(newList: ObservableList<T>?) {
list?.removeOnListChangedCallback(callback)
list = newList
if (list != null) {
list!!.addOnListChangedCallback(callback)
callback.onChanged(list!!)
} else {
container.removeAllViews()
}
}
private class OnListChangedCallback<T> constructor(listener: ItemChangeListener<T>) : ObservableList.OnListChangedCallback<ObservableList<T>>() {
private val weakListener: WeakReference<ItemChangeListener<T>> = WeakReference(listener)
override fun onChanged(sender: ObservableList<T>) {
val listener = weakListener.get()
if (listener != null) {
// TODO: recycle views
listener.container.removeAllViews()
for (i in sender.indices)
listener.container.addView(listener.getView(i, null))
} else {
sender.removeOnListChangedCallback(this)
}
}
override fun onItemRangeChanged(
sender: ObservableList<T>, positionStart: Int,
itemCount: Int
) {
val listener = weakListener.get()
if (listener != null) {
for (i in positionStart until positionStart + itemCount) {
val child = listener.container.getChildAt(i)
listener.container.removeViewAt(i)
listener.container.addView(listener.getView(i, child))
}
} else {
sender.removeOnListChangedCallback(this)
}
}
override fun onItemRangeInserted(
sender: ObservableList<T>, positionStart: Int,
itemCount: Int
) {
val listener = weakListener.get()
if (listener != null) {
for (i in positionStart until positionStart + itemCount)
listener.container.addView(listener.getView(i, null))
} else {
sender.removeOnListChangedCallback(this)
}
}
override fun onItemRangeMoved(
sender: ObservableList<T>, fromPosition: Int,
toPosition: Int, itemCount: Int
) {
val listener = weakListener.get()
if (listener != null) {
val views = arrayOfNulls<View>(itemCount)
for (i in 0 until itemCount) views[i] = listener.container.getChildAt(fromPosition + i)
listener.container.removeViews(fromPosition, itemCount)
for (i in 0 until itemCount) listener.container.addView(views[i], toPosition + i)
} else {
sender.removeOnListChangedCallback(this)
}
}
override fun onItemRangeRemoved(
sender: ObservableList<T>, positionStart: Int,
itemCount: Int
) {
val listener = weakListener.get()
if (listener != null) {
listener.container.removeViews(positionStart, itemCount)
} else {
sender.removeOnListChangedCallback(this)
}
}
}
}
|