github地址
https://github.com/daimajia/AndroidSwipeLayout/
通过对daimajia 的SwipeLayout 中Library的修改和封装,特意多加了一个滑动监听器,让layout在滑动时对外传出相对屏幕顶部和左侧的滑动距离,方便制作其他动态UI
封装好的SwipeLayout.java
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; import android.support.annotation.Nullable; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.FrameLayout; import com.example.xyz.verticalscroll4.R; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class SwipeLayout extends FrameLayout { @Deprecated public static final int EMPTY_LAYOUT = -1; private static final int DRAG_LEFT = 1; private static final int DRAG_RIGHT = 2; private static final int DRAG_TOP = 4; private static final int DRAG_BOTTOM = 8; private static final DragEdge DefaultDragEdge = DragEdge.Right; private int mTouchSlop; private DragEdge mCurrentDragEdge = DefaultDragEdge; private ViewDragHelper mDragHelper; private int mDragDistance = 0; private LinkedHashMap<DragEdge, View> mDragEdges = new LinkedHashMap<>(); private ShowMode mShowMode; private float[] mEdgeSwipesOffset = new float[4]; private List<SwipeListener> mSwipeListeners = new ArrayList<>(); private List<OnSwipeScrollListener> mOnSwipeScrollListeners=new ArrayList<>(); private List<SwipeDenier> mSwipeDeniers = new ArrayList<>(); private Map<View, ArrayList<OnRevealListener>> mRevealListeners = new HashMap<>(); private Map<View, Boolean> mShowEntirely = new HashMap<>(); private Map<View, Rect> mViewBoundCache = new HashMap<>();//save all children's bound, restore in onLayout private DoubleClickListener mDoubleClickListener; private boolean mSwipeEnabled = true; private boolean[] mSwipesEnabled = new boolean[]{ true, true, true, true}; private boolean mClickToClose = false; private float mWillOpenPercentAfterOpen=0.75f; private float mWillOpenPercentAfterClose=0.25f; public enum DragEdge { Left, Top, Right, Bottom } public enum ShowMode { LayDown, PullOut } public SwipeLayout(Context context) { this(context, null); } public SwipeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SwipeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); mDragHelper = ViewDragHelper.create(this, mDragHelperCallback); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeLayout); int dragEdgeChoices = a.getInt(R.styleable.SwipeLayout_drag_edge, DRAG_RIGHT); mEdgeSwipesOffset[DragEdge.Left.ordinal()] = a.getDimension(R.styleable.SwipeLayout_leftEdgeSwipeOffset, 0); mEdgeSwipesOffset[DragEdge.Right.ordinal()] = a.getDimension(R.styleable.SwipeLayout_rightEdgeSwipeOffset, 0); mEdgeSwipesOffset[DragEdge.Top.ordinal()] = a.getDimension(R.styleable.SwipeLayout_topEdgeSwipeOffset, 0); mEdgeSwipesOffset[DragEdge.Bottom.ordinal()] = a.getDimension(R.styleable.SwipeLayout_bottomEdgeSwipeOffset, 0); setClickToClose(a.getBoolean(R.styleable.SwipeLayout_clickToClose, mClickToClose)); if ((dragEdgeChoices & DRAG_LEFT) == DRAG_LEFT) { mDragEdges.put(DragEdge.Left, null); } if ((dragEdgeChoices & DRAG_TOP) == DRAG_TOP) { mDragEdges.put(DragEdge.Top, null); } if ((dragEdgeChoices & DRAG_RIGHT) == DRAG_RIGHT) { mDragEdges.put(DragEdge.Right, null); } if ((dragEdgeChoices & DRAG_BOTTOM) == DRAG_BOTTOM) { mDragEdges.put(DragEdge.Bottom, null); } int ordinal = a.getInt(R.styleable.SwipeLayout_show_mode, ShowMode.PullOut.ordinal()); mShowMode = ShowMode.values()[ordinal]; a.recycle(); } /**listener**/ public interface SwipeListener { void onStartOpen(SwipeLayout layout);//开始滑动到新一页 void onOpen(SwipeLayout layout);//已经打开新一页 void onStartClose(SwipeLayout layout);//开始关闭新页 void onClose(SwipeLayout layout);//已经关闭新页 void onUpdate(SwipeLayout layout, int leftOffset, int topOffset);//U刷新显示,传出相对于左边和顶部的滑动距离 void onHandRelease(SwipeLayout layout, float xvel, float yvel);//释放监听 void onScrollY(SwipeLayout layout, int top);//纵向滑动监听,传出相对于顶端的滑动距离 } public void addSwipeListener(SwipeListener l) { mSwipeListeners.add(l); } public void removeSwipeListener(SwipeListener l) { mSwipeListeners.remove(l); } public void removeAllSwipeListener() { mSwipeListeners.clear(); } /*滑动监听器*/ public interface OnSwipeScrollListener{ //SwipeLayout滑动监听 void onScroll(SwipeLayout layout, int leftOffset, int topOffset); } public void setOnSwipeScrollListener(OnSwipeScrollListener l){ //注册滑动监听器 mOnSwipeScrollListeners.add(l); } public void removeOnSwipeScrollListener(OnSwipeScrollListener l){ //移除滑动监听器 mOnSwipeScrollListeners.remove(l); } public void removeAllOnSwipeScrollListener() { //移除所有滑动监听器 mOnSwipeScrollListeners.clear(); } public interface SwipeDenier { /* * Called in onInterceptTouchEvent Determines if this swipe event should * be denied Implement this interface if you are using views with swipe * gestures As a child of SwipeLayout * * @return true deny false allow */ boolean shouldDenySwipe(MotionEvent ev); } public void addSwipeDenier(SwipeDenier denier) { mSwipeDeniers.add(denier); } public void removeSwipeDenier(SwipeDenier denier) { mSwipeDeniers.remove(denier); } public void removeAllSwipeDeniers() { mSwipeDeniers.clear(); } public interface OnRevealListener { void onReveal(View child, DragEdge edge, float fraction, int distance); } /** * bind a view with a specific * * @param childId the view id. * @param l the target * */ public void addRevealListener(int childId, OnRevealListener l) { View child = findViewById(childId); if (child == null) { throw new IllegalArgumentException("Child does not belong to SwipeListener."); } if (!mShowEntirely.containsKey(child)) { mShowEntirely.put(child, false); } if (mRevealListeners.get(child) == null) mRevealListeners.put(child, new ArrayList<OnRevealListener>()); mRevealListeners.get(child).add(l); } private ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() { @Override public int clampViewPositionHorizontal(View child, int left, int dx) { if (child == getSurfaceView()) { switch (mCurrentDragEdge) { case Top: case Bottom: return getPaddingLeft(); case Left: if (left < getPaddingLeft()) return getPaddingLeft(); if (left > getPaddingLeft() + mDragDistance) return getPaddingLeft() + mDragDistance; break; case Right: if (left > getPaddingLeft()) return getPaddingLeft(); if (left < getPaddingLeft() - mDragDistance) return getPaddingLeft() - mDragDistance; break; } } else if (getCurrentBottomView() == child) { switch (mCurrentDragEdge) { case Top: case Bottom: return getPaddingLeft(); case Left: if (mShowMode == ShowMode.PullOut) { if (left > getPaddingLeft()) return getPaddingLeft(); } break; case Right: if (mShowMode == ShowMode.PullOut) { if (left < getMeasuredWidth() - mDragDistance) { return getMeasuredWidth() - mDragDistance; } } break; } } return left; } @Override public int clampViewPositionVertical(View child, int top, int dy) { if (child == getSurfaceView()) { switch (mCurrentDragEdge) { case Left: case Right: return getPaddingTop(); case Top: if (top < getPaddingTop()) return getPaddingTop(); if (top > getPaddingTop() + mDragDistance) return getPaddingTop() + mDragDistance; break; case Bottom: if (top < getPaddingTop() - mDragDistance) { return getPaddingTop() - mDragDistance; } if (top > getPaddingTop()) { return getPaddingTop(); } } } else { View surfaceView = getSurfaceView(); int surfaceViewTop = surfaceView == null ? 0 : surfaceView.getTop(); switch (mCurrentDragEdge) { case Left: case Right: return getPaddingTop(); case Top: if (mShowMode == ShowMode.PullOut) { if (top > getPaddingTop()) return getPaddingTop(); } else { if (surfaceViewTop + dy < getPaddingTop()) return getPaddingTop(); if (surfaceViewTop + dy > getPaddingTop() + mDragDistance) return getPaddingTop() + mDragDistance; } break; case Bottom: if (mShowMode == ShowMode.PullOut) { if (top < getMeasuredHeight() - mDragDistance)