Как синхронизировать прокрутку первых позиций из 2 RecyclerViews?
Фон
У меня есть 2 экземпляра RecyclerView. Один из них горизонтальный, второй - вертикальный.
Оба они показывают одни и те же данные и имеют одинаковое количество элементов, но по-разному, а ячейки не обязательно равны по размеру через каждый из них.
Я хочу, чтобы прокрутка в одном будет синхронизироваться с другим, так что первый элемент, показанный на одном, всегда будет отображаться на другом (как первый).
Проблема
Несмотря на то, что мне удалось синхронизировать (я просто выбираю, какой из них является "мастером", чтобы управлять прокруткой другого), направление прокрутки, похоже, влияет на способ его работы.
Предположим, что на данный момент ячейки имеют равный вес:
Если я прокручиваю вверх/влево, он работает так, как я предполагал, более или менее:
![введите описание изображения здесь]()
Однако, если я прокручиваю вниз/вправо, это позволяет другому RecyclerView показывать первый элемент другого, но обычно не как первый элемент:
![введите описание изображения здесь]()
Примечание: на приведенных выше снимках экрана я прокрутил в нижней части RecyclerView, но аналогичный результат будет с верхним.
Ситуация становится намного хуже, если, как я уже писал, ячейки имеют разные размеры:
![введите описание изображения здесь]()
Что я пробовал
Я пытался использовать другие способы прокрутки и перехода в другие позиции, но все попытки не работают.
Использование smoothScrollToPosition сделало вещи еще хуже (хотя это кажется более приятным), потому что, если я брошу, в какой-то момент другой RecyclerView будет контролировать тот, с которым я взаимодействовал.
Я думаю, что я должен использовать направление прокрутки вместе с отображаемыми в данный момент элементами на другом RecyclerView.
Здесь текущий (примерный) код. Обратите внимание, что в реальном коде ячейки могут иметь одинаковые размеры (некоторые из них высокие, некоторые короткие, и т.д.). Одна из строк в коде делает ячейки разной высоты.
activity_main.xml
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/topReccyclerView" android:layout_width="0dp" android:layout_height="100dp"
android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp"
android:orientation="horizontal" app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" tools:listitem="@layout/horizontal_cell"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/bottomRecyclerView" android:layout_width="0dp" android:layout_height="0dp"
android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" app:layoutManager="android.support.v7.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/topReccyclerView"
tools:listitem="@layout/horizontal_cell"/>
</android.support.constraint.ConstraintLayout>
horizontal_cell.xml
<TextView
android:id="@+id/textView" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="100dp" android:layout_height="100dp"
android:gravity="center" tools:text="@tools:sample/lorem"/>
vertical_cell.xml
<TextView
android:id="@+id/textView" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="50dp"
android:gravity="center" tools:text="@tools:sample/lorem"/>
MainActivity
class MainActivity : AppCompatActivity() {
var masterView: View? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val inflater = LayoutInflater.from(this)
topReccyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder.itemView as TextView).text = position.toString()
holder.itemView.setBackgroundColor(if(position%2==0) 0xffff0000.toInt() else 0xff00ff00.toInt())
}
override fun getItemCount(): Int {
return 100
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
return object : RecyclerView.ViewHolder(inflater.inflate(R.layout.horizontal_cell, parent, false)) {}
}
}
bottomRecyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val baseHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, resources.displayMetrics).toInt()
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder.itemView as TextView).text = position.toString()
holder.itemView.setBackgroundColor(if(position%2==0) 0xffff0000.toInt() else 0xff00ff00.toInt())
// this makes the heights of the cells different from one another:
holder.itemView.layoutParams.height = baseHeight + (if (position % 3 == 0) 0 else baseHeight / (position % 3))
}
override fun getItemCount(): Int {
return 100
}
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
return object : RecyclerView.ViewHolder(inflater.inflate(R.layout.vertical_cell, parent, false)) {}
}
}
LinearSnapHelper().attachToRecyclerView(topReccyclerView)
LinearSnapHelper().attachToRecyclerView(bottomRecyclerView)
topReccyclerView.addOnScrollListener(OnScrollListener(topReccyclerView, bottomRecyclerView))
bottomRecyclerView.addOnScrollListener(OnScrollListener(bottomRecyclerView, topReccyclerView))
}
inner class OnScrollListener(private val thisRecyclerView: RecyclerView, private val otherRecyclerView: RecyclerView) : RecyclerView.OnScrollListener() {
var lastItemPos: Int = Int.MIN_VALUE
val thisRecyclerViewId = resources.getResourceEntryName(thisRecyclerView.id)
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
Log.d("AppLog", "onScrollStateChanged:$thisRecyclerViewId $newState")
when (newState) {
RecyclerView.SCROLL_STATE_DRAGGING -> if (masterView == null) {
Log.d("AppLog", "setting $thisRecyclerViewId to be master")
masterView = thisRecyclerView
}
RecyclerView.SCROLL_STATE_IDLE -> if (masterView == thisRecyclerView) {
Log.d("AppLog", "resetting $thisRecyclerViewId from being master")
masterView = null
lastItemPos = Int.MIN_VALUE
}
}
}
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if ((dx == 0 && dy == 0) || (masterView != null && masterView != thisRecyclerView))
return
// Log.d("AppLog", "onScrolled:$thisRecyclerView $dx-$dy")
val currentItem = (thisRecyclerView.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition()
if (lastItemPos == currentItem)
return
lastItemPos = currentItem
otherRecyclerView.scrollToPosition(currentItem)
// otherRecyclerView.smoothScrollToPosition(currentItem)
Log.d("AppLog", "currentItem:" + currentItem)
}
}
}
Вопросы
-
Как я могу позволить другому RecycerView всегда иметь первый элемент, такой же, как и текущий, который находится под управлением?
-
Как изменить код для поддержки плавной прокрутки, не вызывая проблемы с тем, что другой RecyclerView является тем, который контролирует?
РЕДАКТИРОВАТЬ: после обновления здесь кода примера с разными размерами ячеек (потому что изначально, что ближе к проблеме, которую я имею, как я описал ранее), я заметил, что привязка не работает хорошо.
Вот почему я решил использовать эту библиотеку для правильной привязки:
https://github.com/DevExchanges/SnappingRecyclerview
Итак, вместо LinearSnapHelper я использую "GravitySnapHelper". Кажется, что они работают лучше, но все еще имеют проблемы с синхронизацией и касаются во время прокрутки.
EDIT:
Я, наконец, исправил все проблемы синхронизации, и он отлично работает, даже если ячейки имеют разные размеры.
Все еще есть некоторые проблемы:
-
Если вы перейдете на один RecyclerView, а затем коснитесь другого, у него будет очень странное поведение прокрутки. Может прокручивать путь больше, чем нужно.
-
Прокрутка не является гладкой (при синхронизации и при переключении), поэтому она не выглядит хорошо.
-
К сожалению, из-за привязки (на самом деле это может понадобиться только для верхнего RecyclerView), это вызывает другую проблему: нижний RecyclerView может отображать последний элемент частично (снимок экрана со 100 элементами), и я могу " t прокрутите еще раз, чтобы показать его полностью:
![введите описание изображения здесь]()
Я даже не думаю, что нижний RecyclerView должен быть привязан, если только верхний не был затронут. К сожалению, это все, что я получил до сих пор, у меня нет проблем с синхронизацией.
Здесь новый код, после всех исправлений, которые я нашел:
class MainActivity : AppCompatActivity() {
var masterView: View? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val inflater = LayoutInflater.from(this)
topReccyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder.itemView as TextView).text = position.toString()
holder.itemView.setBackgroundColor(if (position % 2 == 0) 0xffff0000.toInt() else 0xff00ff00.toInt())
}
override fun getItemCount(): Int = 1000
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
return object : RecyclerView.ViewHolder(inflater.inflate(R.layout.horizontal_cell, parent, false)) {}
}
}
bottomRecyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val baseHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, resources.displayMetrics).toInt()
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder.itemView as TextView).text = position.toString()
holder.itemView.setBackgroundColor(if (position % 2 == 0) 0xffff0000.toInt() else 0xff00ff00.toInt())
holder.itemView.layoutParams.height = baseHeight + (if (position % 3 == 0) 0 else baseHeight / (position % 3))
}
override fun getItemCount(): Int = 1000
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
return object : RecyclerView.ViewHolder(inflater.inflate(R.layout.vertical_cell, parent, false)) {}
}
}
// GravitySnapHelper is available from : https://github.com/DevExchanges/SnappingRecyclerview
GravitySnapHelper(Gravity.START).attachToRecyclerView(topReccyclerView)
GravitySnapHelper(Gravity.TOP).attachToRecyclerView(bottomRecyclerView)
topReccyclerView.addOnScrollListener(OnScrollListener(topReccyclerView, bottomRecyclerView))
bottomRecyclerView.addOnScrollListener(OnScrollListener(bottomRecyclerView, topReccyclerView))
}
inner class OnScrollListener(private val thisRecyclerView: RecyclerView, private val otherRecyclerView: RecyclerView) : RecyclerView.OnScrollListener() {
var lastItemPos: Int = Int.MIN_VALUE
val thisRecyclerViewId = resources.getResourceEntryName(thisRecyclerView.id)
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
when (newState) {
RecyclerView.SCROLL_STATE_DRAGGING -> if (masterView == null) {
masterView = thisRecyclerView
}
RecyclerView.SCROLL_STATE_IDLE -> if (masterView == thisRecyclerView) {
masterView = null
lastItemPos = Int.MIN_VALUE
}
}
}
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dx == 0 && dy == 0 || masterView !== null && masterView !== thisRecyclerView) {
return
}
val otherLayoutManager = otherRecyclerView.layoutManager as LinearLayoutManager
val thisLayoutManager = thisRecyclerView.layoutManager as LinearLayoutManager
val currentItem = thisLayoutManager.findFirstCompletelyVisibleItemPosition()
if (lastItemPos == currentItem) {
return
}
lastItemPos = currentItem
otherLayoutManager.scrollToPositionWithOffset(currentItem, 0)
}
}
}
Ответы
Ответ 1
Объединяя два RecyclerView
s, есть четыре случая движения:
а. Прокрутка горизонтального ресайклера влево
б. Прокрутка вправо
с. Прокрутка вертикального ресайклера вверху
д. Прокрутка к нижней части
Случаи a и c не нужно заботиться, так как они работают из коробки. Для случаев b и d вам нужно сделать две вещи:
- Знайте, в каком ресайклере вы находитесь (вертикальный или горизонтальный), а в каком направлении прокрутка (вверх или вниз или влево или вправо) и
- вычислить смещение (элементов списка) из числа видимых элементов в
otherRecyclerView
(если экран больше, смещение также должно быть больше).
Полагая, что это было немного странно, но результат довольно прямолинейный.
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
if (masterView == otherRecyclerView) {
thisRecyclerView.stopScroll();
otherRecyclerView.stopScroll();
syncScroll(1, 1);
}
masterView = thisRecyclerView;
} else if (newState == RecyclerView.SCROLL_STATE_IDLE && masterView == thisRecyclerView) {
masterView = null;
}
}
@Override
public void onScrolled(RecyclerView recyclerview, int dx, int dy) {
super.onScrolled(recyclerview, dx, dy);
if ((dx == 0 && dy == 0) || (masterView != null && masterView != thisRecyclerView)) {
return;
}
syncScroll(dx, dy);
}
void syncScroll(int dx, int dy) {
LinearLayoutManager otherLayoutManager = (LinearLayoutManager) otherRecyclerView.getLayoutManager();
LinearLayoutManager thisLayoutManager = (LinearLayoutManager) thisRecyclerView.getLayoutManager();
int offset = 0;
if ((thisLayoutManager.getOrientation() == HORIZONTAL && dx > 0) || (thisLayoutManager.getOrientation() == VERTICAL && dy > 0)) {
// scrolling horizontal recycler to left or vertical recycler to bottom
offset = otherLayoutManager.findLastCompletelyVisibleItemPosition() - otherLayoutManager.findFirstCompletelyVisibleItemPosition();
}
int currentItem = thisLayoutManager.findFirstCompletelyVisibleItemPosition();
otherLayoutManager.scrollToPositionWithOffset(currentItem, offset);
}
Конечно, вы могли бы объединить два предложения if, поскольку тела одинаковы. Ради удобочитаемости я подумал, что хорошо держать их в стороне.
Вторая проблема заключалась в синхронизации, когда был затронут соответствующий "другой" ресайлер, когда "первый" ресайклеров все еще прокручивался. Здесь применим следующий код (включенный выше):
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
if (masterView == otherRecyclerView) {
thisRecyclerView.stopScroll();
otherRecyclerView.stopScroll();
syncScroll(1, 1);
}
masterView = thisRecyclerView;
}
newState
равно SCROLL_STATE_DRAGGING
, когда ресайклеров коснулся и немного потянул. Поэтому, если это касание (& drag) после касания соответствующего "другого" ресайклера, второе условие (masterView == otherRecyclerview)
истинно. Оба ресайклера останавливаются тогда, а "другой" повторитель синхронизируется с "this" одним.
Ответ 2
1-) Менеджер макетов
Текущая smoothScrollToPosition
не возвращает элемент вверх. Поэтому напишите новый менеджер макетов. И пусть переопределить этот менеджер макетов smoothScrollToPosition
.
public class TopLinearLayoutManager extends LinearLayoutManager
{
public TopLinearLayoutManager(Context context, int orientation)
{
//orientation : vertical or horizontal
super(context, orientation, false);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position)
{
RecyclerView.SmoothScroller smoothScroller = new TopSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private class TopSmoothScroller extends LinearSmoothScroller
{
TopSmoothScroller(Context context)
{
super(context);
}
@Override
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference)
{
return (boxStart - viewStart);
}
}
}
2-) Настройка
//horizontal one
RecyclerView rvMario = (RecyclerView) findViewById(R.id.rvMario);
//vertical one
RecyclerView rvLuigi = (RecyclerView) findViewById(R.id.rvLuigi);
final LinearLayoutManager managerMario = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.HORIZONTAL, false);
rvMario.setLayoutManager(managerMario);
ItemMarioAdapter adapterMario = new ItemMarioAdapter(itemList);
rvMario.setAdapter(adapterMario);
//Snap to start by using Ruben Sousa RecyclerViewSnap
SnapHelper snapHelper = new GravitySnapHelper(Gravity.START);
snapHelper.attachToRecyclerView(rvMario);
final TopLinearLayoutManager managerLuigi = new TopLinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL);
rvLuigi.setLayoutManager(managerLuigi);
ItemLuigiAdapter adapterLuigi = new ItemLuigiAdapter(itemList);
rvLuigi.setAdapter(adapterLuigi);
3-) Прокручивающий прослушиватель
rvMario.addOnScrollListener(new RecyclerView.OnScrollListener()
{
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
//get firstCompleteleyVisibleItemPosition
int firstCompleteleyVisibleItemPosition = managerMario.findFirstCompletelyVisibleItemPosition();
if (firstCompleteleyVisibleItemPosition >= 0)
{
//vertical one, smooth scroll to position
rvLuigi.smoothScrollToPosition(firstCompleteleyVisibleItemPosition);
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState)
{
super.onScrollStateChanged(recyclerView, newState);
}
});
4-) Выход
![введите описание изображения здесь]()
Ответ 3
Если у вас есть просмотр главного ресайклера (который будет получать действия прокрутки пользователя), вы можете использовать нижеприведенный код для реализации необходимой функциональности
masterRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int firstCompletelyVisiblePosition = layoutManager.findFirstVisibleItemPosition();
slaveRecyclerView.smoothScrollToPosition(pos);
}
});
Ответ 4
Основываясь на Бураке TopLinearLayoutManager
, но исправляя логику OnScrollListener
, мы, наконец, получаем гладкое и правильное привязку (горизонтальное RecyclerView
).
public class MainActivity extends AppCompatActivity {
View masterView = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
final LayoutInflater inflater = LayoutInflater.from(this);
final RecyclerView topRecyclerView = findViewById(R.id.topReccyclerView);
RecyclerView.Adapter adapterTop = new RecyclerView.Adapter<RecyclerView.ViewHolder>() {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(inflater.inflate(R.layout.horizontal_cell, parent, false));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((TextView) holder.itemView).setText(String.valueOf(position));
holder.itemView.setBackgroundColor(position % 2 == 0 ? Integer.valueOf(0xffff0000) : Integer.valueOf(0xff00ff00));
}
@Override
public int getItemCount() {
return 100;
}
class ViewHolder extends RecyclerView.ViewHolder {
final TextView textView;
ViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
}
}
};
topRecyclerView.setAdapter(adapterTop);
final RecyclerView bottomRecyclerView = findViewById(R.id.bottomRecyclerView);
RecyclerView.Adapter adapterBottom = new RecyclerView.Adapter() {
int baseHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50f, getResources().getDisplayMetrics());
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new ViewHolder(inflater.inflate(R.layout.vertical_cell, parent, false));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((TextView) holder.itemView).setText(String.valueOf(position));
holder.itemView.setBackgroundColor((position % 2 == 0) ? Integer.valueOf(0xffff0000) : Integer.valueOf(0xff00ff00));
holder.itemView.getLayoutParams().height = baseHeight + (position % 3 == 0 ? 0 : baseHeight / (position % 3));
}
@Override
public int getItemCount() {
return 100;
}
class ViewHolder extends RecyclerView.ViewHolder {
final TextView textView;
ViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
}
}
};
bottomRecyclerView.setAdapter(adapterBottom);
TopLinearLayoutManager topLayoutManager = new TopLinearLayoutManager(this, LinearLayoutManager.HORIZONTAL);
topRecyclerView.setLayoutManager(topLayoutManager);
TopLinearLayoutManager bottomLayoutManager = new TopLinearLayoutManager(this, LinearLayoutManager.VERTICAL);
bottomRecyclerView.setLayoutManager(bottomLayoutManager);
final OnScrollListener topOnScrollListener = new OnScrollListener(topRecyclerView, bottomRecyclerView);
final OnScrollListener bottomOnScrollListener = new OnScrollListener(bottomRecyclerView, topRecyclerView);
topRecyclerView.addOnScrollListener(topOnScrollListener);
bottomRecyclerView.addOnScrollListener(bottomOnScrollListener);
GravitySnapHelper snapHelperTop = new GravitySnapHelper(Gravity.START);
snapHelperTop.attachToRecyclerView(topRecyclerView);
}
class OnScrollListener extends RecyclerView.OnScrollListener {
private RecyclerView thisRecyclerView;
private RecyclerView otherRecyclerView;
int lastItemPos = Integer.MIN_VALUE;
OnScrollListener(RecyclerView thisRecyclerView, RecyclerView otherRecyclerView) {
this.thisRecyclerView = thisRecyclerView;
this.otherRecyclerView = otherRecyclerView;
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
masterView = thisRecyclerView;
} else if (newState == RecyclerView.SCROLL_STATE_IDLE && masterView == thisRecyclerView) {
masterView = null;
lastItemPos = Integer.MIN_VALUE;
}
}
@Override
public void onScrolled(RecyclerView recyclerview, int dx, int dy) {
super.onScrolled(recyclerview, dx, dy);
if ((dx == 0 && dy == 0) || (masterView != thisRecyclerView)) {
return;
}
int currentItem = ((TopLinearLayoutManager) thisRecyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
if (lastItemPos == currentItem) {
return;
}
lastItemPos = currentItem;
otherRecyclerView.getLayoutManager().smoothScrollToPosition(otherRecyclerView, null, currentItem);
}
}
}
Ответ 5
Еще одно простое решение, прекрасно работающее на моем устройстве
Переменная
RecyclerView horizontalRecyclerView, verticalRecyclerView;
LinearLayoutManager horizontalLayoutManager, verticalLayoutManager;
ArrayList<String> arrayList = new ArrayList<>();
ArrayList<String> arrayList2 = new ArrayList<>();
RecyclerView
код
horizontalRecyclerView = findViewById(R.id.horizontalRc);
verticalRecyclerView = findViewById(R.id.verticalRc);
horizontalRecyclerView.setHasFixedSize(true);
verticalRecyclerView.setHasFixedSize(true);
horizontalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
verticalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
horizontalRecyclerView.setLayoutManager(horizontalLayoutManager);
verticalRecyclerView.setLayoutManager(verticalLayoutManager);
for (int i = 0; i < 50; i++) {
arrayList.add("" + i);
arrayList2.add("" + i);
}
MyDataAdapter horizontalAdapter = new MyDataAdapter(this, arrayList);
MyDataAdapter verticalAdapter = new MyDataAdapter(this, arrayList2);
horizontalRecyclerView.setAdapter(horizontalAdapter);
verticalRecyclerView.setAdapter(verticalAdapter);
логика внутри RecyclerView.addOnScrollListener
horizontalRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int pos = horizontalLayoutManager.findFirstCompletelyVisibleItemPosition();
verticalLayoutManager.scrollToPositionWithOffset(pos, 20);
}
});
verticalRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int pos = verticalLayoutManager.findFirstCompletelyVisibleItemPosition();
horizontalLayoutManager.scrollToPositionWithOffset(pos, 20);
}
});
Надеюсь, что это поможет кому-то
ЦЕЛЬ
public class Main4Activity extends AppCompatActivity {
RecyclerView horizontalRecyclerView, verticalRecyclerView;
LinearLayoutManager horizontalLayoutManager, verticalLayoutManager;
ArrayList<String> arrayList = new ArrayList<>();
ArrayList<String> arrayList2 = new ArrayList<>();
boolean isVertical = true, isHorizontal = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
horizontalRecyclerView = findViewById(R.id.horizontalRc);
verticalRecyclerView = findViewById(R.id.verticalRc);
horizontalRecyclerView.setHasFixedSize(true);
verticalRecyclerView.setHasFixedSize(true);
horizontalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
verticalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
horizontalRecyclerView.setLayoutManager(horizontalLayoutManager);
verticalRecyclerView.setLayoutManager(verticalLayoutManager);
for (int i = 0; i < 50; i++) {
arrayList.add("" + i);
arrayList2.add("" + i);
}
MyDataAdapter horizontalAdapter = new MyDataAdapter(this, arrayList);
MyDataAdapter verticalAdapter = new MyDataAdapter(this, arrayList2);
horizontalRecyclerView.setAdapter(horizontalAdapter);
verticalRecyclerView.setAdapter(verticalAdapter);
horizontalRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int pos = horizontalLayoutManager.findFirstCompletelyVisibleItemPosition();
verticalLayoutManager.scrollToPositionWithOffset(pos, 20);
}
});
verticalRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int pos = verticalLayoutManager.findFirstCompletelyVisibleItemPosition();
horizontalLayoutManager.scrollToPositionWithOffset(pos, 20);
}
});
/* horizontalRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int pos = horizontalLayoutManager.findFirstCompletelyVisibleItemPosition();
verticalLayoutManager.scrollToPositionWithOffset(pos, 20);
*//*if (isHorizontal) {
int pos = horizontalLayoutManager.findFirstCompletelyVisibleItemPosition();
verticalLayoutManager.scrollToPositionWithOffset(pos, 20);
Log.e("isHorizontal", "TRUE");
isVertical = false;
} else {
isHorizontal = true;
}*//*
}
*//* @Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
isVertical = true;
}*//*
});
verticalRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
*//* @Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
isHorizontal = true;
}
*//*
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int pos = verticalLayoutManager.findFirstCompletelyVisibleItemPosition();
horizontalLayoutManager.scrollToPositionWithOffset(pos, 20);
*//* if (isVertical) {
int pos = verticalLayoutManager.findFirstCompletelyVisibleItemPosition();
horizontalLayoutManager.scrollToPositionWithOffset(pos, 20);
Log.e("isVertical", "TRUE");
isHorizontal = false;
} else {
isVertical = true;
}*//*
}
});*/
}
}
Код адаптера
public class MyDataAdapter extends RecyclerView.Adapter<MyDataAdapter.ViewHolder> {
Context context;
ArrayList<String> arrayList;
public MyDataAdapter(Context context, ArrayList<String> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
@Override
public MyDataAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.temp, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(MyDataAdapter.ViewHolder holder, int position) {
if (position % 2 == 0) {
holder.tvNumber.setBackgroundResource(R.color.colorGreen);
} else {
holder.tvNumber.setBackgroundResource(R.color.colorRed);
}
holder.tvNumber.setText(arrayList.get(position));
}
@Override
public int getItemCount() {
return arrayList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView tvNumber;
public ViewHolder(View itemView) {
super(itemView);
tvNumber = itemView.findViewById(R.id.tvNumber);
}
}
}
Макет действий
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/horizontalRc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
android:id="@+id/verticalRc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:visibility="gone" />
</LinearLayout>
temp Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="40dp">
<TextView
android:id="@+id/tvNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="50dp" />
</LinearLayout>
COLOR
<color name="colorGreen">#307832</color>
<color name="colorRed">#ff4c4c</color>