实现的效果如下
上代码,代码中有详细说明
public class SlidingMenuView extends HorizontalScrollView {
// 菜单的宽度
private int mMenuWidth;
private View mContentView,mMenuView;
// 处理快速滑动
private GestureDetector gestureDetector;
// 是否拦截
private boolean mIsIntercept = false;
//菜单是否打开
private boolean mMenuIsOpen = false;
public SlidingMenuView (Context context) {
this(context, null);
}
public SlidingMenuView (Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenuView (Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenuView);
//rightMargin 是通过自定义属性传过来的,
float rightMargin = array.getDimension(
R.styleable.SlidingMenuView_menuRightMargin, dip2px(context, 50));
// 菜单页的宽度
mMenuWidth = (int) (getScreenWidth(context) - rightMargin);
array.recycle();
//快速滑动监听
gestureDetector = new GestureDetector(context,new 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);
}
});
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
ViewGroup container = (ViewGroup) getChildAt(0);
int childCount = container.getChildCount();
if (childCount != 2) {
throw new RuntimeException("侧滑ViewGroup目前只支持两个子View!");
}
mMenuView = container.getChildAt(0);
//以下是调整两个子view的宽度
ViewGroup.LayoutParams menuParams = mMenuView.getLayoutParams();
menuParams.width = mMenuWidth;
mMenuView.setLayoutParams(menuParams);
mContentView = container.getChildAt(1);
ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
layoutParams.width = getScreenWidth(getContext());
mContentView.setLayoutParams(layoutParams);
}
// 右边的缩放
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
float scale = 1f * l / mMenuWidth;// scale 变化是 1 - 0
// 右边的缩放: 最小是 0.7f, 最大是 1f
float rightScale = 0.7f + 0.3f * scale;
//底部的内容缩放
float bottomScale = 0.8f + 0.2f * scale;
// 设置右边的缩放,默认是以中心点缩放
// 设置缩放的中心点位置
mContentView.setPivotX(0);
mContentView.setPivotY(mContentView.getMeasuredHeight() / 2);
mContentView.setScaleX(rightScale);
mContentView.setScaleY(rightScale);
float leftAlpha = 0.5f + (1-scale)*0.5f;
ViewCompat.setAlpha(mMenuView,leftAlpha);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 2. 初始化进来是关闭
scrollTo(mMenuWidth, 0);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mIsIntercept = false;
if (mMenuIsOpen){
float currentX = ev.getX();
if (currentX > mMenuWidth) {
// 关闭菜单
closeMenu();
//这里要拦截子view 的点击事件
mIsIntercept = true;
return true;
}
}else {
//在关闭的时候不要拦截子事件(由于此处内容页包裹的是地图,所以不能影响地图的滑动事件)
return false;
}
return super.onInterceptTouchEvent(ev);
}
//
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mIsIntercept) {
return true;
}
if (gestureDetector.onTouchEvent(ev)) {
return true;
}
if (ev.getAction() == MotionEvent.ACTION_UP) {
// 只需要关注手指抬起 ,根据我们当前滚动的距离来判断
int currentScrollX = getScrollX();
if (currentScrollX > mMenuWidth / 2) {
// 关闭
closeMenu();
} else {
// 打开
openMenu();
}
return true;
}
return super.onTouchEvent(ev);
}
/**
* 打开菜单 滚动到 0 的位置
*/
public void openMenu() {
smoothScrollTo(0, 0);
mMenuIsOpen = true;
}
/**
* 关闭菜单 滚动到 mMenuWidth 的位置
*/
public void closeMenu() {
smoothScrollTo(mMenuWidth, 0);
mMenuIsOpen = false;
}
/**
* 获得屏幕高度
*
* @param context
* @return
*/
private int getScreenWidth(Context context) {
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
return outMetrics.widthPixels;
}
/**
* Dip into pixels
*/
private int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
关于View滑动的说明:
getScrollX()的正负值问题:
初始状态的SlidingMenuView 的左边界是屏幕的左边界
当从右向左滑动时,也就是关闭菜单的状态,处于图中第二种状态,经过滑动SlidingMenuView 的左边界处在SlidingMenuView 的内容区域左边界的右侧,这时 getScrollX()为正值,反之,为负值
View的滑动移动的是View的内容,不是移动View本身,这一点要清楚
使用
使用的话按照HorizontalView的使用方式来就可以,比如:
<SlidingMenuView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:menuRightMargin="126dp"
android:id="@+id/slide_menu_sm"
tools:context=".ui.home.MainSlideActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#EC6503"
android:orientation="horizontal">
<include layout="@layout/activity_my"/>
<include layout="@layout/activity_new"/>
</LinearLayout>
</SlidingMenuView >
<declare-styleable name="SlidingMenuView">
<attr name="menuRightMargin" format="dimension"/>
</declare-styleable>