【问题标题】:Expanding the touch area of a ViewPager扩展 ViewPager 的触摸区域
【发布时间】:2017-07-29 19:34:07
【问题描述】:

我的应用使用以下ViewPager

ViewPager 与显示的宽度相同。

我在 xml 中为 ViewPager 添加了边距,以便我可以看到左(蓝色)和右(灰色)页面。 结果一切正常,但我只能触摸绿色区域来移动页面。

我想将触摸区域扩展到整个屏幕,以便我可以触摸任何地方来移动ViewPager

我尝试添加TouchDelegate

binding.pagerContainer.post {
        val rect = Rect()  
        binding.touchArea.getHitRect(rect)
        binding.pagerContainer.touchDelegate = TouchDelegate(rect, binding.viewPager)
    }

在xml中:

 <FrameLayout
        android:id="@+id/pager_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:clipChildren="false">

        <android.support.v4.view.ViewPager
            android:id="@+id/viewPager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:layout_marginBottom="150dp"
            android:layout_marginLeft="70dp"
            android:layout_marginRight="70dp"
            android:layout_marginTop="150dp"
            android:clipChildren="false"/>

        <View
            android:id="@+id/touchArea"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:alpha="0">
        </View>
    </FrameLayout>

Rect 的大小正确。 我发布了Runnable 以确保视图布局。 我阅读了TouchDelegate 的文档。

我不知道为什么它不起作用。

【问题讨论】:

    标签: android android-layout android-viewpager touch android-view


    【解决方案1】:

    好的。我想这次我有。经过一些研究herehere,我想出了这个答案。我没有尝试使用 TouchDelegate 扩展 ViewPager 的区域,而是将触摸事件从 FrameLayout 重新路由到 ViewPager。 1、制作自己的FrameLayout

    public class MyFrameLayout extends FrameLayout {
    
        // make sure to include all the necessary constructors here
        // with calls to super
    
        // only need to override 1 method
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            ViewPager pager = null;
            int count = getChildCount();
            for(int i=0; i<count; i++) {
                if(getChildAt(i) instanceof ViewPager) {
                    pager = (ViewPager)getChildAt(i);
                    break;
                }
            }
            if(pager != null) {
                return pager.onTouchEvent(event);
            }
            return super.onTouchEvent(event);
        }
    }
    

    如果你知道ViewPager在FrameLayout中总是在同一个地方,那么onTouchEvent()可以简化为

        ViewPager pager = (ViewPager)getChildAt(0); 
        return pager.onTouchEvent(event);
    

    然后你在布局文件中使用你自定义的 FrameLayout

     <your.package.name.MyFrameLayout
        android:id="@+id/pager_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        etc. etc.
    

    我已经对其进行了测试,这对我有用。希望它对你有用。

    【讨论】:

    • 天才!爱你:)
    【解决方案2】:

    在 yourlayout.xml 中添加这一行:

    android:clickable="true"
    android:focusable="true"
    android:focusableInTouchMode="true"
    

    试试这个块:

    YOURActivityLayout.setOnTouchListener(new View.OnTouchListener() {  
            @Override
            public boolean onTouch(View arg0, MotionEvent arg1) {
    
                return true;//always return true 
            }
        });
    

    希望这会奏效

    【讨论】:

    • 用于 touchareaview 的 xml?
    • 在你的布局中验证 @Appyx
    • 我在我的 FrameLayout 中添加了您的 xml。我将 onTouchListener 添加到我的 FrameLayout 中。没有变化。
    • 对了,我发了一个 onTouchListener 的代码块,你知道 ActivityLayout.setOnTouchListener 必须是 YourActivityLayout.setOnTouchListener 吗? @Appyx
    • 请删除此行并重试。android:clickable="true" @Appyx
    【解决方案3】:

    我的 viewpager 后面有一个视图作为背景。我注意到发送到viewpager中活动项目左右的非活动项目的触摸事件被发送到背景视图。这让我想到了在那里拦截触摸并使用 viewpager 的假拖动 api。

    我为 ViewPager2 构建了一个扩展函数,如下所示:

    fun ViewPager2.receiveFakeDragsFrom(view: View) {
        view.setOnTouchListener { _, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    requestDisallowInterceptTouchEvent(true)
                    beginFakeDrag()
                    true
                }
                MotionEvent.ACTION_MOVE -> {
                    if (event.historySize > 0) {
                        fakeDragBy(
                            event.x - event.getHistoricalX(0)
                        )
                    }
                    true
                }
                MotionEvent.ACTION_UP -> {
                    endFakeDrag()
                    requestDisallowInterceptTouchEvent(false)
                    performClick()
                    true
                }
                else -> {
                    false
                }
            }
        }
    }
    

    然后在我的片段中,在设置 viewpager 时,我只是以背景视图作为参数调用了该函数:

    myViewPager.receiveFakeDragsFrom(vpBackgroundView)
    

    【讨论】: