Use BetterLinearLayoutManager for room detail view

Epoxy wants this to be an androidx LinearLayoutManager, or it will crash
on scroll with:

E AndroidRuntime: java.lang.ClassCastException: im.vector.app.features.home.room.detail.RoomDetailFragment$setupRecyclerView$1 cannot be cast to androidx.recyclerview.widget.LinearLayoutManager
E AndroidRuntime: 	at com.airbnb.epoxy.preload.EpoxyPreloader.onScrolled(EpoxyPreloader.kt:107)
E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView.dispatchOnScrolled(RecyclerView.java:5347)
E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView.scrollByInternal(RecyclerView.java:2102)
E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView.onTouchEvent(RecyclerView.java:3531)
E AndroidRuntime: 	at android.view.View.dispatchTouchEvent(View.java:14309)
[...]

Change-Id: Iaf75f07d4309eb5bf0e58eecd6797ef33522acdd
This commit is contained in:
SpiritCroc 2021-10-27 16:51:24 +02:00
parent a321d6a79a
commit 2d63dbbf12
3 changed files with 47 additions and 18 deletions

View File

@ -47,7 +47,7 @@ import java.util.List;
* A {@link RecyclerView.LayoutManager} implementation which provides * A {@link RecyclerView.LayoutManager} implementation which provides
* similar functionality to {@link android.widget.ListView}. * similar functionality to {@link android.widget.ListView}.
*/ */
public class BetterLinearLayoutManager extends LayoutManager implements public class BetterLinearLayoutManager extends LinearLayoutManager implements
ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider { ItemTouchHelper.ViewDropHandler, RecyclerView.SmoothScroller.ScrollVectorProvider {
private static final String TAG = "LinearLayoutManager"; private static final String TAG = "LinearLayoutManager";
@ -179,6 +179,7 @@ public class BetterLinearLayoutManager extends LayoutManager implements
*/ */
public BetterLinearLayoutManager(Context context, @RecyclerView.Orientation int orientation, public BetterLinearLayoutManager(Context context, @RecyclerView.Orientation int orientation,
boolean reverseLayout) { boolean reverseLayout) {
super(context, orientation, reverseLayout);
setOrientation(orientation); setOrientation(orientation);
setReverseLayout(reverseLayout); setReverseLayout(reverseLayout);
} }
@ -193,6 +194,7 @@ public class BetterLinearLayoutManager extends LayoutManager implements
*/ */
public BetterLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, public BetterLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) { int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes); Properties properties = getProperties(context, attrs, defStyleAttr, defStyleRes);
setOrientation(properties.orientation); setOrientation(properties.orientation);
setReverseLayout(properties.reverseLayout); setReverseLayout(properties.reverseLayout);
@ -322,6 +324,11 @@ public class BetterLinearLayoutManager extends LayoutManager implements
* Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)} * Compatibility support for {@link android.widget.AbsListView#setStackFromBottom(boolean)}
*/ */
public void setStackFromEnd(boolean stackFromEnd) { public void setStackFromEnd(boolean stackFromEnd) {
// mAnchorInfo can be uninitialized during super construction execution. Avoid duplicate execution
if (mAnchorInfo == null) {
return;
}
assertNotInLayoutOrScroll(null); assertNotInLayoutOrScroll(null);
if (mStackFromEnd == stackFromEnd) { if (mStackFromEnd == stackFromEnd) {
return; return;
@ -352,6 +359,11 @@ public class BetterLinearLayoutManager extends LayoutManager implements
* @param orientation {@link #HORIZONTAL} or {@link #VERTICAL} * @param orientation {@link #HORIZONTAL} or {@link #VERTICAL}
*/ */
public void setOrientation(@RecyclerView.Orientation int orientation) { public void setOrientation(@RecyclerView.Orientation int orientation) {
// mAnchorInfo can be uninitialized during super construction execution. Avoid duplicate execution
if (mAnchorInfo == null) {
return;
}
if (orientation != HORIZONTAL && orientation != VERTICAL) { if (orientation != HORIZONTAL && orientation != VERTICAL) {
throw new IllegalArgumentException("invalid orientation:" + orientation); throw new IllegalArgumentException("invalid orientation:" + orientation);
} }
@ -406,6 +418,10 @@ public class BetterLinearLayoutManager extends LayoutManager implements
* {@link #setStackFromEnd(boolean)} * {@link #setStackFromEnd(boolean)}
*/ */
public void setReverseLayout(boolean reverseLayout) { public void setReverseLayout(boolean reverseLayout) {
// mAnchorInfo can be uninitialized during super construction execution. Avoid duplicate execution
if (mAnchorInfo == null) {
return;
}
assertNotInLayoutOrScroll(null); assertNotInLayoutOrScroll(null);
if (reverseLayout == mReverseLayout) { if (reverseLayout == mReverseLayout) {
return; return;
@ -2604,4 +2620,4 @@ public class BetterLinearLayoutManager extends LayoutManager implements
mFocusable = false; mFocusable = false;
} }
} }
} }

View File

@ -1,14 +1,27 @@
package de.spiritcroc.recyclerview.widget; package de.spiritcroc.recyclerview.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View; import android.view.View;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
/** /**
* Exposing/replicating some internal functions from RecylerView.LayoutManager * Exposing/replicating some internal functions/attributes from RecylerView.LayoutManager
*/ */
public abstract class LayoutManager extends RecyclerView.LayoutManager { public abstract class LinearLayoutManager extends androidx.recyclerview.widget.LinearLayoutManager {
public LinearLayoutManager(Context context) {
super(context);
}
public LinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/* /*
* Exposed things from RecyclerView.java * Exposed things from RecyclerView.java
@ -22,31 +35,31 @@ public abstract class LayoutManager extends RecyclerView.LayoutManager {
new ViewBoundsCheck.Callback() { new ViewBoundsCheck.Callback() {
@Override @Override
public View getChildAt(int index) { public View getChildAt(int index) {
return LayoutManager.this.getChildAt(index); return LinearLayoutManager.this.getChildAt(index);
} }
@Override @Override
public int getParentStart() { public int getParentStart() {
return LayoutManager.this.getPaddingLeft(); return LinearLayoutManager.this.getPaddingLeft();
} }
@Override @Override
public int getParentEnd() { public int getParentEnd() {
return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight(); return LinearLayoutManager.this.getWidth() - LinearLayoutManager.this.getPaddingRight();
} }
@Override @Override
public int getChildStart(View view) { public int getChildStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams(); view.getLayoutParams();
return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin; return LinearLayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
} }
@Override @Override
public int getChildEnd(View view) { public int getChildEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams(); view.getLayoutParams();
return LayoutManager.this.getDecoratedRight(view) + params.rightMargin; return LinearLayoutManager.this.getDecoratedRight(view) + params.rightMargin;
} }
}; };
@ -58,32 +71,32 @@ public abstract class LayoutManager extends RecyclerView.LayoutManager {
new ViewBoundsCheck.Callback() { new ViewBoundsCheck.Callback() {
@Override @Override
public View getChildAt(int index) { public View getChildAt(int index) {
return LayoutManager.this.getChildAt(index); return LinearLayoutManager.this.getChildAt(index);
} }
@Override @Override
public int getParentStart() { public int getParentStart() {
return LayoutManager.this.getPaddingTop(); return LinearLayoutManager.this.getPaddingTop();
} }
@Override @Override
public int getParentEnd() { public int getParentEnd() {
return LayoutManager.this.getHeight() return LinearLayoutManager.this.getHeight()
- LayoutManager.this.getPaddingBottom(); - LinearLayoutManager.this.getPaddingBottom();
} }
@Override @Override
public int getChildStart(View view) { public int getChildStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams(); view.getLayoutParams();
return LayoutManager.this.getDecoratedTop(view) - params.topMargin; return LinearLayoutManager.this.getDecoratedTop(view) - params.topMargin;
} }
@Override @Override
public int getChildEnd(View view) { public int getChildEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams(); view.getLayoutParams();
return LayoutManager.this.getDecoratedBottom(view) + params.bottomMargin; return LinearLayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
} }
}; };

View File

@ -56,7 +56,6 @@ import androidx.core.view.isVisible
import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import com.airbnb.epoxy.EpoxyModel import com.airbnb.epoxy.EpoxyModel
@ -71,6 +70,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.jakewharton.rxbinding3.view.focusChanges import com.jakewharton.rxbinding3.view.focusChanges
import com.jakewharton.rxbinding3.widget.textChanges import com.jakewharton.rxbinding3.widget.textChanges
import com.vanniktech.emoji.EmojiPopup import com.vanniktech.emoji.EmojiPopup
import de.spiritcroc.recyclerview.widget.BetterLinearLayoutManager
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.dialogs.ConfirmationDialogBuilder import im.vector.app.core.dialogs.ConfirmationDialogBuilder
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
@ -311,7 +311,7 @@ class RoomDetailFragment @Inject constructor(
private lateinit var sharedActionViewModel: MessageSharedActionViewModel private lateinit var sharedActionViewModel: MessageSharedActionViewModel
private lateinit var knownCallsViewModel: SharedKnownCallsViewModel private lateinit var knownCallsViewModel: SharedKnownCallsViewModel
private lateinit var layoutManager: LinearLayoutManager private lateinit var layoutManager: BetterLinearLayoutManager
private lateinit var jumpToBottomViewVisibilityManager: JumpToBottomViewVisibilityManager private lateinit var jumpToBottomViewVisibilityManager: JumpToBottomViewVisibilityManager
private var modelBuildListener: OnModelBuildFinishedListener? = null private var modelBuildListener: OnModelBuildFinishedListener? = null
@ -1219,7 +1219,7 @@ class RoomDetailFragment @Inject constructor(
timelineEventController.timeline = roomDetailViewModel.timeline timelineEventController.timeline = roomDetailViewModel.timeline
views.timelineRecyclerView.trackItemsVisibilityChange() views.timelineRecyclerView.trackItemsVisibilityChange()
layoutManager = object : LinearLayoutManager(context, RecyclerView.VERTICAL, true) { layoutManager = object : BetterLinearLayoutManager(context, RecyclerView.VERTICAL, true) {
override fun onLayoutCompleted(state: RecyclerView.State?) { override fun onLayoutCompleted(state: RecyclerView.State?) {
super.onLayoutCompleted(state) super.onLayoutCompleted(state)
updateJumpToReadMarkerViewVisibility() updateJumpToReadMarkerViewVisibility()