Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setScrollDuration 会导致 offscreenPageLimit 失效 #145

Closed
YuS1aN opened this issue Jul 24, 2020 · 5 comments
Closed

setScrollDuration 会导致 offscreenPageLimit 失效 #145

YuS1aN opened this issue Jul 24, 2020 · 5 comments
Assignees
Labels

Comments

@YuS1aN
Copy link

YuS1aN commented Jul 24, 2020

原因应该是把 ViewPager2LinearLayoutManagerImpl 内部类替换成了 ScrollDurationManger ,导致几个复写方法丢失。

目前我的解决办法是改成装饰器模式。

public class ScrollDurationManager2 extends LinearLayoutManager {
    private LinearLayoutManager mParent;
    private int scrollDuration;

    public ScrollDurationManager2(ViewPager2 viewPager2, int scrollDuration, LinearLayoutManager linearLayoutManager) {
        super(viewPager2.getContext(), linearLayoutManager.getOrientation(), false);
        this.scrollDuration = scrollDuration;
        mParent = linearLayoutManager;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
            @Override
            protected int calculateTimeForDeceleration(int dx) {
                return scrollDuration;
            }
        };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }

    @Override
    public boolean performAccessibilityAction(@NonNull RecyclerView.Recycler recycler,
                                              @NonNull RecyclerView.State state, int action, @Nullable Bundle args) {
        return mParent.performAccessibilityAction(recycler, state, action, args);
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler,
                                                  @NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) {
        mParent.onInitializeAccessibilityNodeInfo(recycler, state, info);
    }

    @Override
    protected void calculateExtraLayoutSpace(@NonNull RecyclerView.State state,
                                             @NonNull int[] extraLayoutSpace) {
        Method method = ReflectionUtils.getAccessibleMethod(mParent, "calculateExtraLayoutSpace", state.getClass(), extraLayoutSpace.getClass());
        try {
            if (method != null) method.invoke(mParent, state, extraLayoutSpace);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
                                                 @NonNull View child, @NonNull Rect rect, boolean immediate,
                                                 boolean focusedChildVisible) {
        return false; // users should use setCurrentItem instead
    }

    public static void reflectLayoutManager(ViewPager2 viewPager2, int scrollDuration) {
        try {
            RecyclerView recyclerView = (RecyclerView) viewPager2.getChildAt(0);
            recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
            ScrollDurationManager2 speedManger = new ScrollDurationManager2(viewPager2, scrollDuration, linearLayoutManager);
            recyclerView.setLayoutManager(speedManger);

            Field mRecyclerView = RecyclerView.LayoutManager.class.getDeclaredField("mRecyclerView");
            mRecyclerView.setAccessible(true);
            mRecyclerView.set(linearLayoutManager, recyclerView);

            Field layoutMangerField = ViewPager2.class.getDeclaredField("mLayoutManager");
            layoutMangerField.setAccessible(true);
            layoutMangerField.set(viewPager2, speedManger);

            Field pageTransformerAdapterField = ViewPager2.class.getDeclaredField("mPageTransformerAdapter");
            pageTransformerAdapterField.setAccessible(true);
            Object mPageTransformerAdapter = pageTransformerAdapterField.get(viewPager2);
            if (mPageTransformerAdapter != null) {
                Class<?> aClass = mPageTransformerAdapter.getClass();
                Field layoutManager = aClass.getDeclaredField("mLayoutManager");
                layoutManager.setAccessible(true);
                layoutManager.set(mPageTransformerAdapter, speedManger);
            }
            Field scrollEventAdapterField = ViewPager2.class.getDeclaredField("mScrollEventAdapter");
            scrollEventAdapterField.setAccessible(true);
            Object mScrollEventAdapter = scrollEventAdapterField.get(viewPager2);
            if (mScrollEventAdapter != null) {
                Class<?> aClass = mScrollEventAdapter.getClass();
                Field layoutManager = aClass.getDeclaredField("mLayoutManager");
                layoutManager.setAccessible(true);
                layoutManager.set(mScrollEventAdapter, speedManger);
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
@YuS1aN
Copy link
Author

YuS1aN commented Jul 24, 2020

顺便希望后续能支持导入自定义Transformer,可以更方便自定义动画效果。

@zhpanvip
Copy link
Owner

顺便希望后续能支持导入自定义Transformer,可以更方便自定义动画效果。

自定义Transforme很早就已经支持的。

@YuS1aN
Copy link
Author

YuS1aN commented Jul 28, 2020

顺便希望后续能支持导入自定义Transformer,可以更方便自定义动画效果。

自定义Transforme很早就已经支持的。

好的,多谢提醒。第一次用文档没看仔细,还写了一堆反射去改动画。。

@zhpanvip zhpanvip added the bug label Aug 20, 2020
zhpanvip added a commit that referenced this issue Aug 20, 2020
@zhpanvip
Copy link
Owner

原因应该是把 ViewPager2LinearLayoutManagerImpl 内部类替换成了 ScrollDurationManger ,导致几个复写方法丢失。

目前我的解决办法是改成装饰器模式。

public class ScrollDurationManager2 extends LinearLayoutManager {
    private LinearLayoutManager mParent;
    private int scrollDuration;

    public ScrollDurationManager2(ViewPager2 viewPager2, int scrollDuration, LinearLayoutManager linearLayoutManager) {
        super(viewPager2.getContext(), linearLayoutManager.getOrientation(), false);
        this.scrollDuration = scrollDuration;
        mParent = linearLayoutManager;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
            @Override
            protected int calculateTimeForDeceleration(int dx) {
                return scrollDuration;
            }
        };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }

    @Override
    public boolean performAccessibilityAction(@NonNull RecyclerView.Recycler recycler,
                                              @NonNull RecyclerView.State state, int action, @Nullable Bundle args) {
        return mParent.performAccessibilityAction(recycler, state, action, args);
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(@NonNull RecyclerView.Recycler recycler,
                                                  @NonNull RecyclerView.State state, @NonNull AccessibilityNodeInfoCompat info) {
        mParent.onInitializeAccessibilityNodeInfo(recycler, state, info);
    }

    @Override
    protected void calculateExtraLayoutSpace(@NonNull RecyclerView.State state,
                                             @NonNull int[] extraLayoutSpace) {
        Method method = ReflectionUtils.getAccessibleMethod(mParent, "calculateExtraLayoutSpace", state.getClass(), extraLayoutSpace.getClass());
        try {
            if (method != null) method.invoke(mParent, state, extraLayoutSpace);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean requestChildRectangleOnScreen(@NonNull RecyclerView parent,
                                                 @NonNull View child, @NonNull Rect rect, boolean immediate,
                                                 boolean focusedChildVisible) {
        return false; // users should use setCurrentItem instead
    }

    public static void reflectLayoutManager(ViewPager2 viewPager2, int scrollDuration) {
        try {
            RecyclerView recyclerView = (RecyclerView) viewPager2.getChildAt(0);
            recyclerView.setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
            ScrollDurationManager2 speedManger = new ScrollDurationManager2(viewPager2, scrollDuration, linearLayoutManager);
            recyclerView.setLayoutManager(speedManger);

            Field mRecyclerView = RecyclerView.LayoutManager.class.getDeclaredField("mRecyclerView");
            mRecyclerView.setAccessible(true);
            mRecyclerView.set(linearLayoutManager, recyclerView);

            Field layoutMangerField = ViewPager2.class.getDeclaredField("mLayoutManager");
            layoutMangerField.setAccessible(true);
            layoutMangerField.set(viewPager2, speedManger);

            Field pageTransformerAdapterField = ViewPager2.class.getDeclaredField("mPageTransformerAdapter");
            pageTransformerAdapterField.setAccessible(true);
            Object mPageTransformerAdapter = pageTransformerAdapterField.get(viewPager2);
            if (mPageTransformerAdapter != null) {
                Class<?> aClass = mPageTransformerAdapter.getClass();
                Field layoutManager = aClass.getDeclaredField("mLayoutManager");
                layoutManager.setAccessible(true);
                layoutManager.set(mPageTransformerAdapter, speedManger);
            }
            Field scrollEventAdapterField = ViewPager2.class.getDeclaredField("mScrollEventAdapter");
            scrollEventAdapterField.setAccessible(true);
            Object mScrollEventAdapter = scrollEventAdapterField.get(viewPager2);
            if (mScrollEventAdapter != null) {
                Class<?> aClass = mScrollEventAdapter.getClass();
                Field layoutManager = aClass.getDeclaredField("mLayoutManager");
                layoutManager.setAccessible(true);
                layoutManager.set(mScrollEventAdapter, speedManger);
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

感谢您提供的解决方案,该问题会在下个版本修复。

@zhpanvip zhpanvip self-assigned this Aug 20, 2020
zhpanvip added a commit that referenced this issue Aug 21, 2020
@zhpanvip
Copy link
Owner

Fixed in v3.1.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants