为了解决滑动事件的冲突需要重写ScrollView
/* * Copyright 2014 Soichiro Kashima * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package observablescrollview; import android.content.Context; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.ScrollView; import java.util.ArrayList; import java.util.List; /** * ScrollView that its scroll position can be observed. */ public class ObservableScrollView extends ScrollView implements Scrollable { // Fields that should be saved onSaveInstanceState private int mPrevScrollY; private int mScrollY; // Fields that don't need to be saved onSaveInstanceState private ObservableScrollViewCallbacks mCallbacks; private List<ObservableScrollViewCallbacks> mCallbackCollection; private ScrollState mScrollState; private boolean mFirstScroll; private boolean mDragging; private boolean mIntercepted; private MotionEvent mPrevMoveEvent; private ViewGroup mTouchInterceptionViewGroup; public void setViewMove(boolean viewMove) { isViewMove = viewMove; } private boolean isViewMove=true; private int downX; private int downY; private int mTouchSlop; public ObservableScrollView(Context context) { super(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; mPrevScrollY = ss.prevScrollY; mScrollY = ss.scrollY; super.onRestoreInstanceState(ss.getSuperState()); } @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.prevScrollY = mPrevScrollY; ss.scrollY = mScrollY; return ss; } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (hasNoCallbacks()) { return; } mScrollY = t; dispatchOnScrollChanged(t, mFirstScroll, mDragging); if (mFirstScroll) { mFirstScroll = false; } if (mPrevScrollY < t) { mScrollState = ScrollState.UP; } else if (t < mPrevScrollY) { mScrollState = ScrollState.DOWN; //} else { // Keep previous state while dragging. // Never makes it STOP even if scrollY not changed. // Before Android 4.4, onTouchEvent calls onScrollChanged directly for ACTION_MOVE, // which makes mScrollState always STOP when onUpOrCancelMotionEvent is called. // STOP state is now meaningless for ScrollView. } mPrevScrollY = t; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (hasNoCallbacks()) { return super.onInterceptTouchEvent(ev); } switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: // Whether or not motion events are consumed by children, // flag initializations which are related to ACTION_DOWN events should be executed. // Because if the ACTION_DOWN is consumed by children and only ACTION_MOVEs are // passed to parent (this view), the flags will be invalid. // Also, applications might implement initialization codes to onDownMotionEvent, // so call it here. downX = (int) ev.getRawX(); downY = (int) ev.getRawY(); mFirstScroll = mDragging = true; dispatchOnDownMotionEvent(); break; case MotionEvent.ACTION_MOVE: if(!isViewMove){ int moveY = (int) ev.getRawY(); if (Math.abs(moveY - downY) > mTouchSlop) { return true; } } } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent ev) { if (hasNoCallbacks()) { return super.onTouchEvent(ev); } switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIntercepted = false; mDragging = false; dispatchOnUpOrCancelMotionEvent(mScrollState); break; case MotionEvent.ACTION_MOVE: if (mPrevMoveEvent == null) { mPrevMoveEvent = ev; } float diffY = ev.getY() - mPrevMoveEvent.getY(); mPrevMoveEvent = MotionEvent.obtainNoHistory(ev); if (getCurrentScrollY() - diffY <= 0) { // Can't scroll anymore. if (mIntercepted) { // Already dispatched ACTION_DOWN event to parents, so stop here. return false; } // Apps can set the interception target other than the direct parent. final ViewGroup parent; if (mTouchInterceptionViewGroup == null) { parent = (ViewGroup) getParent(); } else { parent = mTouchInterceptionViewGroup; } // Get offset to parents. If the parent is not the direct parent, // we should aggregate offsets from all of the parents. float offsetX = 0; float offsetY = 0; for (View v = this; v != null && v != parent; v = (View) v.getParent()) { offsetX += v.getLeft() - v.getScrollX(); offsetY += v.getTop() - v.getScrollY(); } final MotionEvent event = MotionEvent.obtainNoHistory(ev); event.offsetLocation(offsetX, offsetY); if (parent.onInterceptTouchEvent(event)) { mIntercepted = true; // If the parent wants to intercept ACTION_MOVE events, // we pass ACTION_DOWN event to the parent // as if these touch events just have began now. event.setAction(MotionEvent.ACTION_DOWN); // Return this onTouchEvent() first and set ACTION_DOWN event for parent // to the queue, to keep events sequence. post(new Runnable() { @Override public void run() { parent.dispatchTouchEvent(event); } }); return false; } // Even when this can't be scrolled anymore, // simply returning false here may cause subView's click, // so delegate it to super. return super.onTouchEvent(ev); } break; } return super.onTouchEvent(ev); } @Override public void setScrollViewCallbacks(ObservableScrollViewCallbacks listener) { mCallbacks = listener; } @Override public void addScrollViewCallbacks(ObservableScrollViewCallbacks listener) { if (mCallbackCollection == null) { mCallbackCollection = new ArrayList<>(); } mCallbackCollection.add(listener); } @Override public void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener) { if (mCallbackCollection != null) { mCallbackCollection.remove(listener); } } @Override public void clearScrollViewCallbacks() { if (mCallbackCollection != null) { mCallbackCollection.clear(); } } @Override public void setTouchInterceptionViewGroup(ViewGroup viewGroup) { mTouchInterceptionViewGroup = viewGroup; } @Override public void scrollVerticallyTo(int y) { scrollTo(0, y); } @Override public int getCurrentScrollY() { return mScrollY; } private void dispatchOnDownMotionEvent() { if (mCallbacks != null) { mCallbacks.onDownMotionEvent(); } if (mCallbackCollection != null) { for (int i = 0; i < mCallbackCollection.size(); i++) { ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); callbacks.onDownMotionEvent(); } } } private void dispatchOnScrollChanged(int scrollY, boolean firstScroll, boolean dragging) { if (mCallbacks != null) { mCallbacks.onScrollChanged(scrollY, firstScroll, dragging); } if (mCallbackCollection != null) { for (int i = 0; i < mCallbackCollection.size(); i++) { ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); callbacks.onScrollChanged(scrollY, firstScroll, dragging); } } } private void dispatchOnUpOrCancelMotionEvent(ScrollState scrollState) { if (mCallbacks != null) { mCallbacks.onUpOrCancelMotionEvent(scrollState); } if (mCallbackCollection != null) { for (int i = 0; i < mCallbackCollection.size(); i++) { ObservableScrollViewCallbacks callbacks = mCallbackCollection.get(i); callbacks.onUpOrCancelMotionEvent(scrollState); } } } private boolean hasNoCallbacks() { return mCallbacks == null && mCallbackCollection == null; } static class SavedState extends BaseSavedState { int prevScrollY; int scrollY; /** * Called by onSaveInstanceState. */ SavedState(Parcelable superState) { super(superState); } /** * Called by CREATOR. */ private SavedState(Parcel in) { super(in); prevScrollY = in.readInt(); scrollY = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(prevScrollY); out.writeInt(scrollY); } public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } }
上面的是修改的 ScrollView,为了监听滑动的位置,和解决冲突。可以有效的解决RecyclerView 不需要滑动的冲突。贴上接口
/* * Copyright 2014 Soichiro Kashima * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package observablescrollview; import android.view.ViewGroup; /** * Interface for providing common API for observable and scrollable widgets. */ public interface Scrollable { /** * Set a callback listener.<br> * Developers should use {@link #addScrollViewCallbacks(ObservableScrollViewCallbacks)} * and {@link #removeScrollViewCallbacks(ObservableScrollViewCallbacks)}. * * @param listener Listener to set. */ @Deprecated void setScrollViewCallbacks(ObservableScrollViewCallbacks listener); /** * Add a callback listener. * * @param listener Listener to add. * @since 1.7.0 */ void addScrollViewCallbacks(ObservableScrollViewCallbacks listener); /** * Remove a callback listener. * * @param listener Listener to remove. * @since 1.7.0 */ void removeScrollViewCallbacks(ObservableScrollViewCallbacks listener); /** * Clear callback listeners. * * @since 1.7.0 */ void clearScrollViewCallbacks(); /** * Scroll vertically to the absolute Y.<br> * Implemented classes are expected to scroll to the exact Y pixels from the top, * but it depends on the type of the widget. * * @param y Vertical position to scroll to. */ void scrollVerticallyTo(int y); /** * Return the current Y of the scrollable view. * * @return Current Y pixel. */ int getCurrentScrollY(); /** * Set a touch motion event delegation ViewGroup.<br> * This is used to pass motion events back to parent view. * It's up to the implementation classes whether or not it works. * * @param viewGroup ViewGroup object to dispatch motion events. */ void setTouchInterceptionViewGroup(ViewGroup viewGroup); }上面只是列子,请大家自行的修改。