这只是为了学习自定义view,自己写的一个简单的侧滑菜单,建议真实项目中还是使用成熟的侧滑菜单框架(比如Slidingmenu),不要去重复造轮子。代码的逻辑不复杂,且注释写的还算清楚,就不多解释了,直接上代码。
/**
* 自定义的侧滑菜单
* 创建日期:2018-03-15 on 13:45
* 作者:ls
*/
public class SlidingMenu extends ViewGroup {
private static final String TAG = "SlidingMenu";
private int mTouchSlop;//被判定为滑动的最小距离
private Scroller mScroller;//弹性滑动
private VelocityTracker mVelocityTracker;//速度跟踪器
private float mFirstX;
private float mFirstY;
private float mLastX;
private float mLastY;
private int menuWidth;//菜单的宽度
private int contentWidth;//内容的宽度
private View menu;//菜单
private View content;//内容
public SlidingMenu(Context context) {
this(context, null);
}
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mTouchSlop = ViewConfiguration.getTouchSlop();
mScroller = new Scroller(getContext());
mVelocityTracker = VelocityTracker.obtain();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量所有的子view
int childCount = getChildCount();
for(int i = 0; i < childCount; i++){
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d(TAG, "l = " + l + "....." + "t = " + t + "....." + "r = " + r + "....." + "b = " + b);
menu = getChildAt(0);
content = getChildAt(1);
menuWidth = menu.getMeasuredWidth();
contentWidth = content.getMeasuredWidth();
menu.layout(-1 * menuWidth, 0, 0, menu.getMeasuredHeight());
content.layout(0, 0, contentWidth, content.getMeasuredHeight());
Log.d(TAG, "content.getMeasuredWidth() = " + content.getMeasuredWidth());
Log.d(TAG, "content.getMeasuredHeight() = " + content.getMeasuredHeight());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onInterceptTouchEvent.........ACTION_DOWN");
mFirstX = mLastX = ev.getX();
mFirstY = mLastY = ev.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onInterceptTouchEvent.........ACTION_MOVE");
float deltaX = ev.getX() - mLastX;
float deltaY = ev.getY() - mLastY;
if(Math.abs(deltaX) > mTouchSlop && Math.abs(deltaX) > Math.abs(deltaY)){
return true;
}
return false;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mVelocityTracker.addMovement(event);
mVelocityTracker.computeCurrentVelocity(1000);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "onTouchEvent.........ACTION_DOWN");
return true;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "onTouchEvent.........ACTION_MOVE");
Log.d(TAG, "getScrollX() = " + getScrollX());
//边界判断和处理
if (checkBound(event)) return true;
//手指按在屏幕上滑动的处理
float deltaX = event.getX() - mLastX;
scrollBy((int) (-1 * deltaX), 0);
mLastX = event.getX();
mLastY = event.getY();
break;
case MotionEvent.ACTION_UP: //根据滚动的距离判断怎么还原
Log.d(TAG, "onTouchEvent.........ACTION_UP");
int vx = (int) Math.abs(mVelocityTracker.getXVelocity());
Log.d(TAG, "vx = " + vx);
//快速滑动的处理,未必一定是200
if(vx > 200){
if(event.getX() - mFirstX > 0){ //向右快速滑动
showMenuComplete();
return true;
}else{ //向左快速滑动
hideMenuComplete();
return true;
}
}
//正常滑动的处理
if(getScrollX() > -1 * menuWidth && getScrollX() <= -0.5f * menuWidth){ //拉出过半了
showMenuComplete();
return true;
}
if(getScrollX() > -0.5f * menuWidth && getScrollX() <= 0){ //拉出未过半
hideMenuComplete();
return true;
}
break;
}
return super.onTouchEvent(event);
}
/**
* 完全隐藏menu
*/
private void hideMenuComplete() {
mScroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0);
invalidate();
}
/**
* 完全展示menu
*/
private void showMenuComplete() {
mScroller.startScroll(getScrollX(), 0, -1 * menuWidth - getScrollX(), 0);
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
postInvalidate();
}
}
/**
* 边界的判断和处理
*/
private boolean checkBound(MotionEvent event) {
float tempX = event.getX() - mFirstX;
if(tempX > 0){ //说明手指在向右滑动
if(getScrollX() <= -1 * menuWidth){
scrollTo(-1 * menuWidth, 0);
Log.d(TAG, "已经碰到边界了...不能再往右滑动");
return true;
}
}else{ //说明手指在向左滑动
if(getScrollX() >= 0){
scrollTo(0, 0);
Log.d(TAG, "已经碰到边界了...不能再往左滑动");
return true;
}
}
return false;
}
}