У меня есть фрагмент с ViewModel (так Mvvm), а в xml у меня есть 2 RecyclerView, у обоих есть собственный адаптер, использующий один и тот же список элементов, потому что я хочу видеть одни и те же данные в двух разных макетах.
Перед переключением на Mvvm я мог ссылаться на адаптер и recyclerView и мог достичь "getChildAdapterPosition"
Но сейчас это кажется совершенно невозможным. Я создал специальный BindingAdapter для привязки моего адаптера, CustomPageSnaperHelper и scrollListener. но теперь я не могу синхронизировать 2, чтобы отобразить один и тот же элемент, даже если я прокручиваю нижний или верхний.
Собираюсь опубликовать фрагмент кода, но не уверен, какой из них поможет больше:
@JvmStatic @BindingAdapter("adapter")
fun <T : RecyclerView.ViewHolder> setRvAdapter(rv: RecyclerView, adapter: RecyclerView.Adapter<T>?) {
if (adapter != null) rv.adapter = adapter
}
@JvmStatic @BindingAdapter("pageSnapper")
fun setPageSnapper(rv: RecyclerView, cs: CustomSnapHelper?){
cs?.attachToRecyclerView(rv)
}
@JvmStatic @BindingAdapter("rvScrollListener")
fun setRvScrollListener(rv: RecyclerView, sl: RecyclerView.OnScrollListener?){
if (sl == null) return
rv.addOnScrollListener(sl)
}
XML:
<androidx.recyclerview.widget.RecyclerView
adapter = "@{vm.OCardAdapter}"
pageSnapper = "@{vm.OCardPageSnaper}"
rvScrollListener = "@{vm.OCardScrollListener}"
android:layout_width = "match_parent"
android:layout_height = "wrap_content"
android:orientation = "horizontal"
app:layoutManager = "androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf = "parent"
app:layout_constraintStart_toStartOf = "parent"
app:layout_constraintTop_toBottomOf = "@id/top_tabs"
tools:listItem = "@layout/card_layout" />
<androidx.recyclerview.widget.RecyclerView
adapter = "@{vm.OCarouselAdapter}"
pageSnapper = "@{vm.OCarouselPageSnaper}"
rvScrollListener = "@{vm.OCarouselScrollListener}"
android:layout_width = "wrap_content"
android:layout_height = "200dp"
android:background = "@color/colorWhite"
android:orientation = "horizontal"
app:layoutManager = "androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf = "parent"
app:layout_constraintEnd_toEndOf = "parent"
app:layout_constraintStart_toStartOf = "parent"
tools:listItem = "@layout/carousel_layout"/>
Честно говоря, я мог бы вставить больше своего кода, но я не думаю, что это могло бы помочь больше.
val scrollListenerCard = object: RecyclerView.OnScrollListener(){
var mScrolled = false
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
}
}
Я не уверен, что хорошо понимаю вашу проблему, но если бы мне пришлось создать два rw-s, которые всегда прокручиваются в одну и ту же позицию с помощью databindig, я бы дал адаптеру A для прокрутки слушателя B в качестве параметра конструктора (и обратно тоже ), и таким образом я бы вызвал метод scrollTo () адаптера при срабатывании слушателя.
class MyScrollListener(private val recyclerView: RecyclerView): RecyclerView.OnScrollListener(){
var mScrolled = false
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
}
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
this.recyclerView.smoothScrollToPosition(newState)
}
}
val scrollListenerCard = MyScrollListener(recyclerView)
Обновил мой исходный ответ. Я был неправ, вам нужен rw вместо адаптера, но концепция та же.
Вы могли бы создать
@InverseBindingAdapter(attribute = "currentPosition")
public static int getCurrentPosition(final RecyclerView rv) {
return ((LinearLayoutManager) rv.getLayoutManager()).findFirstVisibleItemPosition();
}
@BindingAdapter(value = "currentPositionAttributeChanged")
public static void setListener(final RecyclerView rv, final InverseBindingListener l) {
final int prevPos = ((LinearLayoutManager)rv.getLayoutManager()).findFirstVisibleItemPosition();
rv.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy == 0) {
return;
}
int newPosition = linearLayoutManager.findFirstVisibleItemPosition();
if (previousPosition != newPosition) {
l.onChange();
}
}
});
}
@BindingAdapter("currentPosition")
public void setCurrentPosition(final RecyclerView rv, final int pos) {
// no need to do anything here - setter required by databinding
}
Это даст вам доступ к позициям, или вы можете установить этих слушателей, получив доступ к привязкам внутри вашего фрагмента / активности и обновив оттуда вашу viewModel.
Спасибо за это! Прекрасно работает с несколькими настройками. Я сделал новый ответ в котлине на основе этого ниже.
Основываясь на ответе Мирзы выше, я придумал это в kotlin, который отлично работает:
@JvmStatic
@InverseBindingAdapter(attribute = "currentPosition", event = "currentPositionAttributeChanged")
fun getCurrentPosition(rv: RecyclerView): Int {
return (rv.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
}
@JvmStatic
@BindingAdapter(value = ["currentPositionAttributeChanged"])
fun setListener(rv: RecyclerView, l: InverseBindingListener) {
val layoutManager = (rv.layoutManager as LinearLayoutManager?)!!
var prevPos = layoutManager.findFirstVisibleItemPosition()
rv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy == 0) {
return
}
val newPos = layoutManager.findFirstVisibleItemPosition()
if (prevPos != newPos) {
prevPos = newPos
l.onChange()
}
}
})
}
@JvmStatic
@BindingAdapter("currentPosition")
fun setCurrentPosition(rv: RecyclerView, pos: Int) {
(rv.layoutManager as LinearLayoutManager).scrollToPosition(pos)
}
Я использую это так:
ViewModel имеет:
val currentPos = MutableLiveData<Int>()
А в макете есть:
<androidx.recyclerview.widget.RecyclerView
android:id = "@+id/recyclerview"
android:layout_width = "match_parent"
android:layout_height = "match_parent"
currentPosition = "@ = {viewModel.currentPos}"
app:layoutManager = "androidx.recyclerview.widget.LinearLayoutManager">
Мой ScrollListener - это объект, который я построил. Я добавил их в конце вопроса, мне очень жаль, но я не знаю, как добавить к ним адаптер в качестве конструктора, не могли бы вы мне помочь?