touching/dragging 特定视图时禁用 viewpager

Disable viewpager when touching/dragging certain view

使用类似于 this question, I've tried to disable the ViewPager swipe action for when the user is swiping over a particular item. The view in question is a scrollable chart from the MPAndroidChart 库的答案的东西,所以我自然不希望视图寻呼机干扰图表的滚动。

我遇到的问题是 ViewPager 通常会在我想要的视图上调用 onTouchListener 之前调用 "onInterceptTouch"。

在这段代码中,我在视图为pressed/unpressed:

时进行录制
private long lastDown;

private long lastUp;

...

public void foo(){

    barChart.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View v, MotionEvent event) {

        System.out.println("AAA");

        if(event.getAction() == MotionEvent.ACTION_DOWN){
          lastDown = System.currentTimeMillis();
        }else if(event.getAction() == MotionEvent.ACTION_UP){
          lastUp = System.currentTimeMillis();
        }

    return false;
      }
    });
}

在这段代码中,我判断视图是否被选中:

  public boolean isGraphTouched(){
    return lastDown > lastUp;
  }

在这段代码中,我重写了 onInterceptTouchEvent 方法:

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    System.out.println("BBB");
    return isGraphSelected() ? super.onInterceptTouchEvent(ev) : false;
  }

如果您注意到打印行,就会在之前调用 onInterceptTouchEvent 方法...

我能想到的解决这个问题的唯一方法是制作一个方法来检查图形是否存在于运动事件的坐标处(尽管我不确定这是否可能)然后使用它确定寻呼机是否可滑动。

我通过从另一个 post 合并 this answer 设法解决了我的问题。该代码允许您获取给定视图的坐标并将它们与您自己的坐标进行比较。

在我的 CustomViewPager class 中,我实现了上面提到的内容:

  private boolean isPointInsideView(float x, float y, View view) {
    int location[] = new int[2];
    view.getLocationOnScreen(location);
    int viewX = location[0];
    int viewY = location[1];

    return ((x > viewX && x < (viewX + view.getWidth())) && (y > viewY && y < (viewY + view.getHeight())));
  }

然后我有一个方法 returns 一个布尔值,它检查几个条件和 returns true/false 是否应该能够刷寻呼机:

  public boolean canSwipe(float x, float y) {
    boolean canSwipe = true;
    if (launchActivity.isReady(MainScreenPagerAdapter.STAT_PAGE)) {
      FragmentStatistics fragmentStatistics = (FragmentStatistics) launchActivity.getPageAdapter().instantiateItem(this, MainScreenPagerAdapter.STAT_PAGE);
      View chart = fragmentStatistics.getView().findViewById(R.id.chart);
      canSwipe = !isPointInsideView(x, y, chart) || !fragmentStatistics.isGraphPannable();
    }
    return canSwipe;
  }

然后,当然,我像这样重写了 onInterceptTouchEvent:

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    return canSwipe(ev.getX(), ev.getY()) ? super.onInterceptTouchEvent(ev) : false;
  }

现在,可以完全平移图表,而寻呼机根本不会干扰它。

完整的 CustomViewPager 代码:

public class CustomViewPager extends ViewPager {

  /** Reference to the launch activity */
  private LaunchActivity launchActivity;

  /**
   * Constructor to call the super constructor
   *
   * @param context The application context
   * @param attrs   The attributes
   */
  public CustomViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  /**
   * Sets object reference for the {@code launchActivity}
   *
   * @param launchActivity The LaunchActivity to be set
   */
  public void set(LaunchActivity launchActivity) {
    this.launchActivity = launchActivity;
  }

  /**
   * Determines if the pager can be swiped based off the x and y inputs provided, as well as if the
   * barchart can be panned or not.
   *
   * @param x The x coordinate to check
   * @param y The y coordinate to check
   * @return True if the ViewPager will continue with normal swiping action.
   */
  public boolean canSwipe(float x, float y) {
    boolean canSwipe = true;
    if (launchActivity.isReady(MainScreenPagerAdapter.STAT_PAGE)) {
      FragmentStatistics fragmentStatistics = (FragmentStatistics) launchActivity.getPageAdapter().instantiateItem(this, MainScreenPagerAdapter.STAT_PAGE);
      View chart = fragmentStatistics.getView().findViewById(R.id.chart);
      canSwipe = !isPointInsideView(x, y, chart) || !fragmentStatistics.isGraphPannable();
    }
    return canSwipe;
  }

  /**
   * Takes x and y coordinates and compares them to the coordinates of the passed view. Returns true if the passed coordinates
   * are within the range of the {@code view}
   *
   * @param x    The x coordinate to compare
   * @param y    The y coordinate to compare
   * @param view The view to check the coordinates of
   * @return True if the x and y coordinates match that of the view
   */
  private boolean isPointInsideView(float x, float y, View view) {
    int location[] = new int[2];
    view.getLocationOnScreen(location);
    int viewX = location[0];
    int viewY = location[1];

    // point is inside view bounds
    return ((x > viewX && x < (viewX + view.getWidth())) && (y > viewY && y < (viewY + view.getHeight())));
  }

  /**
   * Override of the onInterceptTouchEvent which allows swiping to be disabled when chart is selected
   *
   * @param ev The MotionEvent object
   * @return Call to super if true, otherwise returns false
   */
  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    return canSwipe(ev.getX(), ev.getY()) ? super.onInterceptTouchEvent(ev) : false;
  }

}

我设法使用函数 parent.requestDisallowInterceptTouchEvent(true); 放置在 child 视图 onTouchEvent() 中使其工作。这样,View 不允许他的 parents 中的 none 拦截他的触摸事件,以防发生滚动并由 ViewPager 处理。

但是在我的例子中,我有一个 ViewPager,里面有一个可拖动的自定义视图,我想在没有 ViewPager 更改页面的情况下移动它。

我在代码方面的解决方案:(Kotlin)

view.setOnTouchListener { v, event ->
    parent.requestDisallowInterceptTouchEvent(true);
    //Drag and drop handling is here and the rest of the event logic
}

希望这对你也有帮助。