繁体   English   中英

滚动带有ListView的Vertical ViewPager

[英]Scroll Vertical ViewPager with ListView inside

我正在使用castorflex / VerticalViewPager库,该库包含内部带有ListView的片段。 问题是,当我开始在ListView元素上滑动时,无法更改ViewPager的页面。 当ListView到达列表的顶部/底部到ViewPager时,有什么方法可以传递触摸事件? 我将无法获得流畅的效果(当列表不再能够滚动时,ViewPager将开始滚动,所有这些操作只需一次触摸即可)

这是我对实现垂直寻呼机级解决方案, 在这里

代替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() (并可能迭代地返回到根父级)是否解析为要覆盖触摸的View类的ViewParent 。从。 如果是这样,则在该ViewParent requestDisallowInterceptTouchEvent()设置为true

这是一个较晚的答复,但最近我被要求将我们的应用程序ViewPager更改为垂直,并且我的列表完全相同。 我希望这可以帮助其他可能遇到相同问题的人。

首先,我的Fragment是ListFragment的扩展,但是它对包含列表的Fragment的工作方式相同(显然需要进行少量修改)。 首先,我在列表上设置了一个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);
    }

    .....
}

在我的活动中,我在ViewPager上设置了一个OnPageChangeListener,在onPageSelected中,我使用了适配器来获取Fragment,以便可以对其调用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;
            }

        }
        .....
    }

    .....
}

暂无
暂无

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

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