思路
侧滑菜单有2部分,左布局和主布局,所以必须继承ViewGroup,所有先要在onMeasure()测量menuView+contentView的宽高,这样在onLayout()中摆放着2个子布局,在move过程中调用scrollBy()实现滑动效果,在手指移开后打开或关闭menu
效果图
过程
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
tools:context="com.cqc.slideviewdemo.MainActivity">
<com.cqc.slideviewdemo.SlideView
android:id="@+id/slideView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/menu_left" />
<include layout="@layout/content" />
</com.cqc.slideviewdemo.SlideView>
</LinearLayout>
Step1:实现拖动效果
public class SlideView extends ViewGroup {
private int menuViewWidth;
private View menuView;
private View contentView;
private int downX;
private int moveX;
public SlideView(Context context) {
this(context, null);
}
public SlideView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//自定义控件需要测量子控件的宽高
menuView = getChildAt(0);
menuViewWidth = menuView.getLayoutParams().width;
menuView.measure(menuViewWidth, heightMeasureSpec);
contentView = getChildAt(1);
contentView.measure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
menuView.layout(-menuViewWidth, t, 0, b);
contentView.layout(l, t, r, b);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
moveX = (int) event.getX();
int instance = downX - moveX;
scrollBy(instance, 0);
downX = moveX;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
Step2:限制拖动边界
上面的代码实现了滑动,但是会有一个问题,menuView会拖过界,我们必须设置getScrollX的取值范围,getScrollX()
就是SlideView左边界到当前点的值 getScrollX() = downX-currentX,修改状态为MotionEvent.ACTION_MOVE时的拖动范围
case MotionEvent.ACTION_MOVE:
moveX = (int) event.getX();
int instance = downX - moveX;
int scrollX = getScrollX() + instance;
if (scrollX < -menuViewWidth) {
//注意不是menuView.scrollTo(-menuViewWidth, 0);
scrollTo(-menuViewWidth, 0);
} else if (scrollX > 0) {
scrollTo(0, 0);
} else {
}
scrollBy(instance, 0);
downX = moveX;
break;
Step3:拖动后的打开或关闭menu
MotionEvent.ACTION_UP状态下实现menu的打开或关闭
case MotionEvent.ACTION_UP:
int center = menuViewWidth / 2;
int upScrollX = getScrollX();
if (upScrollX < -center) {
scrollTo(-menuViewWidth, 0);
} else {
scrollTo(0, 0);
}
break;
Step4:menu的打开或关闭 加入动画效果 平滑的实现
上面调用的是scrollTo(),没有动画效果,无论你滑动多长的距离,menu的打开或关闭所需时间都是固定的,我们要实现平滑的打开或关闭。
case MotionEvent.ACTION_UP:
int center = menuViewWidth / 2;
int upScrollX = getScrollX();
int startX = upScrollX;
int dx;
if (upScrollX < -center) {//关闭menu
dx = -menuViewWidth - startX;
menuIsOpen = false;
} else { //打开menu
dx = 0 - startX;
menuIsOpen = true;
}
int duration = Math.abs(dx) * 5;
scroller.startScroll(startX, 0, dx, duration);
invalidate();
break;
还需要重写computeScroll()
,原因未知
@Override
public void computeScroll() {
if (scroller.computeScrollOffset()) {
scrollTo(scroller.getCurrX(), 0);
invalidate();
}
}
Step5:点击按钮 实现menu的打开或关闭
对ACTION_UP进行修改
case MotionEvent.ACTION_UP:
int center = -menuViewWidth / 2;
int upScrollX = getScrollX();
if (upScrollX > center) {//
menuIsOpen = false;
} else if (upScrollX < center) { //
menuIsOpen = true;
}
switchMenu();
private void switchMenu() {
int startX = getScrollX();
int dx;
if (menuIsOpen) {
dx = -menuViewWidth - startX;
} else {
dx = -startX;
}
int duration = Math.abs(dx) * 5;
//dx应该是剩余的距离,而不是滑动距离。view往右走是负值,往左走是正值
scroller.startScroll(startX, 0, dx, duration);
invalidate();
}
点击按钮,打开或关闭menu
public void clickMenu() {
menuIsOpen = !menuIsOpen;
switchMenu();
}
在点击事件中直接调用
slideView.clickMenu();