【问题标题】:Scroll Vertical ViewPager with ListView inside滚动带有 ListView 的 Vertical ViewPager
【发布时间】:2014-09-11 08:05:35
【问题描述】:

我正在使用 castorflex/VerticalViewPager 库,其中包含内部带有 ListView 的片段。问题是,当我开始在 ListView 元素上滑动时,我无法更改 ViewPager 的页面。当 ListView 到达列表的顶部/底部到 ViewPager 时,有什么方法可以将触摸事件传递给 ViewPager?我不想达到平滑的效果(当列表无法滚动时,ViewPager 开始滚动,一键式事件)

【问题讨论】:

    标签: android android-listview scroll android-viewpager


    【解决方案1】:

    这是我实现的 Vertical Pager 类的解决方案here

    代替 RecyclerView 实现一个自定义类来扩展它,并在我们的自定义 RecyclerView 类覆盖 public boolean onTouchEvent(MotionEvent event)

    在这里,我们可以将MotionEvent event 传递给如下类以确定我们是否应该拦截事件:

    /**
     * Tests the MotionEvent for valid behavior to care about overriding the touch event with
     */
    private boolean shouldDisableScrollableParent(final MotionEvent event) {
        boolean intercept = false;
        float x = event.getX();
        float y = event.getY();
    
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
    
                float dx = x - mPreviousX;
                float dy = y - mPreviousY;
    
                // Here you can try to detect the swipe. It will be necessary to
                // store more than the previous value to check that the user move constantly in the same direction
                intercept = detectSwipe(dx, dy);
                break;
    
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            default:
                break;
        }
    
        mPreviousX = x;
        mPreviousY = y;
    
        return intercept;
    }
    
    /**
     * Tests if we want to disable the parent touch interception
     */
    private boolean detectSwipe(final float dx, final float dy) {
        // True if we're scrolling in Y direction
        if (Math.abs(dx) < Math.abs(dy)) {
            FoundationLog.d(TAG, "Direction Y is yielding: " + dy);
    
            if (dy < 0) {
                // Touch from lower to higher
                return true;
            }
    
            // Top view isn't shown, can always scroll up
            final View view = getChildAt(0);
            if (view == null) {
                return true;
            }
    
            // Top view baseline doesn't equal to top of the RecyclerView
            if (view.getTop() < 0) {
                return true;
            }
        }
    
        return false;
    }
    

    然后在自定义 RecyclerView 类的public boolean onTouchEvent(MotionEvent event) 方法中,检查getParent()(并且可能迭代到根父级)是否解析为要覆盖的视图类的ViewParent从接触。如果是这样,请将requestDisallowInterceptTouchEvent() 设置为true 上的ViewParent

    【讨论】:

      【解决方案2】:

      这有点晚了,但我最近被要求将我们的应用 ViewPager 更改为垂直,而我的列表也遇到了完全相同的问题。我希望这对可能遇到同样问题的其他人有所帮助。

      首先,我的 Fragment 是 ListFragment 的扩展,但它对于任何包含列表的 Fragment 的工作方式都是相同的(显然需要稍作修改)。首先,我在列表中设置了一个 OnScrollListener,在 onScrollStateChanged 中,我有一个方法 updateListVisibility,如果顶部的顶部或底部的底部在列表中可见(updateListPositionVisibility),它会回调我的 Activity 更新。

      public class MyListFragment extends ListFragment {
      
         .....
         private OnScrollStateChangedListener mListener;
         .....
      
          @Override
          public void onViewCreated(View view, Bundle savedInstanceState) {
      
              super.onViewCreated(view, savedInstanceState);
              ......
              getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
      
                  @Override
                  public void onScrollStateChanged(AbsListView view, int scrollState) {
                      updateListVisibility();
                  }
      
                  @Override
                  public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
      
                  }
              });
          }
      
          public void updateListVisibility() {
      
              if (getListView() != null && getListView().getChildCount() > 0) {
      
                  boolean firstItemVisible = getListView().getFirstVisiblePosition() == 0;
                  boolean topOfFirstItemVisible = getListView().getChildAt(0).getTop() == 0;
      
                  boolean lastItemVisible = getListView().getLastVisiblePosition() == getListView().getAdapter().getCount() - 1;
                  boolean bottomOfLastItemVisible = getListView().getChildAt(getListView().getChildCount() - 1).getBottom() <= getListView().getHeight();
      
                  mListener.updateListPositionVisibility(firstItemVisible && topOfFirstItemVisible, lastItemVisible && bottomOfLastItemVisible);
              }
          }
      
          @Override   
          public void onAttach(Activity activity) {
      
              super.onAttach(activity);
              try {
                  mListener = (OnScrollStateChangedListener) activity;
              } catch (ClassCastException e) {
                  throw new ClassCastException(activity.toString() + " must implement OnScrollStateChangedListener");
              }
          }
      
      
          /**
           * Container Activity must implement this interface....
           */
          public interface OnScrollStateChangedListener {
      
              public void updateListPositionVisibility(boolean topIsVisible, boolean bottomIsVisible);
          }
      
          .....
      }
      

      在我的活动中,我在 ViewPager 上设置了一个 OnPageChangeListener,在 onPageSelected 中,我使用我的适配器来获取片段,以便我可以在其上调用 updateListVisibility,然后回调我的活动。我的回调实现使用列表顶部和底部的可见性状态更新了 ViewPager(请参阅 setListViewPositionsInViewPager)。

      public class VerticallyPagedActivity extends BaseFragmentActivity implements
          MyListFragment.OnScrollStateChangedListener {
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
      
              ......
      
              mPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
              @Override
              public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
      
              }
      
              @Override
              public void onPageSelected(int position) {
                  Object fragment = mAdapter.mItems.get(position);
                  if (fragment instanceof MyListFragment) {
                      ((MyListFragment) fragment).updateListVisibility();
                  } else {
                      setListViewPositionsInViewPager(false, false);
                  }
              }
      
              @Override
              public void onPageScrollStateChanged(int state) {
      
              }
          });
      }
      
      public void setListViewPositionsInViewPager(boolean handleEventTop, boolean handleEventBottom) {
          mPager.setListVisibility(handleEventTop, handleEventBottom);
      }
      
      // Callback.
      @Override
      public void updateListPositionVisibility(boolean topIsVisible, boolean bottomIsVisible) {
          setListViewPositionsInViewPager(topIsVisible, bottomIsVisible);
      }
      

      我的适配器只是在 mItems 中维护片段的一个实例。

      public class MyAdapter extends FragmentStatePagerAdapter {
      
          .....
          public Map<Integer, Fragment> mItems;
      
          public MyAdapter(FragmentManager fm, List<Content> content) {
          super(fm);
          mItems = new HashMap<>();
          .....
      
          @Override
          public Fragment getItem(int position) {
      
              .....   
              MyListFragment fragment = new MyListFragment();
              .....
              mItems.put(position, fragment);
              return fragment;
          }
      
          @Override
          public void destroyItem(ViewGroup container, int position, Object object) {
              mItems.remove(position);
              super.destroyItem(container, position, object);
          }
      }
      

      然后在我的 Castorflex 垂直视图寻呼机中,我使用名为 setListVisibility 的方法设置列表可见性值。每次滚动列表或分页 ViewPager 时,它们都会更新,这意味着它们始终是最新的。最重要的部分是寻呼机在顶部可见但用户试图向上或底部可见但用户试图向下时拦截事件。使用在 MotionEvent.ACTION_MOVE 下的 switch 语句中计算的 dy 值很容易实现这一点。

      public class VerticalPager extends ViewGroup {
      
          .....
          private boolean topOfListIsVisible;
          private boolean bottomOfListIsVisible;
      
          public void setListVisibility(boolean pTop, boolean pBottom) {
              topOfListIsVisible = pTop;
              bottomOfListIsVisible = pBottom;
          }
      
          .....
      
          @Override
          public boolean onInterceptTouchEvent(MotionEvent ev) {
      
              ......
      
              switch (action) {
                  case MotionEvent.ACTION_MOVE: {
      
                  ......
                  final float y = MotionEventCompat.getY(ev, pointerIndex);
                  final float dy = y - mLastMotionY;
                  final float yDiff = Math.abs(dy);
                  ......
      
                  // Pager needs to handle the event.
                  if (topOfListIsVisible && bottomOfListIsVisible || bottomOfListIsVisible && dy < 0 || topOfListIsVisible && dy > 0) {
                      return true;
                  }
      
              }
              .....
          }
      
          .....
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-05
        • 1970-01-01
        相关资源
        最近更新 更多