zhpanvip / BannerViewPager

🚀 An awesome banner view for Android,Based on ViewPager2. 这可能是全网最好用的ViewPager轮播图。简单、高效,一行代码实现循环轮播,一屏三页任意变,指示器样式任你挑。
Apache License 2.0
3.62k stars 439 forks source link

setScrollDuration 会导致 offscreenPageLimit 失效 #145

Closed YuS1aN closed 4 years ago

YuS1aN commented 4 years ago

原因应该是把 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 commented 4 years ago

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

zhpanvip commented 4 years ago

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

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

YuS1aN commented 4 years ago

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

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

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

zhpanvip commented 4 years ago

原因应该是把 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 commented 4 years ago

Fixed in v3.1.6