【问题标题】:Swipe Gesture applied at Fragment level along with ViewPager with its default swipe disabled在 Fragment 级别应用滑动手势以及 ViewPager 并禁用其默认滑动
【发布时间】:2013-09-16 06:40:40
【问题描述】:

我发现了以下问题: Android: FragmentActivity inside FragmentActivity (ScrollView in NavigationBar) .但是,我的这个问题是关于如何使用事务来显示片段以及以安全的方式启用滑动手势检测。 注意:我发布的答案中有一个链接(这也有一种使用事务在容器中显示片段的有效方法,包括两个场景)。请看那个。 我已经尝试了一些事情,但是通过使用支持 ViewPager 的片段,而不是嵌套:

详情:https://moqups.com/abhsax130778@gmail.com/lc0ZOERO/p:a5d7b55eb

  1. 使用自定义 ViewPager 禁用使用 onTouchEvent 和 onInterceptTouchEvent 的默认滑动。
  2. 声明的 FragmentStatePagerAdapter 提供了一个片段列表,每个片段都显示在每个选项卡中。
  3. 在具有此 ViewPager 的 main_activity 的 main_layout 中声明片段。 想法是在单击 ViewPager_fragment 上的按钮以及用户按下返回时显示 activity_fragments 按钮,然后使用添加时的回栈事务再次显示默认的 ViewPager_fragment,并在活动的 BackPressed 上弹出它们。因此,我还在维护我的自定义后台堆栈,以便在后台堆栈弹出事务时显示/隐藏 activity_fragments。

现在我想要实现的是使用滑动手势进行上述第三点,仅用于右侧 [从左到右] 滑动。

我为此使用了 GestureListener 和 SimpleOnGestureListener 以及活动的 OnTouchEvent。

我面临的问题是:

此手势适用于片段下方的活动屏幕部分和活动部分。 我想要手势工作

  1. 在其布局中有多个视图的片段区域上。
  2. 仅在左右方向。
  3. 仅从 activity_fragment 到 ViewPager 的选项卡片段的行为类似于在 onBakPressed/onKeyDown 中已经完成的历史导航 使用返回堆栈弹出窗口和我的自定义返回堆栈实现。

我尝试了以下课程并在这样的布局中更改了我的活动片段。

我的手势和触摸监听器扩展了框架布局:

        public class OnSwipeTouchListener extends FrameLayout {

        private final GestureDetector gestureDetector;
        private static final String TAG = "OnSwipeTouchListener";
    private Context context;
        public OnSwipeTouchListener(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context=context;
            gestureDetector = new GestureDetector(context, new GestureListener());
            setClickable(true);

        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return super.onInterceptTouchEvent(ev);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event) {
 gestureDetector.onTouchEvent(motionEvent);
            return super.onTouchEvent(event);
        }


        private final class GestureListener extends SimpleOnGestureListener {

            private static final int SWIPE_THRESHOLD = 100;
            private static final int SWIPE_VELOCITY_THRESHOLD = 100;

            @Override
            public boolean onDown(MotionEvent e) {

                return true;
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

                boolean result = false;
                try {
                    float diffY = e2.getY() - e1.getY();
                    float diffX = e2.getX() - e1.getX();
                    if (Math.abs(diffX) > Math.abs(diffY)) {
                        if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) {
                            if (diffX > 0) {
                                onSwipeRight();
                            } else {
                                onSwipeLeft();
                            }
                        }
                    } else {
                        if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) >           SWIPE_VELOCITY_THRESHOLD) {
                            if (diffY > 0) {
                                onSwipeBottom();
                            } else {
                                onSwipeTop();
                            }
                        }
                    }
                } catch (Exception exception) {
                    //see that e1 is null
                }
                return result;
            }
        }

        public void onSwipeRight() {
            ///manage my back stack history here.
        }

        public void onSwipeLeft() {
        }

        public void onSwipeTop() {
        }

        public void onSwipeBottom() {
        }
    }

然后将框架布局更改为此类引用:

<path.to.my.package.OnSwipeTouchListener
            android:id="@+id/activity_frag_frame_layout1"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:visibility="invisible" >

            <fragment
                android:id="@+id/activity_frag1"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                class="path.to.my.package.fragments.Activity_Frag1"
                android:tag="act_frag1" />
        </path.to.my.package.OnSwipeTouchListener>

它对我帮助不大,所以它应该可以帮助您找到解决方案。在其中一个停止工作的片段中有一个 searchView,并且没有点击工作。 如果您得到我想要实现的目标,请在这方面提供帮助。

更新 1: 在 onTouchEvent 和 onInterceptTouchEvent 中返回 true 具有预期的效果,但它会阻止 SearchView 所在的片段中的点击,在可点击的视图中没有点击。只有滑动有效。更新:在我的最新版本中:我也完全放弃了事务回栈的使用,因为我依赖于我的自定义回栈,我在其中维护所有选项卡的导航历史记录。我使用 ViewPager 和 FragmentStatePagerAdapter 提供的 setCurrentTab 和 onTabSelected 方法来执行此操作。

