背景
最新做的项目需求要实现二级滑动侧边栏菜单,说到侧边栏首先想到的是android的DrawerLayout,但是发现它只有两种状态(打开、关闭),并不能设置打开一半这种效果。
先看看效果:
刚开始的时候,主界面完全看不到侧边栏,这时从侧边栏右滑,会出现一列图标的侧边栏,再从左边缘向右滑动,侧边栏就显示描述 + 图标。 点击菜单栏的三横线, 就可以打开或者关闭侧边栏。(打开侧边栏时会记住上一次的状态, 是只显示图标还是描述 + 图标)。
既然DrawerLayout不能满足需求,而且也并没有找到适合的viewGroup,于是就想着自定义一个viewGroup来满足我的需求. 尝试看了一下DrawerLayout的源码,发现其中一个很重要的东西:ViewDragHelper, 使用该类可以进行View的滑动。 关于ViewDragHelper就不做过多介绍了,参照鸿洋大神的博客:Android ViewDragHelper完全解析 自定义ViewGroup神器
话不多说,首先看下如果构建ViewDragHelper对象,并且需要实现哪些方法。
ViewDragHelper是使用静态方法create来创建实例对象的。
需要传递三个参数
ViewGroup: 当前使用ViewDragHelper的ViewGroup
float: 灵敏度,该值范围是0~1,,越大越灵敏,通常建议就是1
ViewDragHelper.Callback: 回调接口,最主要的实现就是在这里。
代码附上:
mHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
/**
* 该方法的返回值决定哪个子view可以拖动
* @param child 子view
* @param pointerId
* @return true: 代表该child可以被拖动, false: 代表child不可以被拖动
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
return child == mDrawerView;
}
/**
* 返回一个适当的数值就能实现横向拖动效果,
* @param child
* @param left left参数指当前拖动子view应该到达的x坐标 按照常理直接返回该参数就可以了,但是为了让被拖动的view遇到边界就不能再拖动
* 了,所以就可以对该值进行处理,返回更合理的数值
* @param dx
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
int newLeft = Math.max(-child.getWidth(), Math.min(left, 0));
return newLeft;
}
/**
* 在边缘滑动的时候根据滑动距离移动一个子view
* @param edgeFlags
* @param pointerId
*/
@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
mHelper.captureChildView(mDrawerView, pointerId);
}
/**
* 手指释放的时候回调该方法
* @param releasedChild
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
final int childWidth = releasedChild.getWidth();
float offset = (childWidth + releasedChild.getLeft()) * 1.0f / childWidth;
int width;
Log.d(TAG, "onViewReleased: xvel : " + xvel + ", offset: " + offset);
if (offset < PROPORTION * 0.5) {
Log.d(TAG, "invisible");
width = -childWidth;
} else if (offset >= PROPORTION * 0.5 && offset <= (1 + PROPORTION) * 0.5) {
width = (int) (-childWidth * (1 - PROPORTION));
mCurrentWidth = width;
Log.d(TAG, "part visible");
} else {
width = 0;
mCurrentWidth = width;
Log.d(TAG, "visible");
}
mHelper.settleCapturedViewAt(
width,
mDrawerTopMargin);
invalidate();
}
/**
* changeView在拖动过程中,坐标发生变化时回调该方法,包括手动拖动和view自动滚动
* @param changedView
* @param left
* @param top
* @param dx
* @param dy
*/
@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
// super.onViewPositionChanged(changedView, left, top, dx, dy);
final int childWidth = changedView.getWidth();
float offset = (float) (childWidth + left) / childWidth;
mDrawerOnScreen = offset;
//offset can callback here
changedView.setVisibility(offset == 0 ? INVISIBLE : VISIBLE);
invalidate();
}
/**
* 返回横向拖动的最大距离
* @param child
* @return
*/
@Override
public int getViewHorizontalDragRange(View child) {
// return super.getViewHorizontalDragRange(child);
return child == mDrawerView ? mDrawerView.getWidth() : 0;
}
});
代码下载: github