youth5201314 / banner

🔥🔥🔥Banner 2.0 来了!Android广告图片轮播控件,内部基于ViewPager2实现,Indicator和UI都可以自定义。
Apache License 2.0
12.92k stars 2.52k forks source link

ViewPager2->Fragment->Banner,Banner滑动到底后无法滑动ViewPager2 #1057

Closed junixapp closed 10 months ago

junixapp commented 3 years ago

场景是:ViewPager2->Fragment->Banner

问题:Banner滑动到底后无法滑动ViewPager2,希望能继续滑动外层的ViewPager2。

Chensp171254 commented 3 years ago

同问,TabLayout+ViewPager->Fragment->Banner,ViewPager页面间的滑动失效,同时单一页面内Banner的手势滑动只能响应一个方向,要么一直向左,要么一直向右,不能同时起作用

censhengde commented 3 years ago

解决方案:继承Banner,核心代码如下: private var mStartX = 0f private var mTouchSlop = 0

init {
    mTouchSlop = ViewConfiguration.get(context).scaledTouchSlop / 2
}

override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
    event ?: return super.onInterceptTouchEvent(event)
    LogUtil.i("===>", "onInterceptTouchEvent")
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            mStartX = event.x
        }
        MotionEvent.ACTION_MOVE -> {
            val distanceX = event.x - mStartX
            if (viewPager2.orientation == HORIZONTAL && distanceX.absoluteValue >= mTouchSlop) {
                // 如果是在最后一条item,继续左滑,则把事件交还给父容器视图
                if (isOnLastItem()) {
                    LogUtil.i("===>", "distanceX= ${distanceX}")
                    // 右滑,则重置事件拦截状态
                    return if (distanceX > 0) {
                        LogUtil.i("===>", "右滑")
                        setIntercept(true)
                        super.onInterceptTouchEvent(event)
                    }
                    // 左滑
                    else {
                        LogUtil.i("===>", "左滑")
                        parent.requestDisallowInterceptTouchEvent(false)
                        false
                    }
                }
                // 如果是在第一条item,继续右滑,则把事件交还给父容器视图
                else if (isOnFirstItem()) {
                    // 右滑,则重置事件拦截状态
                    return if (distanceX > 0) {
                        LogUtil.i("===>", "右滑")
                        parent.requestDisallowInterceptTouchEvent(false)
                        false
                    }
                    //左滑,
                    else {
                        LogUtil.i("===>", "左滑")
                        setIntercept(true)
                        super.onInterceptTouchEvent(event)
                    }
                }

            }

        }
        MotionEvent.ACTION_UP,
        MotionEvent.ACTION_CANCEL -> {
            // 当前处于第一条或者是最后一条item时,把滑动主导权交还给父容器
            if (isOnLastItem() || isOnFirstItem()) {
                LogUtil.i("===>", "当前item: $currentItem ")
                parent.requestDisallowInterceptTouchEvent(false)
                setIntercept(false)
            }

        }
    }

    return super.onInterceptTouchEvent(event)
}

private fun isOnLastItem(): Boolean {
    return adapter != null && currentItem == adapter.itemCount - 1
}

private fun isOnFirstItem(): Boolean {
    return adapter != null && currentItem == 0
}
wyjsonGo commented 2 years ago

按照楼上的继承Banner解决方案,改成java版本,加入RTL,修复只有一个item的情况


public class MyBanner extends Banner {

    private boolean isRTL = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == LayoutDirection.RTL;
    private int mTouchSlop;

    public MyBanner(Context context) {
        super(context);
        init(context);
    }

    public MyBanner(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public MyBanner(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop() / 2;
    }

    private float mStartX;

    @SuppressLint("WrongConstant")
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStartX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                float distanceX = event.getX() - mStartX;
                if (getViewPager2().getOrientation() == HORIZONTAL && Math.abs(distanceX) >= mTouchSlop) {
                    if (isOnOnlyItem()) {
                        getParent().requestDisallowInterceptTouchEvent(false);
                        return false;
                    } else if (isOnLastItem()) {// 如果是在最后一条item,继续左滑,则把事件交还给父容器视图
                        if (isRTL) {
                            if (distanceX > 0) {// 右滑
                                getParent().requestDisallowInterceptTouchEvent(false);
                                return false;
                            } else {// 左滑
                                setIntercept(true);
                            }
                        } else {
                            if (distanceX > 0) {// 右滑
                                setIntercept(true);
                            } else {// 左滑
                                getParent().requestDisallowInterceptTouchEvent(false);
                                return false;
                            }
                        }
                    } else if (isOnFirstItem()) { // 如果是在第一条item,继续右滑,则把事件交还给父容器视图
                        if (isRTL) {
                            if (distanceX > 0) {// 右滑
                                setIntercept(true);
                            } else {// 左滑
                                getParent().requestDisallowInterceptTouchEvent(false);
                                return false;
                            }
                        } else {
                            if (distanceX > 0) {// 右滑
                                getParent().requestDisallowInterceptTouchEvent(false);
                                return false;
                            } else {// 左滑
                                setIntercept(true);
                            }
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                // 当前处于第一条或者是最后一条item时,把滑动主导权交还给父容器
                if (isOnLastItem() || isOnFirstItem()) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                    setIntercept(false);
                }
                break;
        }
        return super.onInterceptTouchEvent(event);
    }

    private boolean isOnLastItem() {
        return getAdapter() != null && getCurrentItem() == getAdapter().getItemCount() - 1;
    }

    private boolean isOnFirstItem() {
        return getAdapter() != null && getCurrentItem() == 0;
    }

    private boolean isOnOnlyItem() {
        return getItemCount() == 0 || getItemCount() == 1;
    }

}