更新 2: 这个很难实现: 检查哪些事件需要拦截,哪些要传递给子视图:http://developer.android.com/training/gestures/viewgroup.html

更新 3:

  1. 在我的活动布局中,有一个视图分页器,在它下面的每个框架布局中,都有一个片段。

  2. 当您启动应用程序时,ViewPager 会在第一个选项卡中显示片段。

  3. 单击此片段上的按钮时,会显示视图分页器下方的框架布局中的片段之一,并将其​​添加到我的自定义返回堆栈中。

  4. 当按下返回键时,此片段会再次隐藏以在视图分页器中显示选项卡片段。以上每一项都已完成。只有我希望从左到右的滑动手势起作用,我发现我可以通过拦截触摸事件来做到这一点。 但它根本不这样做当我返回真时, 只有滑动手势有效,否则,此操作将被取消并 对此片段的点击有效。

【问题讨论】:

  • @AliImran 澄清:如果您想要除了选项卡片段(显示在选项卡上)之外的更多片段怎么办,因为当您单击该片段上的按钮时,您希望显示另一个片段而不是而不是一个新的活动,因为您希望保持选项卡和操作栏显示并让用户觉得这个新片段已在同一个选项卡中打开,因为它将占用与选项卡片段相同的空间。这就是我试图做的。但现在我想要如上所述的滑动功能。
  • 我想你想要这个:-github.com/astuetz/ViewPagerExtensions
  • 用图片解释你的问题。
  • 我添加了一个 moqup(问题中的链接)。在不离开当前选项卡的情况下访问子页面。新页面与当前选项卡的上下文有关。如果问题完全是文本形式,则应养成使用图表或草稿本的习惯,以了解问题的全部内容。

标签: android android-fragments android-viewpager swipe-gesture


【解决方案1】:

我在http://mobi-app-dev.blogspot.in/2014/01/android-transactions.html提到过这个答案。

检查哪些事件需要拦截,哪些要传递给子视图:http://developer.android.com/training/gestures/viewgroup.html

而不是在框架布局中使用我的滑动手势监听器 附加了哪些片段, 现在我在作为根视图的相对布局上使用它 碎片。

@Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = super.onInterceptTouchEvent(event);

        // In general, we don't want to intercept touch events. They should be
        // handled by the child view.

        gestureDetector.onTouchEvent(event);
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean touched = super.onTouchEvent(event);
        gestureDetector.onTouchEvent(event);
        return touched;
    }

以下是我现在在活动中使用片段时遵循的一些提示:

片段事务的有效管理:

[注意:如果您使用的是 FragmentStatePagerAdapter,您可以在您用作此适配器的数据的片段列表中的特定索引处用另一个片段替换片段,以您所处的方式相同的选项卡,但其内容在不更改选项卡的情况下更改。然后使用 notifyDataSetChanged()。在这里你不使用事务,否则你会使用]。

  1. 使用 Fragment.instantiate() 来初始化您的片段,而不是 构造函数。
  2. 使用监听器更新/删除实例的引用 (来自用于引用它们的集合)片段 onCreateView 和 onDestroy。
  3. 使用 getView() 获取片段的根视图,每个位置和更新时 功能。
  4. 不要跟踪实例,实际上尽可能避免使用静态指针。
  5. 使用 getSupportFragment 而不是保留 FragmentManager 的引用 在视图寻呼机适配器或其他地方。
  6. 在事务隐藏视图上使用附加分离,以摆脱子视图 Fragment 的根视图并重新初始化。 这是因为一次只需要一个片段可见,其他片段需要分离。 因此,按需重新附加。
  7. getActivity() 或 getApplicationContext() 任何适用的,而不是保留 一个全局变量,用于保持对上下文的引用。
  8. onConfiguration 更改(方向、键盘、调整大小):在活动中执行并通过它 到管理它的活动片段。 重要提示:如果您确实想将片段用于事务,请不要在布局中声明它们。 在添加片段事务中提供容器(布局)ID。
  9. 片段事务应该在事件调用中完成,而不是在其他任何地方, 并且这样的事件不应该重复。这可以防止非法参数异常。
  10. 有一个 ViewPager 方法,您不必使用 Fragments。相反, 您可以使用视图或视图组。这取代了片段的嵌套。
  11. 检测片段的级别,这意味着如果您希望片段“B”替换现有片段“A”,并且当您想要返回显示片段“A”时按下“B”,请输入回栈中的事务。

提示:添加滑动功能时要小心,因为还有其他可点击的视图。 在 RootView 上使用拦截触摸滑动,返回 false 但正在分析触摸。


还有一件事:您应该观察到一次将一个片段放在一个容器中可以解决另一个问题:如果有两个片段,一个在另一个的顶部,则来自一个片段的点击会转到另一个片段,即下面。所以隐藏或替换一次只有一个片段。 Back-Stack 有助于维护导航级别,并且在配置更改或更新时,尝试更新目标活动片段的同一实例,而不是执行事务,因为这会改变导航片段的顺序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-04
    • 1970-01-01
    • 2015-02-26
    相关资源
    最近更新 更多