简体   繁体   English

滚动带有ListView的Vertical ViewPager

[英]Scroll Vertical ViewPager with ListView inside

I'm using castorflex/VerticalViewPager library which contains fragments with ListView inside. 我正在使用castorflex / VerticalViewPager库,该库包含内部带有ListView的片段。 The problem is, that I can't change page of ViewPager, when I start swiping over the ListView element. 问题是,当我开始在ListView元素上滑动时,无法更改ViewPager的页面。 Is there any way to pass touch event when ListView reach top/bottom of list to the ViewPager? 当ListView到达列表的顶部/底部到ViewPager时,有什么方法可以传递触摸事件? I wan't to achieve smooth effect (when list cant be scrolled anymore, ViewPager starts scrolling, all in one touch event) 我将无法获得流畅的效果(当列表不再能够滚动时,ViewPager将开始滚动,所有这些操作只需一次触摸即可)

Here's my solution for the Vertical Pager class implemented here 这是我对实现垂直寻呼机级解决方案, 在这里

Instead of a RecyclerView implement a custom class that extends it, and inside our custom RecyclerView Class override public boolean onTouchEvent(MotionEvent event) 代替RecyclerView实施一个扩展它的自定义类,并在我们的自定义RecyclerView类内重写public boolean onTouchEvent(MotionEvent event)

Here we can pass the MotionEvent event into a class like follows to determine if we should intercept the 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;
}

Then in the public boolean onTouchEvent(MotionEvent event) method of the custom RecyclerView class, check to see if getParent() (and possibly iteratively so to the root parent) resolves to the ViewParent of the class of the View you want to override the touches from. 然后,在自定义RecyclerView类的public boolean onTouchEvent(MotionEvent event)方法中,检查getParent() (并可能迭代地返回到根父级)是否解析为要覆盖触摸的View类的ViewParent 。从。 If so, set requestDisallowInterceptTouchEvent() to true on that ViewParent 如果是这样,则在该ViewParent requestDisallowInterceptTouchEvent()设置为true

This is a bit of a late reply but I was recently asked to change our apps ViewPager to vertical and I had exactly the same problem with my list inside. 这是一个较晚的答复,但最近我被要求将我们的应用程序ViewPager更改为垂直,并且我的列表完全相同。 I hope this helps someone else who might have the same problem. 我希望这可以帮助其他可能遇到相同问题的人。

Firstly, my Fragment was extension of ListFragment but it works the same way for any Fragment containing a list (with minor modification obviously). 首先,我的Fragment是ListFragment的扩展,但是它对包含列表的Fragment的工作方式相同(显然需要进行少量修改)。 Firstly, I set an OnScrollListener on my list and in onScrollStateChanged I had a method updateListVisibility which called back to my Activity updating if the top of the top or the bottom of the bottom were visible in the list (updateListPositionVisibility). 首先,我在列表上设置了一个OnScrollListener,在onScrollStateChanged中,我有一个方法updateListVisibility,如果在列表中可以看到顶部的顶部或底部的底部,则可以调用Activity更新(updateListPositionVisibility)。

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);
    }

    .....
}

In my activity I set an OnPageChangeListener on my ViewPager and in onPageSelected I used my adapter to get the Fragment so that I could call updateListVisibility on it which then called back to my activity. 在我的活动中,我在ViewPager上设置了一个OnPageChangeListener,在onPageSelected中,我使用了适配器来获取Fragment,以便可以对其调用updateListVisibility,然后再调用我的活动。 My callback implementation updated the ViewPager with the visibility status of the top and bottom of the list (see setListViewPositionsInViewPager). 我的回调实现使用列表顶部和底部的可见性状态更新了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);
}

My Adapter simply maintains an instance of the fragment in mItems. 我的适配器只是在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);
    }
}

Then in my Castorflex vertical view pager I set the list visibility values using a method called setListVisibility. 然后,在我的Castorflex垂直视图寻呼机中,使用称为setListVisibility的方法设置列表可见性值。 These are updated every time the list is scrolled or the ViewPager is paged meaning they're always up to date. 每次滚动列表或分页ViewPager时,这些内容都会更新,这意味着它们始终是最新的。 The most important part is then for the pager to intercept the events when either the top was visible and the user attempted to go up or the bottom was visible and the user attempted to go down. 然后最重要的部分是寻呼机在顶部可见并且用户试图向上或者底部可见并且用户试图向下时拦截事件。 This is easily achived using the dy value that is calculated inside the switch statement under MotionEvent.ACTION_MOVE. 使用在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;
            }

        }
        .....
    }

    .....
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM