'disable the scroll effect of the action list

I've got a GuidedStepSupportFragment fragment like this.

public class SampleStepFragment extends GuidedStepSupportFragment {

    @NonNull
    @Override
    public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
        String title = "Title";
        String breadcrumb = "Breadcrumb";
        String description = "Description";
        Drawable icon = getActivity().getDrawable(R.drawable.ic_videocam_black_24dp);

        return new GuidanceStylist.Guidance(title, description, breadcrumb, icon);
    }

    @Override
    public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {

        addAction(actions, ACTION_CONTINUE, "Action1");
        addAction(actions, ACTION_BACK, "Action2");

    }
}

action1

Problem: When I scroll the action list, it shows like this;

action1

But I want to something like this;

expected

How can I disable this effect on my action list?

Thanks



Solution 1:[1]

I managed it and it wasn't easy to figure out.

There's no supported way of doing it, since the APIs that actually make this possible are package private or hidden from public use on purpose. (You can do it yourself, but you just end up copying classes from the leanback libraries.)

The solution:

@Override
public void onCreateActions(@NonNull List<GuidedAction> actions, Bundle savedInstanceState) {

    addAction(actions, GuidedAction.ACTION_ID_CONTINUE, "Action1");
    addAction(actions, GuidedAction.ACTION_ID_CANCEL, "Action2");

    // Run code delayed on mainThread (any other/better method can/should be used)
    // It's delayed because if focus scroll is disabled, the list will stick to the top of the layout
    new Handler(Looper.getMainLooper()).postDelayed(this::disableFocusScroll, 500);
}

private void disableFocusScroll() {
    RecyclerView.LayoutManager layoutManager = SampleStepFragment.this.getGuidedActionsStylist().getActionsGridView().getLayoutManager();
    try {
        Method method = layoutManager.getClass().getMethod("setFocusScrollStrategy", int.class);
        method.invoke(layoutManager, 1 /* FOCUS_SCROLL_ITEM */);
    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        Log.e(TAG, "disableFocusScroll: ", e);
    }
}

Full example

The explanation:

A GuidedStepSupportFragment requests a GuidedActionsStylist which is responsible for rendering the list items on the right side. source

The GuidedActionsStylist stylist inflates the layout lb_guidedactions.xml which contains a VerticalGridView source

The VerticalGridView extends BaseGridView and creates a GridLayoutManager as its layout manager. This GridLayoutManager is sadly package private and final... (android why..?). It has the method setFocusScrollStrategy which is used to determine how scrolling behaves.source

See the different focus scroll strategies:

/**
 * Always keep focused item at a aligned position.  Developer can use
 * WINDOW_ALIGN_XXX and ITEM_ALIGN_XXX to define how focused item is aligned.
 * In this mode, the last focused position will be remembered and restored when focus
 * is back to the view.
 * @hide
 */
@RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_ALIGNED = 0;

/**
 * Scroll to make the focused item inside client area.
 * @hide
 */
@RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_ITEM = 1;

/**
 * Scroll a page of items when focusing to item outside the client area.
 * The page size matches the client area size of RecyclerView.
 * @hide
 */
@RestrictTo(LIBRARY_GROUP)
public final static int FOCUS_SCROLL_PAGE = 2;

So since the API is hidden, we just use reflection to expose the setFocusScrollStrategy method and set it to FOCUS_SCROLL_ITEM.

We can't do this immediately though, since without the default scroll setting, the list items will pop to the top of the layout and won't stay centered. So I added a delay of 500ms which is horrible... If you manage to find out when it's best to trigger this, let me know.

Solution 2:[2]

It turns out there is a much more elegant and simpler solution. It is enough to add to the body of onViewCreated in the class that inherits guideStepSupportFragment windowAlignment option (there are other alignment options):

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 
    // ... 
    val gridView = guidedActionsStylist.actionsGridView 
    gridView.windowAlignment = VerticalGridView.WINDOW_ALIGN_BOTH_EDGE 
    // ... 
    super.onViewCreated(view, savedInstanceState) 
} 

Solution 3:[3]

Here is a simple way to disable VerticalGridView scrolling

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    VerticalGridView gridView = getGuidedActionsStylist().getActionsGridView();
    gridView.setScrollEnabled(false);
    super.onViewCreated(view, savedInstanceState);
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 XJIOP