效果图
实现思路
- 继承 HorizontalScrollView 实现横向滚动效果
- 处理手势快速滑动控制菜单的显示和隐藏
- 处理内容点击关闭菜单
- 处理阴影
- 处理阴影透明度
SlideMenu 处理横向滚动
public class SlideMenu extends HorizontalScrollView {
public SlideMenu(Context context) {
this(context, null);
}
public SlideMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initMenu(context, attrs);
}
private void initMenu(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlideMenu);
float rightPadding = array.getDimension(R.styleable.SlideMenu_rightPadding, dp2px(50));
mMenuWidth = (int) (getScreenWidth() - rightPadding);
array.recycle();
//初始化手势
mGestureDetector = new GestureDetector(context, new GestureListener());
}
}
GestureListener 处理手势快速滑动
//手势处理
private class GestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mMenuIsOpen) {
//当往左滑动时关闭菜单
if (velocityX < 0) {
closeMenu();
return true;
}
} else {
//当往右滑动时打开菜单
if (velocityX > 0) {
openMenu();
return true;
}
}
return super.onFling(e1, e2, velocityX, velocityY);
}
}
onTouchEvent 处理内容点击
@Override
public boolean onTouchEvent(MotionEvent ev) {
//如果点击被拦截,就不触发之后的操作
if (mIsIntercept) {
return true;
}
//触发快速滑动,下面就不要执行
if (mGestureDetector.onTouchEvent(ev)) {
return true;
}
switch (ev.getAction()) {
case MotionEvent.ACTION_UP:
// 手指抬起获取滚动的位置
int currentScrollX = getScrollX();
if (currentScrollX > mMenuWidth / 2) {
// 关闭菜单
closeMenu();
} else {
// 打开菜单
openMenu();
}
return false;
}
return super.onTouchEvent(ev);
}
onFinishInflate 处理阴影
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//获取最外层布局
ViewGroup container = (ViewGroup) this.getChildAt(0);
//统计子布局的数量
int containerChildCount = container.getChildCount();
if (containerChildCount > 2) {
throw new IllegalStateException("SlideMenu ChildView More Than 2");
}
//获取菜单布局
mMenuView = container.getChildAt(0);
//菜单宽度 = 屏幕宽度 - 右边 Padding
mMenuView.getLayoutParams().width = mMenuWidth;
//获取内容布局
mContentView = container.getChildAt(1);
//获取 LayoutParams
ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
//移除原来内容布局
container.removeView(mContentView);
//创建新的 RelativeLayout
RelativeLayout contentView = new RelativeLayout(getContext());
//创建阴影布局
mShadowView = new View(getContext());
//设置阴影颜色
mShadowView.setBackgroundColor(Color.parseColor("#80000000"));
//设置透明度
mShadowView.setAlpha(0.0f);
//将内部加入布局
contentView.addView(mContentView);
//将阴影加入布局
contentView.addView(mShadowView);
//内容宽度 = 屏幕宽度
layoutParams.width = getScreenWidth();
//设置 layoutParams
contentView.setLayoutParams(layoutParams);
container.addView(contentView);
}
处理阴影的大致原理可分成 5 个步骤 :
- 拿到内容布局
- 创建一个新的 RelativeLayout
- 创建一个阴影布局
- 将内容布局与阴影布局添加到 RelativeLayout
- 将 RelativeLayout 添加到原来的位置
onScrollChanged 处理阴影透明度
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale = 1f * l / mMenuWidth;
float alphaScale = 1 - scale;
mShadowView.setAlpha(alphaScale);
}
根据菜单的宽度和坐标计算偏移量
网友评论