Android - 将触摸事件发送到其他视图

时间:2015-07-08 06:54:07

标签: android layout android-recyclerview

我正在尝试实现滚动效果,我认为可以这样做,因为我看到一些应用实现了这个。

我有一个FrameLayout,在这个布局中我有: - 回收者视图 - 浮动视图

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <LinearLayout // float layout here
        android:layout_width="match_parent"
        android:layout_height="100dp">
    </LinearLayout>
</FrameLayout>

当我滚动回收器视图时,我也可以看到浮动视图滚动,但当它到达屏幕顶部时,我希望它停在那里。我已成功实现了这一点,但在此之后我面临一个新问题。由于浮动视图位于回收器视图上方,因此触摸并滚动浮动视图时无法滚动。在这种情况下,浮动视图似乎消耗了触摸事件,因此回收器不执行任何操作。

我想要实现的是当用户想要滚动回收器视图时应该使用它。 我想把浮动视图的触摸事件发送到回收者视图。

感谢。

1 个答案:

答案 0 :(得分:2)

前段时间我发现了同样的问题。这是我的解决方案(它有点hacky,但没有找到更好的解决方案)。放入自定义的FrameLayout类:

public class CustomFrameLayout extends FrameLayout {

    ... 

    @InjectView(R.id.rv_details)
    RecyclerView recyclerView;

    @InjectView(R.id.ll_details_action_bar_wrapper)
    ViewGroup actionBarWrapperViewGroup;

    private List<MotionEvent> cachedEventList = new ArrayList<>();

    private boolean touchIsFromActionBar;

    private boolean yTranslationThresholdPassed;

    // Pawel Janeczek
    // Those two overrides is for forwarding touch events, that started on action bar, to recyclerview.
    // But you may ask, why there are so many lines? it should by only recyclerView.dispatchTouchEvent(ev) and it should be fine
    // It is because RecyclerView when it is starting scrolling it sends parent.requestDisallowInterceptTouchEvent which disables sending onInterceptTouchEvent to parent
    // In such case we must set a flag touchIsFromActionBar when motion event starts and is in action bar, and then when this flag is set we remove calling super on requestDisallowInterceptTouchEvent
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        if (action == MotionEvent.ACTION_DOWN && viewUtils.isWithinViewBounds(actionBarWrapperViewGroup, ev.getRawX(), ev.getRawY())) {
            touchIsFromActionBar = true;
        }
        if (touchIsFromActionBar && shouldDispatchEventToRecyclerView(ev)) {
            if (!listUtils.isEmpty(cachedEventList)) {
                for (MotionEvent motionEvent : cachedEventList) {
                    recyclerView.dispatchTouchEvent(motionEvent);
                }
                cachedEventList.clear();
            }
            recyclerView.dispatchTouchEvent(ev);
        }
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            cachedEventList.clear();
            yTranslationThresholdPassed = false;
            touchIsFromActionBar = false;
        }
        return false;
    }

    private boolean shouldDispatchEventToRecyclerView(MotionEvent event) {
        if (yTranslationThresholdPassed) {
            return true;
        } else if (listUtils.isEmpty(cachedEventList)) {
            cachedEventList.add(MotionEvent.obtain(event));
            return false;
        }

        int yTranslationThreshold = 2;

        MotionEvent lastEvent = listUtils.getLast(cachedEventList);
        if (Math.abs(lastEvent.getY() - event.getY()) > yTranslationThreshold) {
            yTranslationThresholdPassed = true;
            return true;
        } else {
            cachedEventList.add(MotionEvent.obtain(event));
            return false;
        }
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        if (!touchIsFromActionBar) {
            super.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }
    ...
}

名为actionBarWrapperViewGroup的ViewGroup是样本中的流布局。

和CustomFrameLayout的xml:

<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <RecyclerView
            android:id="@+id/rv_details"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />

    <LinearLayout
            android:id="@+id/ll_details_action_bar_wrapper"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

        ...

        <FrameLayout
            android:id="@+id/ll_details_action_bar_container"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="@dimen/default_bar_height"
            android:background="?colorPrimary"/>

        ...
    </LinearLayout>
</merge>

它是从我的项目中复制的,因此名称可能会产生误导,但我认为这是可以理解的。如果您有任何疑问,请继续。