SmartSwipe框架的抽屉效果有两个消费者,分别是:
DrawerConsumer
和SlidingConsumer
。他们的区别是:
DrawerConsumer
:抽屉显示在被包裹的控件上层
SlidingConsumer
:抽屉显示在被包裹的控件下层
(1)DrawerConsumer的使用
代码如下:
DrawerConsumer.java
/**
* contains content view and at most 4 drawer view
* drawer view shows above content view
* default release mode is {@link #RELEASE_MODE_AUTO_OPEN_CLOSE}
*
* @author billy.qi
*/
public class DrawerConsumer extends SwipeConsumer implements View.OnClickListener {
protected final View[] mDrawerViews = new View[4];
protected View mCurDrawerView;
protected int l, t, r, b;
protected int mScrimColor = 0;
protected int mShadowColor = 0;
protected ScrimView mScrimView;
protected int mShadowSize;
protected boolean mDrawerViewRequired = true;
protected boolean mShowScrimAndShadowOutsideContentView;
public DrawerConsumer() {
//set default release mode
setReleaseMode(SwipeConsumer.RELEASE_MODE_AUTO_OPEN_CLOSE);
}
@Override
public void onAttachToWrapper(SmartSwipeWrapper wrapper, SwipeHelper swipeHelper) {
super.onAttachToWrapper(wrapper, swipeHelper);
for (int i = 0; i < mDrawerViews.length; i++) {
attachDrawerView(i);
}
if (mShadowSize == 0) {
//10dp by default
mShadowSize = SmartSwipe.dp2px(10, wrapper.getContext());
}
}
@Override
protected void initChildrenFormXml() {
final SmartSwipeWrapper wrapper = mWrapper;
int childCount = wrapper.getChildCount();
View contentView = wrapper.getContentView();
for (int i = 0; i < childCount; i++) {
View child = wrapper.getChildAt(i);
if (child == contentView || !(child.getLayoutParams() instanceof SmartSwipeWrapper.LayoutParams)) {
continue;
}
final int gravity = ((SmartSwipeWrapper.LayoutParams) child.getLayoutParams()).gravity;
if (mDrawerViews[0] == null && (gravity & DIRECTION_LEFT) == DIRECTION_LEFT) {
// This child is a left drawer
setLeftDrawerView(child);
mWrapper.consumeInflateFromXml();
}
if (mDrawerViews[1] == null && (gravity & DIRECTION_RIGHT) == DIRECTION_RIGHT) {
// This child is a right drawer
setRightDrawerView(child);
mWrapper.consumeInflateFromXml();
}
if (mDrawerViews[2] == null && (gravity & DIRECTION_TOP) == DIRECTION_TOP) {
// This child is a top drawer
setTopDrawerView(child);
mWrapper.consumeInflateFromXml();
}
if (mDrawerViews[3] == null && (gravity & DIRECTION_BOTTOM) == DIRECTION_BOTTOM) {
// This child is a bottom drawer
setBottomDrawerView(child);
mWrapper.consumeInflateFromXml();
}
}
}
@Override
public void onDetachFromWrapper() {
super.onDetachFromWrapper();
if (mScrimView != null) {
mWrapper.removeView(mScrimView);
mScrimView.setOnClickListener(null);
mScrimView = null;
}
for (View drawerView : mDrawerViews) {
if (drawerView != null) {
mWrapper.removeView(drawerView);
}
}
mCurDrawerView = null;
}
@Override
protected void onOpened() {
super.onOpened();
if (mScrimView != null && !mShowScrimAndShadowOutsideContentView) {
mScrimView.setOnClickListener(this);
}
}
@Override
protected void onClosed() {
super.onClosed();
if (mCurDrawerView != null) {
changeDrawerViewVisibility(INVISIBLE);
}
if (mScrimView != null) {
mScrimView.setOnClickListener(null);
mScrimView.setClickable(false);
mScrimView.setFocusable(false);
mScrimView.setVisibility(GONE);
}
}
@Override
public boolean tryAcceptMoving(int pointerId, float downX, float downY, float dx, float dy) {
boolean handle = super.tryAcceptMoving(pointerId, downX, downY, dx, dy);
if (handle && mCachedSwipeDistanceX == 0 && mCachedSwipeDistanceY == 0) {
if (mDrawerViewRequired && getDrawerView(mDirection) == null) {
handle = false;
}
}
return handle;
}
@Override
public void onSwipeAccepted(int activePointerId, boolean settling, float initialMotionX, float initialMotionY) {
if (mCachedSwipeDistanceX == 0 && mCachedSwipeDistanceY == 0) {
changeDrawerViewVisibility(INVISIBLE);
mCurDrawerView = getDrawerView(mDirection);
changeDrawerViewVisibility(VISIBLE);
}
int w = mWidth;
int h = mHeight;
if (mCurDrawerView != null) {
w = mCurDrawerView.getMeasuredWidth();
h = mCurDrawerView.getMeasuredHeight();
} else if (mDrawerViewRequired) {
return;
}
if (!mOpenDistanceSpecified) {
if ((mDirection & DIRECTION_HORIZONTAL) > 0) {
mOpenDistance = w;
} else {
mOpenDistance = h;
}
}
calculateDrawerDirectionInitPosition(mDirection, w, h);
changeDrawerViewVisibility(VISIBLE);
initScrimView();
layoutChildren();
orderChildren();
super.onSwipeAccepted(activePointerId, settling, initialMotionX, initialMotionY);
}
protected void changeDrawerViewVisibility(int visibility) {
if (mCurDrawerView != null) {
mCurDrawerView.setVisibility(visibility);
}
}
@Override
public void setCurrentStateAsClosed() {
mCurDrawerView = null;
super.setCurrentStateAsClosed();
}
protected void initScrimView() {
if (mScrimColor != 0 || mShadowColor != 0 && mShadowSize > 0) {
if (mScrimView == null) {
mScrimView = new ScrimView(mWrapper.getContext());
mWrapper.addView(mScrimView);
}
mScrimView.setScrimColor(mScrimColor);
if (mShadowColor != 0 && mShadowSize > 0) {
int shadowDirection = this.mDirection;
if (mShowScrimAndShadowOutsideContentView) {
shadowDirection = SwipeUtil.getReverseDirection(mDirection);
}
mScrimView.setDirection(this.mDirection, mShadowColor, shadowDirection, mShadowSize, mWidth, mHeight);
}
mScrimView.setVisibility(VISIBLE);
}
}
protected void calculateDrawerDirectionInitPosition(int direction, int w, int h) {
switch (direction) {
case DIRECTION_LEFT: l = -w; r = l + w; t = 0; b = h; break;
case DIRECTION_RIGHT: l = mWidth; r = l + w; t = 0; b = h; break;
case DIRECTION_TOP: l = 0; r = mWidth; t = -h; b = t + h; break;
case DIRECTION_BOTTOM: l = 0; r = mWidth; t = mHeight;b = t + h; break;
default: break;
}
}
@Override
public boolean onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mWrapper != null) {
layoutChildren();
return true;
}
return false;
}
@Override
protected void onDisplayDistanceChanged(int distanceXToDisplay, int distanceYToDisplay, int dx, int dy) {
View drawerView = mCurDrawerView;
if (drawerView != null && drawerView.getParent() == mWrapper) {
boolean horizontal = (mDirection & DIRECTION_HORIZONTAL) > 0;
if (horizontal) {
ViewCompat.offsetLeftAndRight(drawerView, dx);
} else {
ViewCompat.offsetTopAndBottom(drawerView, dy);
}
layoutScrimView();
}
}
protected void orderChildren() {
if (mCurDrawerView != null) {
mCurDrawerView.bringToFront();
}
if (mScrimView != null) {
mScrimView.bringToFront();
}
}
protected void layoutChildren() {
layoutContentView(mWrapper.getContentView());
layoutDrawerView();
layoutScrimView();
}
protected void layoutContentView(View contentView) {
if (contentView != null) {
contentView.layout(0, 0, mWidth, mHeight);
}
}
protected void layoutDrawerView() {
if (mCurDrawerView != null && mCurDrawerView.getVisibility() == VISIBLE) {
mCurDrawerView.layout(l + mCurDisplayDistanceX, t + mCurDisplayDistanceY, r + mCurDisplayDistanceX, b + mCurDisplayDistanceY);
}
}
protected void layoutScrimView() {
if (mScrimView != null && mScrimView.getVisibility() == VISIBLE) {
int l = 0, r = mWidth, t = 0, b = mHeight;
if (mShowScrimAndShadowOutsideContentView) {
switch (mDirection) {
case DIRECTION_LEFT: r = mCurDisplayDistanceX; break;
case DIRECTION_RIGHT: l = r + mCurDisplayDistanceX; break;
case DIRECTION_TOP: b = mCurDisplayDistanceY; break;
case DIRECTION_BOTTOM: t = b + mCurDisplayDistanceY; break;
default:
}
} else {
switch (mDirection) {
case DIRECTION_LEFT: l = mCurDisplayDistanceX; break;
case DIRECTION_RIGHT: r = r + mCurDisplayDistanceX; break;
case DIRECTION_TOP: t = mCurDisplayDistanceY; break;
case DIRECTION_BOTTOM: b = b + mCurDisplayDistanceY; break;
default:
}
}
mScrimView.layout(l, t, r, b);
mScrimView.setProgress(mShowScrimAndShadowOutsideContentView ? (1 - mProgress) : mProgress);
}
}
@Override
protected void notifySwipeStart() {
if (mCurDrawerView instanceof SwipeListener) {
((SwipeListener)mCurDrawerView).onSwipeStart(mWrapper, this, mDirection);
}
super.notifySwipeStart();
}
@Override
protected void notifySwipeProgress(boolean settling) {
if (mCurDrawerView instanceof SwipeListener) {
((SwipeListener) mCurDrawerView).onSwipeProcess(mWrapper, this, mDirection, settling, mProgress);
}
super.notifySwipeProgress(settling);
}
@Override
protected void notifySwipeRelease(float xVelocity, float yVelocity) {
if (mCurDrawerView instanceof SwipeListener) {
((SwipeListener) mCurDrawerView).onSwipeRelease(mWrapper, this, mDirection, mProgress, xVelocity, yVelocity);
}
super.notifySwipeRelease(xVelocity, yVelocity);
}
public View getDrawerView(int direction) {
int viewIndex = -1;
switch (direction) {
default: break;
case DIRECTION_LEFT: viewIndex = 0; break;
case DIRECTION_RIGHT: viewIndex = 1; break;
case DIRECTION_TOP: viewIndex = 2; break;
case DIRECTION_BOTTOM: viewIndex = 3; break;
}
if (viewIndex < 0) {
return null;
}
return mDrawerViews[viewIndex];
}
public DrawerConsumer setLeftDrawerView(View drawerView) {
return setDrawerView(DIRECTION_LEFT, drawerView);
}
public DrawerConsumer setRightDrawerView(View drawerView) {
return setDrawerView(DIRECTION_RIGHT, drawerView);
}
public DrawerConsumer setTopDrawerView(View drawerView) {
return setDrawerView(DIRECTION_TOP, drawerView);
}
public DrawerConsumer setBottomDrawerView(View drawerView) {
return setDrawerView(DIRECTION_BOTTOM, drawerView);
}
public DrawerConsumer setHorizontalDrawerView(View drawerView) {
return setDrawerView(DIRECTION_HORIZONTAL, drawerView);
}
public DrawerConsumer setVerticalDrawerView(View drawerView) {
return setDrawerView(DIRECTION_VERTICAL, drawerView);
}
public DrawerConsumer setAllDirectionDrawerView(View drawerView) {
return setDrawerView(DIRECTION_ALL, drawerView);
}
/**
* set a extension to the direction, also set direction enable if drawerView is not null(otherwise, disable the direction)
* direction can be a single direction or mixed direction(eg: DIRECTION_LEFT | DIRECTION_RIGHT)
* @param direction direction to set
* @param drawerView view
* @return this
*/
public DrawerConsumer setDrawerView(int direction, View drawerView) {
enableDirection(direction, drawerView != null);
if ((direction & DIRECTION_LEFT) > 0) {
setOrUpdateDrawerView(0, drawerView);
}
if ((direction & DIRECTION_RIGHT) > 0) {
setOrUpdateDrawerView(1, drawerView);
}
if ((direction & DIRECTION_TOP) > 0) {
setOrUpdateDrawerView(2, drawerView);
}
if ((direction & DIRECTION_BOTTOM) > 0) {
setOrUpdateDrawerView(3, drawerView);
}
return this;
}
private void setOrUpdateDrawerView(int index, View drawerView) {
View oldView = mDrawerViews[index];
if (oldView == drawerView) {
return;
}
mDrawerViews[index] = drawerView;
attachDrawerView(index);
}
private void attachDrawerView(final int index) {
final View drawerView = mDrawerViews[index];
final SmartSwipeWrapper wrapper = mWrapper;
if (drawerView != null && wrapper != null && drawerView.getParent() != wrapper) {
if (drawerView.getParent() != null) {
((ViewGroup)drawerView.getParent()).removeView(drawerView);
}
int contentViewIndex = wrapper.indexOfChild(wrapper.getContentView());
if (contentViewIndex >= 0) {
ViewGroup.LayoutParams lp = drawerView.getLayoutParams();
if (lp == null) {
int w = FrameLayout.LayoutParams.WRAP_CONTENT, h = FrameLayout.LayoutParams.WRAP_CONTENT;
switch (index) {
default: break;
case 0: case 1: h = FrameLayout.LayoutParams.MATCH_PARENT; break;
case 2: case 3: w = FrameLayout.LayoutParams.MATCH_PARENT; break;
}
lp = new FrameLayout.LayoutParams(w, h);
drawerView.setLayoutParams(lp);
}
wrapper.addView(drawerView, contentViewIndex);
drawerView.setVisibility(INVISIBLE);
}
}
}
@Override
public int getOpenDistance() {
if (mCurDrawerView == null) {
return super.getOpenDistance();
}
if ((mDirection & DIRECTION_HORIZONTAL) > 0) {
return mCurDrawerView.getMeasuredWidth();
}
return mCurDrawerView.getMeasuredHeight();
}
/**
* Set a color to use for the scrim that obscures primary content while a drawer is open.
* @param color Color to use in 0xAARRGGBB format.
* @return this
*/
public DrawerConsumer setScrimColor(int color) {
mScrimColor = color;
return this;
}
/**
* Set a color to use for the shadow at the edge of content view while a drawer is open.
* @param shadowColor Color to use in 0xAARRGGBB format.
* @return this
*/
public DrawerConsumer setShadowColor(int shadowColor) {
mShadowColor = shadowColor;
return this;
}
public int getShadowSize() {
return mShadowSize;
}
/**
* set the size of shadow at the edge of content view while a drawer is open.
* @param size shadow size in pixel
* @return this
*/
public DrawerConsumer setShadowSize(int size) {
this.mShadowSize = size;
return this;
}
public boolean isDrawerViewRequired() {
return mDrawerViewRequired;
}
/**
* set the extension view as drawer is required or not
* it useful inside this sdk framework,
* developers who use this SDK do not call this function unless you really know what its mean
* @param required required or not
* @return this
*/
public DrawerConsumer setDrawerViewRequired(boolean required) {
this.mDrawerViewRequired = required;
return this;
}
public boolean isScrimAndShadowOutsideContentView() {
return mShowScrimAndShadowOutsideContentView;
}
public DrawerConsumer showScrimAndShadowOutsideContentView() {
this.mShowScrimAndShadowOutsideContentView = true;
return this;
}
public DrawerConsumer showScrimAndShadowInsideContentView() {
this.mShowScrimAndShadowOutsideContentView = false;
return this;
}
@Override
public void onClick(View v) {
if (getDragState() == SwipeHelper.STATE_IDLE && !mShowScrimAndShadowOutsideContentView && v == mScrimView) {
smoothClose();
}
}
}
抽屉的创建
//--- 横向抽屉布局的创建(左滑和右滑的抽屉)
View horizontalMenu = LayoutInflater.from(this).inflate(R.layout.layout_main_menu, null);
horizontalMenu.setLayoutParams(new ViewGroup.LayoutParams(500, ViewGroup.LayoutParams.MATCH_PARENT));
SmartSwipeWrapper horizontalMenuWrapper = SmartSwipe.wrap(horizontalMenu);
//--- 左边的抽屉
View leftMenu = LayoutInflater.from(this).inflate(R.layout.layout_main_menu, null);
leftMenu.setLayoutParams(new ViewGroup.LayoutParams(500, ViewGroup.LayoutParams.MATCH_PARENT));
SmartSwipeWrapper leftMenuWrapper = SmartSwipe.wrap(leftMenu);
//--- 右边的抽屉
View rightMenu = LayoutInflater.from(this).inflate(R.layout.layout_main_menu, null);
rightMenu.setLayoutParams(new ViewGroup.LayoutParams(500, ViewGroup.LayoutParams.MATCH_PARENT));
SmartSwipeWrapper rightMenuWrapper = SmartSwipe.wrap(rightMenu);
//--- 上边的抽屉
View topMenu = LayoutInflater.from(this).inflate(R.layout.layout_main_menu, null);
topMenu.setLayoutParams(new ViewGroup.LayoutParams(500, ViewGroup.LayoutParams.MATCH_PARENT));
SmartSwipeWrapper topMenuWrapper = SmartSwipe.wrap(topMenu);
//--- 下边的抽屉
View bottomMenu = LayoutInflater.from(this).inflate(R.layout.layout_main_menu, null);
bottomMenu.setLayoutParams(new ViewGroup.LayoutParams(500, ViewGroup.LayoutParams.MATCH_PARENT));
SmartSwipeWrapper bottomMenuWrapper = SmartSwipe.wrap(horizontalMenu);
抽屉打开或者关闭的监听
SimpleSwipeListener listener = new SimpleSwipeListener() {
@Override
public void onSwipeOpened(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
super.onSwipeOpened(wrapper, consumer, direction);
}
@Override
public void onSwipeClosed(SmartSwipeWrapper wrapper, SwipeConsumer consumer, int direction) {
super.onSwipeClosed(wrapper, consumer, direction);
}
};
抽屉消费者对象的创建与属性
mDrawerConsumer = new DrawerConsumer()
//横向布局的抽屉
//.setHorizontalDrawerView(horizontalMenuWrapper)
//仅显示左边抽屉
.setLeftDrawerView(leftMenuWrapper)
//仅显示右边抽屉
.setRightDrawerView(rightMenuWrapper)
//仅显示上边抽屉
//.setTopDrawerView(topMenuWrapper)
//仅显示下边抽屉
.setBottomDrawerView(bottomMenu)
//设置遮罩层背景颜色,默认透明
.setScrimColor(0x80A7BBBB)
//设置边框阴影颜色,默认透明
.setShadowColor(0x80CAA639)
//设置边框阴影大小
.setShadowSize(SmartSwipe.dp2px(20, this))
//设置监听
.addListener(listener)
//设置边的大小,手指可以在某个方位的20dp范围内触发侧滑事件
.setEdgeSize(SmartSwipe.dp2px(20, this))
//将SwipeConsumer类型转换为DrawerConsumer类型
.as(DrawerConsumer.class);
将消费者添加到某View上
SmartSwipe.wrap(this).addConsumer(mDrawerConsumer);
效果如下:
(2)SlidingConsumer的使用
SlidingConsumer
的使用方式和DrawerConsumer
一样,只要将DrawerConsumer
换成SlidingConsumer
就可以了,SlidingConsumer
代码如下:
SlidingConsumer.java
/**
* @author billy.qi
*/
public class SlidingConsumer extends DrawerConsumer {
/** contentView moves above the drawer view, drawer view show below contentView */
public static final float FACTOR_COVER = 0F;
/** Default factor, contentView moves and drawer view followed pixel by pixel */
public static final float FACTOR_FOLLOW = 1F;
protected float mRelativeMoveFactor = 0.5F;
protected boolean mEdgeAffinity;
protected boolean mDrawerExpandable;
protected int mDrawerW, mDrawerH;
@Override
public void onDetachFromWrapper() {
super.onDetachFromWrapper();
for (View drawerView : mDrawerViews) {
if (drawerView != null) {
drawerView.scrollTo(0, 0);
}
}
View contentView = mWrapper.getContentView();
if (contentView != null) {
contentView.layout(0, 0, mWidth, mHeight);
}
}
@Override
protected void calculateDrawerDirectionInitPosition(int direction, int w, int h) {
mDrawerW = w;
mDrawerH = h;
int initDistance = (int) (mOpenDistance * (1 - mRelativeMoveFactor) + 0.5F);
switch (direction) {
case DIRECTION_LEFT:
l = -w + initDistance;
if (mEdgeAffinity && l > 0) {
l = 0;
}
r = l + w; t = 0; b = h;
break;
case DIRECTION_RIGHT:
l = mWidth - initDistance;
r = l + w; t = 0; b = h;
if (mEdgeAffinity && r < mWidth) {
r = mWidth;
l = r - w;
}
break;
case DIRECTION_TOP:
l = 0; r = mWidth; t = -h + initDistance;
if (mEdgeAffinity && t > 0) {
t = 0;
}
b = t + h; break;
case DIRECTION_BOTTOM:
l = 0; r = mWidth; t = mHeight - initDistance;b = t + h;
if (mEdgeAffinity && b < mHeight) {
b = mHeight;
t = b - h;
}
break;
default: break;
}
}
@Override
protected void onDisplayDistanceChanged(int distanceXToDisplay, int distanceYToDisplay, int dx, int dy) {
layoutChildren();
}
@Override
protected void orderChildren() {
View contentView = mWrapper.getContentView();
if (contentView != null) {
contentView.bringToFront();
}
if (mScrimView != null) {
mScrimView.bringToFront();
}
}
@Override
protected void layoutContentView(View contentView) {
if (contentView != null) {
contentView.layout(mCurDisplayDistanceX, mCurDisplayDistanceY, mWidth + mCurDisplayDistanceX, mHeight + mCurDisplayDistanceY);
}
}
@Override
protected void layoutDrawerView() {
View contentView = mWrapper.getContentView();
final View drawerView = mCurDrawerView;
if (contentView == null || drawerView == null) {
return;
}
if (drawerView.getVisibility() == VISIBLE) {
//layout drawer view
int rdx = (int) (mCurDisplayDistanceX * mRelativeMoveFactor + (mCurDisplayDistanceX > 0 ? 0.5f : -0.5f));
int rdy = (int) (mCurDisplayDistanceY * mRelativeMoveFactor + (mCurDisplayDistanceY > 0 ? 0.5f : -0.5f));
int scrollX = 0, scrollY = 0;
int left = l, top = t, right = r, bottom = b;
switch (mDirection) {
case DIRECTION_LEFT:
left = l + rdx;
right = contentView.getLeft();
if (!mDrawerExpandable) {
if (!mEdgeAffinity && right > mDrawerW) {
left = right - mDrawerW;
} else if (mEdgeAffinity && left > 0) {
left = 0;
}
if (right - left > mDrawerW) {
right = left + mDrawerW;
}
} else if (left > 0) {
left = 0;
}
break;
case DIRECTION_RIGHT:
left = contentView.getRight();
right = r + rdx;
if (!mDrawerExpandable) {
if (!mEdgeAffinity && left + mDrawerW < mWidth) {
right = left + mDrawerW;
} else if (mEdgeAffinity && right < mWidth) {
right = mWidth;
}
if (right - left > mDrawerW) {
left = right - mDrawerW;
}
} else if (right < mWidth) {
right = mWidth;
}
scrollX = (int) ((mOpenDistance + mCurDisplayDistanceX) * (1 - mRelativeMoveFactor));
scrollX = Math.max(scrollX, 0);
break;
case DIRECTION_TOP:
top = t + rdy;
bottom = contentView.getTop();
if (!mDrawerExpandable) {
if (!mEdgeAffinity && bottom > mDrawerH) {
top = bottom - mDrawerH;
} else if (mEdgeAffinity && top > 0) {
top = 0;
}
if (bottom - top > mDrawerH) {
bottom = top + mDrawerH;
}
} else if (top > 0) {
top = 0;
}
break;
case DIRECTION_BOTTOM:
top = contentView.getBottom();
bottom = b + rdy;
if (!mDrawerExpandable) {
if (!mEdgeAffinity && top + mDrawerH < mHeight) {
bottom = top + mDrawerH;
} else if (mEdgeAffinity && bottom < mHeight) {
bottom = mHeight;
}
if (bottom - top > mDrawerH) {
top = bottom - mDrawerH;
}
} else if (bottom < mHeight) {
bottom = mHeight;
}
scrollY = (int) ((mOpenDistance + mCurDisplayDistanceY) * (1 - mRelativeMoveFactor));
scrollY = Math.max(scrollY, 0);
break;
default: break;
}
drawerView.layout(left, top, right, bottom);
//compat for contentView background is transparent
// drawer view don`t overlap with contentView
drawerView.scrollTo(scrollX, scrollY);
}
}
public float getRelativeMoveFactor() {
return mRelativeMoveFactor;
}
/**
* Set the movement factor of drawer view relative to content view
* @param factor Multiplier of mCurDrawerView relative movement to mWrapper.getContentView().
* this value must to be between 0F and 1F.
* 0: drawer view not move
* 0~1:
* 1: drawer view followed contentView pixel by pixel
* @return this
*/
public SlidingConsumer setRelativeMoveFactor(float factor) {
this.mRelativeMoveFactor = SmartSwipe.ensureBetween(factor, FACTOR_COVER, FACTOR_FOLLOW);
return this;
}
public boolean isEdgeAffinity() {
return mEdgeAffinity;
}
/**
* set drawer is affinity to the edge or not
* <pre>
* this property make effect when {@link #mDrawerExpandable} is false and {@link #mSwipeMaxDistance} is bigger than drawerView`s size(width or height).
* it may be cause by:
* 1. {@link #mSwipeOpenDistance} is bigger than drawerView`s size via {@link #setOpenDistance(int)}
* 2. {@link #mOverSwipeFactor} is bigger than 0F via {@link #setOverSwipeFactor(float)}
* </pre>
* if set true, drawer view adsorbs to {@link com.billy.android.swipe.SmartSwipeWrapper}`s edge
* @param affinity adsorbs to edge or not
* @return this
* @see #setOpenDistance(int)
* @see #setOverSwipeFactor(float)
* @see #setDrawerExpandable(boolean)
*/
public SlidingConsumer setEdgeAffinity(boolean affinity) {
this.mEdgeAffinity = affinity;
return this;
}
public boolean isDrawerExpandable() {
return mDrawerExpandable;
}
/**
* determine whether drawers expandable.
* <pre>
* this property make effect when {@link #mSwipeMaxDistance} is bigger than drawerView`s size(width or height).
* it may be cause by:
* 1. {@link #mSwipeOpenDistance} is bigger than drawerView`s size via {@link #setOpenDistance(int)}
* 2. {@link #mOverSwipeFactor} is bigger than 0F via {@link #setOverSwipeFactor(float)}
* </pre>
* if set true, {@link #setEdgeAffinity(boolean)} will be ignored.
* @param expandable expandable or not
* @return this
* @see #setEdgeAffinity(boolean)
* @see #setOverSwipeFactor(float)
*/
public SlidingConsumer setDrawerExpandable(boolean expandable) {
this.mDrawerExpandable = expandable;
return this;
}
}
将消费者添加到某View上
mSlidingConsumer = new SlidingConsumer()
//横向布局的抽屉
//.setHorizontalDrawerView(horizontalMenuWrapper)
//仅显示左边抽屉
.setLeftDrawerView(leftMenuWrapper)
//仅显示右边抽屉
.setRightDrawerView(rightMenuWrapper)
//仅显示上边抽屉
//.setTopDrawerView(topMenuWrapper)
//仅显示下边抽屉
.setBottomDrawerView(bottomMenu)
//设置遮罩层背景颜色,默认透明
.setScrimColor(0x80A7BBBB)
//设置边框阴影颜色,默认透明
.setShadowColor(0x80CAA639)
//设置边框阴影大小
.setShadowSize(SmartSwipe.dp2px(20, this))
//设置监听
.addListener(listener)
//设置边的大小,手指可以在某个方位的20dp范围内触发侧滑事件
.setEdgeSize(SmartSwipe.dp2px(20, this))
//将SwipeConsumer类型转换为DrawerConsumer类型
.as(SlidingConsumer.class);
将消费者添加到某View上
SmartSwipe.wrap(this).addConsumer(mSlidingConsumer);
效果如下:
(3)侧滑布局的两个辅助类代码
ViewCompat.java
/**
* @author billy.qi
* @since 2019-05-23 11:25
*/
public class ViewCompat {
/**
* Indicates that the input type for the gesture is from a user touching the screen.
*/
public static final int TYPE_TOUCH = 0;
/**
* Indicates that the input type for the gesture is caused by something which is not a user
* touching a screen. This is usually from a fling which is settling.
*/
public static final int TYPE_NON_TOUCH = 1;
/**
* Indicates no axis of view scrolling.
*/
public static final int SCROLL_AXIS_NONE = 0;
/**
* Indicates scrolling along the horizontal axis.
*/
public static final int SCROLL_AXIS_HORIZONTAL = 1 << 0;
/**
* Indicates scrolling along the vertical axis.
*/
public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
private static ThreadLocal<Rect> sThreadLocalRect;
private static Rect getEmptyTempRect() {
if (sThreadLocalRect == null) {
sThreadLocalRect = new ThreadLocal<>();
}
Rect rect = sThreadLocalRect.get();
if (rect == null) {
rect = new Rect();
sThreadLocalRect.set(rect);
}
rect.setEmpty();
return rect;
}
/**
* <p>Cause an invalidate to happen on the next animation time step, typically the
* next display frame.</p>
*
* <p>This method can be invoked from outside of the UI thread
* only when this View is attached to a window.</p>
*
* @param view View to invalidate
*/
public static void postInvalidateOnAnimation(View view) {
if (Build.VERSION.SDK_INT >= 16) {
view.postInvalidateOnAnimation();
} else {
view.postInvalidate();
}
}
/**
* Offset this view's vertical location by the specified number of pixels.
* @param view view
* @param offset the number of pixels to offset the view by
*/
public static void offsetTopAndBottom(View view, int offset) {
if (view == null || offset == 0) {
return;
}
if (Build.VERSION.SDK_INT >= 23) {
view.offsetTopAndBottom(offset);
} else if (Build.VERSION.SDK_INT >= 21) {
final Rect parentRect = getEmptyTempRect();
boolean needInvalidateWorkaround = false;
final ViewParent parent = view.getParent();
if (parent instanceof View) {
final View p = (View) parent;
parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
// If the view currently does not currently intersect the parent (and is therefore
// not displayed) we may need need to invalidate
needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom());
}
// Now offset, invoking the API 14+ implementation (which contains its own workarounds)
compatOffsetTopAndBottom(view, offset);
// The view has now been offset, so let's intersect the Rect and invalidate where
// the View is now displayed
if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom())) {
((View) parent).invalidate(parentRect);
}
} else {
compatOffsetTopAndBottom(view, offset);
}
}
private static void compatOffsetTopAndBottom(View view, int offset) {
view.offsetTopAndBottom(offset);
if (view.getVisibility() == View.VISIBLE) {
tickleInvalidationFlag(view);
ViewParent parent = view.getParent();
if (parent instanceof View) {
tickleInvalidationFlag((View) parent);
}
}
}
/**
* Offset this view's horizontal location by the specified amount of pixels.
*
* @param view view
* @param offset the number of pixels to offset the view by
*/
public static void offsetLeftAndRight(View view, int offset) {
if (view == null || offset == 0) {
return;
}
if (Build.VERSION.SDK_INT >= 23) {
view.offsetLeftAndRight(offset);
} else if (Build.VERSION.SDK_INT >= 21) {
final Rect parentRect = getEmptyTempRect();
boolean needInvalidateWorkaround = false;
final ViewParent parent = view.getParent();
if (parent instanceof View) {
final View p = (View) parent;
parentRect.set(p.getLeft(), p.getTop(), p.getRight(), p.getBottom());
// If the view currently does not currently intersect the parent (and is therefore
// not displayed) we may need need to invalidate
needInvalidateWorkaround = !parentRect.intersects(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom());
}
// Now offset, invoking the API 14+ implementation (which contains its own workarounds)
compatOffsetLeftAndRight(view, offset);
// The view has now been offset, so let's intersect the Rect and invalidate where
// the View is now displayed
if (needInvalidateWorkaround && parentRect.intersect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom())) {
((View) parent).invalidate(parentRect);
}
} else {
compatOffsetLeftAndRight(view, offset);
}
}
private static void compatOffsetLeftAndRight(View view, int offset) {
view.offsetLeftAndRight(offset);
if (view.getVisibility() == View.VISIBLE) {
tickleInvalidationFlag(view);
ViewParent parent = view.getParent();
if (parent instanceof View) {
tickleInvalidationFlag((View) parent);
}
}
}
private static void tickleInvalidationFlag(View view) {
final float y = view.getTranslationY();
view.setTranslationY(y + 1);
view.setTranslationY(y);
}
/**
* <p>Convert script specific gravity to absolute horizontal value.</p>
*
* if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
* if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
*
*
* @param gravity The gravity to convert to absolute (horizontal) values.
* @param layoutDirection The layout direction.
* @return gravity converted to absolute (horizontal) values.
*/
public static int getAbsoluteGravity(int gravity, int layoutDirection) {
if (SDK_INT >= 17) {
return Gravity.getAbsoluteGravity(gravity, layoutDirection);
} else {
// Just strip off the relative bit to get LEFT/RIGHT.
return gravity & ~Gravity.RELATIVE_LAYOUT_DIRECTION;
}
}
public static int getLayoutDirection(View view) {
if (Build.VERSION.SDK_INT >= 17) {
return view.getLayoutDirection();
}
return View.LAYOUT_DIRECTION_LTR;
}
/**
* Check if the items in the list can be scrolled in a certain direction.
*
* @param listView listView
* @param direction Negative to check scrolling up, positive to check
* scrolling down.
* @return true if the list can be scrolled in the specified direction,
* false otherwise.
*/
public static boolean canListViewScrollVertical(AbsListView listView, int direction) {
if (Build.VERSION.SDK_INT >= 19) {
// Call the framework version directly
return listView.canScrollList(direction);
} else {
// provide backport on earlier versions
final int childCount = listView.getChildCount();
if (childCount == 0) {
return false;
}
final int firstPosition = listView.getFirstVisiblePosition();
if (direction > 0) {
final int lastBottom = listView.getChildAt(childCount - 1).getBottom();
final int lastPosition = firstPosition + childCount;
return lastPosition < listView.getCount()
|| (lastBottom > listView.getHeight() - listView.getListPaddingBottom());
} else {
final int firstTop = listView.getChildAt(0).getTop();
return firstPosition > 0 || firstTop < listView.getListPaddingTop();
}
}
}
}
ScrimView.java
/**
* @author billy.qi
*/
public class ScrimView extends View {
public static float MAX_PROGRESS = 1F;
public static float MIN_PROGRESS = 0F;
private int mSize = 60;
private final Paint mPaint;
private Rect mBounds = new Rect();
private int mScrimColor;
private int mBaseAlpha;
private int mDirection;
private Paint mShadowPaint;
private Rect mShadowRect = new Rect();
private int mShadowColor = 0x80000000;
private int mShadowDirection;
public ScrimView(Context context) {
super(context);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
mShadowPaint = new Paint();
mShadowPaint.setDither(true);
mShadowPaint.setAntiAlias(true);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.mBounds.right = w;
this.mBounds.bottom = h;
}
@Override
protected void onDraw(Canvas canvas) {
if (mScrimColor != 0) {
canvas.drawRect(mBounds, mPaint);
}
if (mSize > 0 && mShadowColor != 0 && (mDirection & SwipeConsumer.DIRECTION_ALL) > 0) {
canvas.save();
switch (mShadowDirection) {
default: break;
case SwipeConsumer.DIRECTION_RIGHT: canvas.translate(mBounds.right - mSize, 0); break;
case SwipeConsumer.DIRECTION_BOTTOM: canvas.translate(0, mBounds.bottom - mSize); break;
}
canvas.clipRect(mShadowRect);
canvas.drawPaint(mShadowPaint);
canvas.restore();
}
}
public void setProgress(float progress) {
float mProgress = SmartSwipe.ensureBetween(progress, MIN_PROGRESS, MAX_PROGRESS);
final int alpha = (int) (mBaseAlpha * mProgress);
final int color = alpha << 24 | (mScrimColor & 0xFFFFFF);
mPaint.setColor(color);
}
public void setScrimColor(int scrimColor) {
this.mScrimColor = scrimColor;
mBaseAlpha = (mScrimColor & 0xFF000000) >>> 24;
}
public void setDirection(int direction, int shadowColor, int shadowDirection, int shadowSize, int parentWidth, int parentHeight) {
this.mDirection = direction;
this.mShadowColor = shadowColor;
this.mShadowDirection = shadowDirection;
this.mSize = shadowSize;
if (mShadowColor == 0) {
return;
}
int l = 0, t = 0, r, b;
switch (mShadowDirection) {
default: return;
case SwipeConsumer.DIRECTION_LEFT: case SwipeConsumer.DIRECTION_RIGHT: r = mSize; t = b = parentHeight; break;
case SwipeConsumer.DIRECTION_TOP: case SwipeConsumer.DIRECTION_BOTTOM: r = parentWidth; b = mSize; break;
}
mShadowRect.right = r;
mShadowRect.bottom = b;
int alpha = (mShadowColor & 0xFF000000) >>> 24;
int steps = 30;
float[] positions = new float[steps + 1];
int[] colors = new int[steps + 1];
boolean revert = mShadowDirection == SwipeConsumer.DIRECTION_LEFT || mShadowDirection == SwipeConsumer.DIRECTION_TOP;
for (int i = 0; i <= steps; i++) {
positions[i] = i * 1F / steps;
}
float position;
for (int i = 0; i <= steps; i++) {
position = positions[revert ? steps - i : i];
colors[i] = ((int) (alpha * position * position) << 24) | (mShadowColor & 0xFFFFFF);
}
boolean horizontal = direction == SwipeConsumer.DIRECTION_LEFT || direction == SwipeConsumer.DIRECTION_RIGHT;
if (horizontal) {
t = b = b >> 1;
} else {
l = r = r >> 1;
}
LinearGradient shader = new LinearGradient(l, t, r, b, colors, positions, LinearGradient.TileMode.CLAMP);
mShadowPaint.setShader(shader);
}
public int getShadowColor() {
return mShadowColor;
}
}
(4)抽屉总结:
SmartSwipe
框架的DrawerConsumer
和SlidingConsumer
可以当成工具使用,当项目中需要使用抽屉布局时使用,它们的区别在效果图中可以看到,不再说明。
(5)抽屉效果补充:沉浸式状态栏
使用抽屉布局的话,往往需要做好沉浸式状态栏
效果,以下简单贴一下实现代码,但不能作为沉浸式状态栏
教程方案,如果想了解更多,请查看其它资料。
新增以下两个文件,如图:
values-v19-->styles.xml
values-v21-->styles.xml
(6)绑定在某View或者ViewGroup上
另外,以下代码,是将Consumer
绑定在Activity上
SmartSwipe.wrap(this).addConsumer(mSlidingConsumer);
我们也可以将Consumer
绑定在某个View上,先来看一下效果:
代码如下:
View menu_1 = LayoutInflater.from(this).inflate(R.layout.layout_main_menu, null);
menu_1.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
SmartSwipeWrapper menu1Wrapper = SmartSwipe.wrap(menu_1);
View menu_2 = LayoutInflater.from(this).inflate(R.layout.layout_main_menu, null);
menu_2.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
SmartSwipeWrapper menu2Wrapper = SmartSwipe.wrap(menu_2);
mSlidingConsumer = new SlidingConsumer()
//横向布局的抽屉
.setHorizontalDrawerView(menu1Wrapper)
.setVerticalDrawerView(menu2Wrapper)
//仅显示左边抽屉
//.setLeftDrawerView(menuWrapper)
//仅显示右边抽屉
//.setRightDrawerView(menuWrapper)
//仅显示上边抽屉
//.setTopDrawerView(menuWrapper)
//仅显示下边抽屉
//.setBottomDrawerView(menuWrapper)
//设置遮罩层背景颜色,默认透明
//.setScrimColor(0x80A7BBBB)
//设置边框阴影颜色,默认透明
//.setShadowColor(0x80CAA639)
//设置边框阴影大小,默认为0
//.setShadowSize(SmartSwipe.dp2px(20, this))
//设置监听
.addListener(listener)
//设置边的大小,手指可以在某个方位的20dp范围内触发侧滑事件,默认值为View的宽度或高度
//.setEdgeSize(SmartSwipe.dp2px(20, this))
//将SwipeConsumer类型转换为DrawerConsumer类型
.as(SlidingConsumer.class);
SmartSwipe.wrap(findViewById(R.id.ll_swip)).addConsumer(mSlidingConsumer);
ID为ll_swip的布局:
layout_main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#E0608C">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher_round"
android:padding="10dp"
android:layout_centerInParent="true" />
</RelativeLayout>
如果将SlidingConsumer
改成DrawerConsumer
,效果如下:
(7)布局实现
除了用代码实现,我们还可以直接使用布局实现,代码如下:
SmartSwipeWrapper wrapper = findViewById(R.id.main_ui_wrap_view);
wrapper.addConsumer(new SlidingConsumer()).setRelativeMoveFactor(SlidingConsumer.FACTOR_FOLLOW);
<org.bytedeco.xxx.smartswipedemo.swipe.SmartSwipeWrapper
android:id="@+id/main_ui_wrap_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#80CAA639">
<RelativeLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:swipe_gravity="left|right"
android:background="#E0608C">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher_round"
android:padding="10dp"
android:layout_centerInParent="true" />
</RelativeLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="26sp"
android:gravity="center"
android:text="侧滑数据项"/>
<RelativeLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:swipe_gravity="top|bottom"
android:background="#E0608C">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher_round"
android:padding="10dp"
android:layout_centerInParent="true" />
</RelativeLayout>
</org.bytedeco.xxx.smartswipedemo.swipe.SmartSwipeWrapper>
(8)SlidingConsumer之关联移动因子
如下代码:
SmartSwipeWrapper wrapper = findViewById(R.id.main_ui_wrap_view);
wrapper.addConsumer(new SlidingConsumer()).setRelativeMoveFactor(0.5f);
当使用SlidingConsumer
时,setRelativeMoveFactor
可以设置关联移动因子
,它的取值范围是[0,1],默认情况下,移动因子是0.5,下面分别将移动因子设置成0,0.5,1来演示:
- 当移动因子是0时,也就是抽屉完全在被包裹的控件覆盖,效果如下:
- 当移动因子是0.5时,也就是抽屉有一半的布局在被包裹的控件覆盖,效果如下:
- 当移动因子是1时,也就是抽屉虽然在被包裹的控件下层,但是两者不会有任何重合的地方,效果如下:
[本章完...]