Android NavigationDrawer和ViewPager-滑动问题

Android NavigationDrawer和ViewPager-滑动问题,android,android-viewpager,navigation-drawer,drawerlayout,Android,Android Viewpager,Navigation Drawer,Drawerlayout,我有一个ViewPager,里面有3个片段。我已经扩展了ViewPager,并且由于我的应用程序特有的原因禁用了它的刷卡功能。因此,为了在这些片段之间导航,我在活动中添加了一个TabLayout 在我的第二个片段(中间的片段)中,我在左边缘有一个抽屉布局。 每次我在这个片段中从左到右做一个滑动动作,就会显示 强>即使我在屏幕中间滑动。只有当我靠近屏幕左边缘滑动时,才会发生这种情况。 我一直很难理解Android中的触摸和运动事件处理。。。是否有办法保持我的查看页面(禁用滑动)并限制触摸事件抽屉布

我有一个
ViewPager
,里面有3个片段。我已经扩展了
ViewPager
,并且由于我的应用程序特有的原因禁用了它的刷卡功能。因此,为了在这些片段之间导航,我在活动中添加了一个
TabLayout

在我的第二个片段(中间的片段)中,我在左边缘有一个
抽屉布局。
每次我在这个片段中从左到右做一个滑动动作,就会显示<<代码> DawerLayout <代码> > <>强>即使我在屏幕中间滑动。只有当我靠近屏幕左边缘滑动时,才会发生这种情况。

我一直很难理解Android中的触摸和运动事件处理。。。是否有办法保持我的
查看页面
(禁用滑动)并限制触摸事件
抽屉布局
“侦听”屏幕边缘(按其预期)

如果我不修改
ViewPager
(也就是说,如果我一直启用滑动),它也不起作用,因为从左边缘滑动将开始打开
抽屉布局
,但同时将开始
ViewPager
的转换

我在一个小应用程序中重新创建了这个问题

这是MyViewPager:

public class MyViewPager extends ViewPager {

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


    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return false;
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }
}
这是主要的活动:

public class MainActivity extends AppCompatActivity {

    Fragment Fragment1;
    Fragment Fragment2;
    Fragment Fragment3;
    MyViewPager viewPager;
    TabLayout tabLayout;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Fragment1 = new Fragment1();
        Fragment2 = new Fragment2();
        Fragment3 = new Fragment3();

        viewPager = findViewById(R.id.ViewPager);

        tabLayout = findViewById(R.id.TabLayout);

        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                switch (position) {
                    case 0:
                        if (Fragment1 == null) {
                            Fragment1 = new Fragment1();
                        }
                        return Fragment1;
                    case 1:
                        if (Fragment2 == null) {
                            Fragment2 = new Fragment2();
                        }
                        return Fragment2;
                    case 2:
                        if (Fragment3 == null) {
                            Fragment3 = new Fragment3();
                        }
                        return Fragment3;
                    default:
                        return null;
                }
            }


            @Override
            public int getCount() {
                return 3;
            }


            @Override
            public CharSequence getPageTitle(int position) {
                switch (position) {
                    case 0:
                        return "frag1";
                    case 1:
                        return "frag2";
                    case 2:
                        return "frag3";
                    default:
                        return "0";
                }
            }
        });

        viewPager.setOffscreenPageLimit(2);
        viewPager.setCurrentItem(0);
        tabLayout.setupWithViewPager(viewPager);
    }
}
活动主布局:

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    android:id="@+id/main_frame"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#4dea21">

    <com.example.android.navbarviewpager.MyViewPager
        android:id="@+id/ViewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:background="#ea3ee2"/>

    <android.support.design.widget.TabLayout
        android:id="@+id/TabLayout"
        android:layout_width="wrap_content"
        android:layout_height="45dp"
        android:layout_gravity="left|bottom"
        app:tabMode="scrollable">

        <android.support.design.widget.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="frag1"/>

        <android.support.design.widget.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="frag2"/>

        <android.support.design.widget.TabItem
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="frag3"/>

    </android.support.design.widget.TabLayout>

</FrameLayout>
片段2:

public class Fragment1 extends Fragment {


    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment2, container, false);

    }
}
片段3:

public class Fragment1 extends Fragment {


    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment3, container, false);

    }
}
片段2的布局(带有抽屉布局的布局):


以及其他片段的布局:

片段一

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    android:id="@+id/frag_1"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#eea2a2"
    android:padding="0dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="fragmento UM"
        android:gravity="center"/>

</FrameLayout>

片段三

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    android:id="@+id/frag_1"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#abeea2"
    android:padding="0dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="fragmento TRÊS"
        android:gravity="center"/>

</FrameLayout>

虽然我认为不可能将水平
浏览页面
和水平
抽屉布局
放在一起,但您可以将其中一个旋转90度,使其成为垂直滑动视图(对于
DrawerLayout
来说很容易,如果您使用的是github上有编号的库,那么对于
ViewPager
来说也很容易)

关于您问题的核心:可以将
抽屉布局的敏感区域限制在其滑动的边缘。为此,您需要覆盖
抽屉布局
,并使用它,而不是
/layout/fragment2.xml
中的
抽屉布局

代码如下:

public class MyDrawerLayout extends DrawerLayout {
    Context context;

    public MyDrawerLayout(Context context) {
        this(context, null);
    }

