现在android中有很多产品实现了侧边栏菜单的滑动,比如说百度贴吧、腾讯QQ、Facebook、知乎等,这样做的好处:
一是可以装下更多的内容,二是给用户更好的视觉感受。
下面举一个知乎的例子:
一、实现的思路:
首先定义三个FrameLayout:leftMenu、middleMenu、rightMenu
middleMenu为主面板,大小设置为屏幕的大小;
leftMenu、rightMenu为左右侧边栏,大小设置为屏幕的一定比例,我设置的是80%,位置分别设置在middleMenu的左右两边,一开始打开的时候并不能看见。
实现的滑动的方法是dispatchTouchEvent() :
通过ACTION_DOWN和ACTION_MOVE不断获取滑动中的x、y坐标来进行判断是上下滑动还是左右滑动,通过Scroller来实现滑动的动画效果。
二、代码:
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.Scroller;
public class MainUI extends RelativeLayout {
private Context context;
private FrameLayout leftMenu;//设置左面板
private FrameLayout middleMenu;//设置主面板
private FrameLayout rightMenu;//设置右面板
private Scroller mScroller;
private FrameLayout middleMask;
public static final int LEFT_ID = 0xaabbcc;
public static final int MIDDLE_ID = 0xaaccbb;
public static final int RIGHT_ID = 0xccbbaa;
public MainUI(Context context) {
super(context);
initView(context);
}
public MainUI(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(Context context) {//初始化
this.context = context;
mScroller = new Scroller(context, new DecelerateInterpolator());
leftMenu = new FrameLayout(context);
middleMenu = new FrameLayout(context);
rightMenu = new FrameLayout(context);
middleMask = new FrameLayout(context);
leftMenu.setBackgroundColor(Color.RED);
middleMenu.setBackgroundColor(Color.GREEN);
rightMenu.setBackgroundColor(Color.RED);
leftMenu.setId(LEFT_ID);
middleMenu.setId(MIDDLE_ID );
rightMenu.setId(RIGHT_ID );
middleMask.setBackgroundColor(0x88000000);
addView(leftMenu);
addView(middleMenu);
addView(rightMenu);
addView(middleMask);
middleMask.setAlpha(0);//设置透明度
}
public float onMiddleMask(){
System.out.println("透明度:"+middleMask.getAlpha());
return middleMask.getAlpha();
}
public void scrollTo(int x,int y){//滑动方法
super.scrollTo(x,y);
onMiddleMask();
int curX = Math.abs(getScrollX());
float scale = curX/(float)leftMenu.getMeasuredWidth();
middleMask.setAlpha(scale);
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//设置面板大小
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
middleMenu.measure(widthMeasureSpec, heightMeasureSpec);
middleMask.measure(widthMeasureSpec, heightMeasureSpec);
int realWidth = MeasureSpec.getSize(widthMeasureSpec);
int tempWidthMeasure = MeasureSpec.makeMeasureSpec((int) (realWidth * 0.8f), MeasureSpec.EXACTLY);
leftMenu.measure(tempWidthMeasure, heightMeasureSpec);
rightMenu.measure(tempWidthMeasure, heightMeasureSpec);
}
protected void onLayout(boolean changed, int l, int t, int r, int b) {//设置面板位置
super.onLayout(changed, l, t, r, b);
middleMenu.layout(l, t, r, b);
middleMask.layout(l, t, r, b);
leftMenu.layout(l - leftMenu.getMeasuredWidth(), t, l, b);
rightMenu.layout(l + middleMenu.getMeasuredWidth(), t,
r + rightMenu.getMeasuredWidth(), b);
}
private boolean isTestCompete;
private boolean isleftrightEvent;
public boolean dispatchTouchEvent(MotionEvent ev) {//事件分发
if (!isTestCompete) {
getEventType(ev);
return true;
}
if (isleftrightEvent) {//如果是左右滑动
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
int curScrollX = getScrollX();//滚动距离
int dis_x = (int) (ev.getX() - point.x);//手指按下后滑动距离
int expectX = -dis_x + curScrollX;
int finalX = 0; //初始化屏幕滑动距离
if (expectX < 0) { //向左滑动
finalX = Math.max(expectX, -leftMenu.getMeasuredWidth());
} else { //向右滑动
finalX = Math.min(expectX, rightMenu.getMeasuredWidth());
}
scrollTo(finalX, 0);//屏幕移动
point.x = (int) ev.getX();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
curScrollX = getScrollX();
if (Math.abs(curScrollX) > leftMenu.getMeasuredWidth() >> 1) {//滑动大于屏幕距离一半时,启动动画
if (curScrollX < 0) {//向左
mScroller.startScroll(curScrollX, 0, -leftMenu.getMeasuredWidth() - curScrollX, 0);
} else {//向右
mScroller.startScroll(curScrollX, 0, leftMenu.getMeasuredWidth() - curScrollX, 0);
}
} else {
mScroller.startScroll(curScrollX, 0, -curScrollX, 0);//距离不到返回原点
}
invalidate();//View重绘
isleftrightEvent = false;
isTestCompete = false;
break;
}
} else {//上下滑动也要初始化
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_UP:
isleftrightEvent = false;
isTestCompete = false;
break;
}
}
return super.dispatchTouchEvent(ev);
}
private Point point = new Point();
private static final int TEST_DIS = 20;
public void computeScroll() {//回调方法
super.computeScroll();
if (!mScroller.computeScrollOffset()) {
return;
}
int tempX = mScroller.getCurrX();//总滑动值
scrollTo(tempX, 0);
}
private void getEventType(MotionEvent e) {//判断事件类型
switch (e.getActionMasked()){
case MotionEvent.ACTION_DOWN:
point.x=(int) e.getX();
point.y=(int) e.getY();
super.dispatchTouchEvent(e);
break;
case MotionEvent.ACTION_MOVE:
int dX = Math.abs((int) e.getX()-point.x);
int dY = Math.abs((int) e.getY()-point.y);
if(dX > TEST_DIS && dX > dY){//左右滑动
isleftrightEvent = true;
isTestCompete = true;
point.x = (int)e.getX();
point.y = (int)e.getY();
}else if(dY > TEST_DIS && dY>dX){//上下滑动
isleftrightEvent = false;
isTestCompete = true;
point.x = (int)e.getX();
point.y = (int)e.getY();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
super.dispatchTouchEvent(e);
isleftrightEvent = false;
isTestCompete = false;
break;
}
}
}
源码已上传。