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

View File

@ -1,14 +1,27 @@
package de.spiritcroc.recyclerview.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
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
@ -22,31 +35,31 @@ public abstract class LayoutManager extends RecyclerView.LayoutManager {
new ViewBoundsCheck.Callback() {
@Override
public View getChildAt(int index) {
return LayoutManager.this.getChildAt(index);
return LinearLayoutManager.this.getChildAt(index);
}
@Override
public int getParentStart() {
return LayoutManager.this.getPaddingLeft();
return LinearLayoutManager.this.getPaddingLeft();
}
@Override
public int getParentEnd() {
return LayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
return LinearLayoutManager.this.getWidth() - LinearLayoutManager.this.getPaddingRight();
}
@Override
public int getChildStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return LayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
return LinearLayoutManager.this.getDecoratedLeft(view) - params.leftMargin;
}
@Override
public int getChildEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
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() {
@Override
public View getChildAt(int index) {
return LayoutManager.this.getChildAt(index);
return LinearLayoutManager.this.getChildAt(index);
}
@Override
public int getParentStart() {
return LayoutManager.this.getPaddingTop();
return LinearLayoutManager.this.getPaddingTop();
}
@Override
public int getParentEnd() {
return LayoutManager.this.getHeight()
- LayoutManager.this.getPaddingBottom();
return LinearLayoutManager.this.getHeight()
- LinearLayoutManager.this.getPaddingBottom();
}
@Override
public int getChildStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return LayoutManager.this.getDecoratedTop(view) - params.topMargin;
return LinearLayoutManager.this.getDecoratedTop(view) - params.topMargin;
}
@Override
public int getChildEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
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.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager
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.widget.textChanges
import com.vanniktech.emoji.EmojiPopup
import de.spiritcroc.recyclerview.widget.BetterLinearLayoutManager
import im.vector.app.R
import im.vector.app.core.dialogs.ConfirmationDialogBuilder
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
@ -311,7 +311,7 @@ class RoomDetailFragment @Inject constructor(
private lateinit var sharedActionViewModel: MessageSharedActionViewModel
private lateinit var knownCallsViewModel: SharedKnownCallsViewModel
private lateinit var layoutManager: LinearLayoutManager
private lateinit var layoutManager: BetterLinearLayoutManager
private lateinit var jumpToBottomViewVisibilityManager: JumpToBottomViewVisibilityManager
private var modelBuildListener: OnModelBuildFinishedListener? = null
@ -1219,7 +1219,7 @@ class RoomDetailFragment @Inject constructor(
timelineEventController.timeline = roomDetailViewModel.timeline
views.timelineRecyclerView.trackItemsVisibilityChange()
layoutManager = object : LinearLayoutManager(context, RecyclerView.VERTICAL, true) {
layoutManager = object : BetterLinearLayoutManager(context, RecyclerView.VERTICAL, true) {
override fun onLayoutCompleted(state: RecyclerView.State?) {
super.onLayoutCompleted(state)
updateJumpToReadMarkerViewVisibility()