Added OverscrollLinearLayoutManager to fix scrolling in AppDetails2

This commit is contained in:
mvp76 2017-03-21 10:09:56 +01:00
parent 343e91280a
commit 03a8151875
2 changed files with 128 additions and 1 deletions

View File

@ -13,6 +13,7 @@ import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog;
@ -46,6 +47,7 @@ import org.fdroid.fdroid.installer.InstallerService;
import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderService;
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
import org.fdroid.fdroid.views.OverscrollLinearLayoutManager;
import org.fdroid.fdroid.views.ShareChooserDialog;
import org.fdroid.fdroid.views.apps.FeatureImage;
@ -59,6 +61,8 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog
private FDroidApp fdroidApp;
private App app;
private CoordinatorLayout coordinatorLayout;
private AppBarLayout appBarLayout;
private RecyclerView recyclerView;
private AppDetailsRecyclerViewAdapter adapter;
private LocalBroadcastManager localBroadcastManager;
@ -91,10 +95,42 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog
localBroadcastManager = LocalBroadcastManager.getInstance(this);
coordinatorLayout = (CoordinatorLayout) findViewById(R.id.rootCoordinator);
appBarLayout = (AppBarLayout) coordinatorLayout.findViewById(R.id.app_bar);
recyclerView = (RecyclerView) findViewById(R.id.rvDetails);
adapter = new AppDetailsRecyclerViewAdapter(this, app, this);
LinearLayoutManager lm = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
OverscrollLinearLayoutManager lm = new OverscrollLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
lm.setStackFromEnd(false);
/** The recyclerView/AppBarLayout combo has a bug that prevents a "fling" from the bottom
* to continue all the way to the top by expanding the AppBarLayout. It will instead stop
* with the app bar in a collapsed state. See here: https://code.google.com/p/android/issues/detail?id=177729
* Not sure this is the exact issue, but it is true that while in a fling the RecyclerView will
* consume the scroll events quietly, without calling the nested scrolling mechanism.
* We fix this behavior by using an OverscrollLinearLayoutManager that will give us information
* of overscroll, i.e. when we have not consumed all of a scroll event, and use this information
* to send the scroll to the app bar layout so that it will expand itself.
*/
lm.setOnOverscrollListener(new OverscrollLinearLayoutManager.OnOverscrollListener() {
@Override
public int onOverscrollX(int overscroll) {
return 0;
}
@Override
public int onOverscrollY(int overscroll) {
int consumed = 0;
if (overscroll < 0) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
CoordinatorLayout.Behavior behavior = lp.getBehavior();
if (behavior != null && behavior instanceof AppBarLayout.Behavior) {
((AppBarLayout.Behavior) behavior).onNestedScroll(coordinatorLayout, appBarLayout, recyclerView, 0, 0, 0, overscroll);
consumed = overscroll; // Consume all of it!
}
}
return consumed;
}
});
recyclerView.setLayoutManager(lm);
recyclerView.setAdapter(adapter);

View File

@ -0,0 +1,91 @@
package org.fdroid.fdroid.views;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
/**
* This class is like a standard LinearLayoutManager but with an option to add an
* overscroll listener. This can be used to consume overscrolls, e.g. to draw custom
* "glows".
*/
public class OverscrollLinearLayoutManager extends LinearLayoutManager {
/**
* A listener interface to get overscroll infromation.
*/
public interface OnOverscrollListener {
/**
* Notifies the listener that an overscroll has happened in the x direction.
* @param overscroll If negative, the recycler view has been scrolled to the "start"
* position. If positive to the "end" position.
* @return Return the amount of overscroll consumed. Returning 0 will let the
* recycler view handle this in the default way. Return "overscroll" to consume the
* whole event.
*/
int onOverscrollX(int overscroll);
/**
* Notifies the listener that an overscroll has happened in the y direction.
* @param overscroll If negative, the recycler view has been scrolled to the "top"
* position. If positive to the "bottom" position.
* @return Return the amount of overscroll consumed. Returning 0 will let the
* recycler view handle this in the default way. Return "overscroll" to consume the
* whole event.
*/
int onOverscrollY(int overscroll);
}
private OnOverscrollListener overscrollListener = null;
public OverscrollLinearLayoutManager(Context context) {
super(context);
}
public OverscrollLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public OverscrollLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
/**
* Set the {@link OverscrollLinearLayoutManager.OnOverscrollListener} to get information about
* when the parent recyclerview is overscrolled.
*
* @param listener Listener to add
* @see OverscrollLinearLayoutManager.OnOverscrollListener
*/
public void setOnOverscrollListener(OnOverscrollListener listener) {
overscrollListener = listener;
}
@Override
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
int consumed = super.scrollHorizontallyBy(dx, recycler, state);
int overscrollX = dx - consumed;
if (overscrollX != 0) {
if (overscrollListener != null) {
int consumedByListener = overscrollListener.onOverscrollX(overscrollX);
consumed += consumedByListener;
}
}
return consumed;
}
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
int consumed = super.scrollVerticallyBy(dy, recycler, state);
int overscrollY = dy - consumed;
if (overscrollY != 0) {
if (overscrollListener != null) {
int consumedByListener = overscrollListener.onOverscrollY(overscrollY);
consumed += consumedByListener;
}
}
return consumed;
}
}