    public MyDrawerLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
        // if the drawer is open already, pass motionEvent upwards
        if (this.isDrawerOpen(Gravity.START)) {
            return super.onInterceptTouchEvent(motionEvent);
        }
        // sensitive area is set to 1/4 of the drawer's full width
        // adopt to your needs
        int sensitiveWidth = ((Activity) context).findViewById(R.id.drawer_layout).getWidth() / 4;
        // if the drawer is not (fully) open AND
        // motionEvent.getX() is close to the left edge,
        // fully open the drawer and pass motionEvent upwards
        if (motionEvent.getX() < sensitiveWidth) {
            this.openDrawer(Gravity.START);
            return super.onInterceptTouchEvent(motionEvent);
        }
        // otherwise eat motionEvent
        return false;
    }
}
public类MyDrawerLayout扩展了DrawerLayout{
语境;
公共MyDrawerLayout(上下文){
这个(上下文,空);
}
公共MyDrawerLayout(上下文、属性集属性){
这(上下文,属性,0);
}
公共MyDrawerLayout(上下文、属性集属性、int-defStyle){
超级(上下文、属性、定义样式);
this.context=上下文;
}
@凌驾
公共布尔值onInterceptTouchEvent(MotionEvent){
//如果抽屉已打开,则向上传递motionEvent
如果(此.isDrawerOpen(重力启动)){
返回super.onInterceptTouchEvent(motionEvent);
}
//敏感区域设置为抽屉全宽的1/4
//适应你的需要
int sensitiveWidth=((活动)上下文).findViewById(R.id.drawer\u布局).getWidth()/4;
//如果抽屉未(完全)打开
//motionEvent.getX()靠近左边缘,
//完全打开抽屉并向上传递
if(motionEvent.getX()
注二:

  • 一旦抽屉打开,您可能希望它在任何交互之后关闭,即使它位于“敏感区域”之外。这就是
    onInterceptTouchEvent
    中的第一个if子句的作用

  • 抽屉未完全打开时,执行会将
    MotionEvent
    s吞入
    DrawerLayout
    上的“敏感区域”之外。因此,有空间进行“投掷”手势很窄。为了使打开抽屉更容易,我建议在这里以编程方式打开抽屉。如果你的应用程序不依赖于半开/半闭抽屉,这似乎是一个很好的可用性解决方案

  • 我解释了如何使用表格布局向所有方向移动,这会有所帮助

    对于您的情况,您可以使用手势检测器

    gestureDetectorCompat = new GestureDetectorCompat(this, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            float angle = (float) Math.toDegrees(Math.atan2(e1.getY() - e2.getY(), e2.getX() - e1.getX()));
    
            if (angle > -45 && angle <= 45) {
                if(goRight != 5) mainContainer.setCurrentItem(goRight);
                return true;
            }
    
            if (angle >= 135 && angle < 180 || angle < -135 && angle > -180) {
                if(goLeft != 5) mainContainer.setCurrentItem(goLeft);
                return true;
            }
    
            if (angle < -45 && angle >= -135) {
                if(goUp != 5)mainContainer.setCurrentItem(goUp);
                return true;
            }
    
            if (angle > 45 && angle <= 135) {
                if(goDown != 5)mainContainer.setCurrentItem(goDown);
                return true;
            }
    
            return false;
        }
    
    
    });
    
    重要的一步是将.xml中的“someView”设置为获取运动的区域。您不需要观看整个屏幕


    希望对您有所帮助

    效果很好,谢谢。有没有办法禁用抽屉的“偷看”状态(当屏幕上只有一小部分抽屉可见时)?我一直在查看抽屉布局的源代码,但我不知道从哪里开始(这里是初学者)。您可以添加类似于
    if(this.isDrawervible)的内容(Gravity.START)&!this.isDrawerOpen(Gravity.START){//做点什么,例如,关闭抽屉}
    onInterceptTouchEvent
    方法。我知道了,它是可见的,但没有打开。它在
    onInterceptTouchEvent
    中不起作用,但在
    SimpleDrawerListener
    onDrawerStateChanged
    中起作用。再次感谢!
    public class MyDrawerLayout extends DrawerLayout {
        Context context;
    
        public MyDrawerLayout(Context context) {
            this(context, null);
        }
    
        public MyDrawerLayout(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MyDrawerLayout(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            this.context = context;
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
            // if the drawer is open already, pass motionEvent upwards
            if (this.isDrawerOpen(Gravity.START)) {
                return super.onInterceptTouchEvent(motionEvent);
            }
            // sensitive area is set to 1/4 of the drawer's full width
            // adopt to your needs
            int sensitiveWidth = ((Activity) context).findViewById(R.id.drawer_layout).getWidth() / 4;
            // if the drawer is not (fully) open AND
            // motionEvent.getX() is close to the left edge,
            // fully open the drawer and pass motionEvent upwards
            if (motionEvent.getX() < sensitiveWidth) {
                this.openDrawer(Gravity.START);
                return super.onInterceptTouchEvent(motionEvent);
            }
            // otherwise eat motionEvent
            return false;
        }
    }
    
    gestureDetectorCompat = new GestureDetectorCompat(this, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            float angle = (float) Math.toDegrees(Math.atan2(e1.getY() - e2.getY(), e2.getX() - e1.getX()));
    
            if (angle > -45 && angle <= 45) {
                if(goRight != 5) mainContainer.setCurrentItem(goRight);
                return true;
            }
    
            if (angle >= 135 && angle < 180 || angle < -135 && angle > -180) {
                if(goLeft != 5) mainContainer.setCurrentItem(goLeft);
                return true;
            }
    
            if (angle < -45 && angle >= -135) {
                if(goUp != 5)mainContainer.setCurrentItem(goUp);
                return true;
            }
    
            if (angle > 45 && angle <= 135) {
                if(goDown != 5)mainContainer.setCurrentItem(goDown);
                return true;
            }
    
            return false;
        }
    
    
    });
    
    someView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                gestureDetectorCompat.onTouchEvent(motionEvent);
                return false;
            }
        });