概述
目录
2.3设置SlidingView菜单栏视图
2.4显示SlidingView内容
3.1按下事件监听
3.2滑动事件监听
3.3手势抬起
3.4事件的分发和拦截
3.5当前页面点击返回按钮时关闭菜单栏
4.CustomViewBehind
4.1菜单栏的滚动显示/隐藏
4.1.1只分析LEFT的情况(请他情况同理)
4.1.2在CustomViewAbove重写dispatchDraw方法,调用CustomViewAbove绘制阴影,淡入和选择器方法实现绘制
1.SlidingMenu是什么
SlidingMenu是一个开源的Android库,支持左右菜单栏,它允许开发者使用滑动菜单轻松创建应用程序,就像谷歌+、YouTube和Facebook应用程序中流行的那些滑动菜单一样。你可以在你的Android应用程序中自由使用它;
1.1SlidingMenu类结构
SlidingMenu库设计思路三个主要的ViewGroup(SlidingMenu,CustomViewAbove,CustomViewBehind), SlidingMenu包含两个盖在一起的ViewGroup(CustomViewAbove,CustomViewBehind),CustomViewAbove做为上层视图(主要将Activity中setContentView设置的视图添加到上层视图CustomViewAbove),CustomViewBehind做为下层视图(主要将左右菜单栏视图加添加到下层视图);
在CustomViewAbove上滑动时显示下层菜单视图CustomViewBehind;
主ViewGroup(SlidingMenu)作为一个自定义控件,里面的内容和菜单利用自定义属性设置;
public class SlidingMenu extends RelativeLayout {
private CustomViewAbove mViewAbove;
private CustomViewBehind mViewBehind;
}
SlidingMenu(继承RelativeLayout),就是这个主ViewGroup,其中又包含两个Activity内容CustomViewAbove和菜单CustomViewBehind(都继承ViewGroup),SlidingMenu提供两个自定义属性,供使用者传入两个布局id,对应主体内容和菜单布局,此外还有其他一些属性供开发者设置菜单效果;
public class SlidingMenu extends RelativeLayout {
public void setContent(int res) {
this.setContent(LayoutInflater.from(this.getContext()).inflate(res, (ViewGroup)null));
}
//设置Activity主视图
public void setContent(View view) {
this.mViewAbove.setContent(view);
this.showContent();
}
public void setMenu(int res) {
this.setMenu(LayoutInflater.from(this.getContext()).inflate(res, (ViewGroup)null));
}
//设置左侧菜单栏视图
public void setMenu(View v) {
this.mViewBehind.setContent(v);
}
public void setSecondaryMenu(int res) {
this.setSecondaryMenu(LayoutInflater.from(this.getContext()).inflate(res, (ViewGroup)null));
}
//设置右侧菜单栏视图
public void setSecondaryMenu(View v) {
this.mViewBehind.setSecondaryContent(v);
}
}
2.SlidingMenu使用及源码分析
SlidingMenu类提供API接口调用,同时提供相应属性参数设置;
2.1新建SlidingMenu对象
slidingMenu = new SlidingMenu(this);
public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mActionbarOverlay = false;
LayoutParams behindParams = new LayoutParams(-1, -1);
//底层视图主要添加左右菜单栏使用
this.mViewBehind = new CustomViewBehind(context);
this.addView(this.mViewBehind, behindParams);
LayoutParams aboveParams = new LayoutParams(-1, -1);
//上层视图主要添加主视图(当前Activity的视图)
this.mViewAbove = new CustomViewAbove(context);
this.addView(this.mViewAbove, aboveParams);
this.mViewAbove.setCustomViewBehind(this.mViewBehind);
this.mViewBehind.setCustomViewAbove(this.mViewAbove);
//设置监听菜单栏打开或者关闭接口
this.mViewAbove.setOnPageChangeListener(new OnPageChangeListener() {
public static final int POSITION_OPEN = 0;
public static final int POSITION_CLOSE = 1;
public static final int POSITION_SECONDARY_OPEN = 2;
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
public void onPageSelected(int position) {
if(position == 0 && SlidingMenu.this.mOpenListener != null) {
SlidingMenu.this.mOpenListener.onOpen();
} else if(position == 1 && SlidingMenu.this.mCloseListener != null) {
SlidingMenu.this.mCloseListener.onClose();
} else if(position == 2 && SlidingMenu.this.mSecondaryOpenListner != null) {
SlidingMenu.this.mSecondaryOpenListner.onOpen();
}
}
});
//设置菜单栏模式,左侧,右侧,左右同时同存在
//public static final int LEFT = 0;
//public static final int RIGHT = 1;
//public static final int LEFT_RIGHT = 2;
TypedArray ta = context.obtainStyledAttributes(attrs, styleable.SlidingMenu);
int mode = ta.getInt(styleable.SlidingMenu_mode, 0);
this.setMode(mode);
int viewAbove = ta.getResourceId(styleable.SlidingMenu_viewAbove, -1);
//设置空的主视图
if(viewAbove != -1) {
this.setContent(viewAbove);
} else {
this.setContent(new FrameLayout(context));
}
//设置空的菜单栏视图
int viewBehind = ta.getResourceId(styleable.SlidingMenu_viewBehind, -1);
if(viewBehind != -1) {
this.setMenu(viewBehind);
} else {
this.setMenu(new FrameLayout(context));
}
//设置显示菜单栏显示操作模式
//public static final int TOUCHMODE_MARGIN = 0;
// public static final int TOUCHMODE_FULLSCREEN = 1;
//public static final int TOUCHMODE_NONE = 2;
int touchModeAbove = ta.getInt(styleable.SlidingMenu_touchModeAbove, 0);
this.setTouchModeAbove(touchModeAbove);
int touchModeBehind = ta.getInt(styleable.SlidingMenu_touchModeBehind, 0);
this.setTouchModeBehind(touchModeBehind);
//设置视图显示样式
int offsetBehind = (int)ta.getDimension(styleable.SlidingMenu_behindOffset, -1.0F);
int widthBehind = (int)ta.getDimension(styleable.SlidingMenu_behindWidth, -1.0F);
if(offsetBehind != -1 && widthBehind != -1) {
throw new IllegalStateException("Cannot set both behindOffset and behindWidth for a SlidingMenu");
} else {
if(offsetBehind != -1) {
this.setBehindOffset(offsetBehind);
} else if(widthBehind != -1) {
this.setBehindWidth(widthBehind);
} else {
this.setBehindOffset(0);
}
float scrollOffsetBehind = ta.getFloat(styleable.SlidingMenu_behindScrollScale, 0.33F);
this.setBehindScrollScale(scrollOffsetBehind);
int shadowRes = ta.getResourceId(styleable.SlidingMenu_shadowDrawable, -1);
if(shadowRes != -1) {
this.setShadowDrawable(shadowRes);
}
//设置阴影效果
int shadowWidth = (int)ta.getDimension(styleable.SlidingMenu_shadowWidth, 0.0F);
this.setShadowWidth(shadowWidth);
boolean fadeEnabled = ta.getBoolean(styleable.SlidingMenu_fadeEnabled, true);
this.setFadeEnabled(fadeEnabled);
float fadeDeg = ta.getFloat(styleable.SlidingMenu_fadeDegree, 0.33F);
this.setFadeDegree(fadeDeg);
boolean selectorEnabled = ta.getBoolean(styleable.SlidingMenu_selectorEnabled, false);
this.setSelectorEnabled(selectorEnabled);
int selectorRes = ta.getResourceId(styleable.SlidingMenu_selectorDrawable, -1);
if(selectorRes != -1) {
this.setSelectorDrawable(selectorRes);
}
ta.recycle();
}
}
2.2slidingMenu基础参数设置
slidingMenu.setMode(SlidingMenu.LEFT_RIGHT);
// 设置触摸屏幕的模式
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
// 设置渐入渐出效果的值
slidingMenu.setFadeDegree(0.35f);
slidingMenu.setFadeEnabled(true);
a.设置菜单栏模式
slidingMenu.setMode(SlidingMenu.LEFT_RIGHT);
/** Constant value for use with setMode(). Puts the menu to the left of the content.
*/
/**方法setMode()使用的常量.把菜单放在内容的左边*/
public static final int LEFT = 0;
/** Constant value for use with setMode(). Puts the menu to the right of the content.
*/
/**方法setMode()使用的常量.把菜单放在内容的右边*/
public static final int RIGHT = 1;
/** Constant value for use with setMode(). Puts menus to the left and right of the content.
*/
/**方法setMode()使用的常量.把菜单放在内容的左边和右边*/
b.设置触摸屏幕模式
slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
/** Constant value for use with setTouchModeAbove(). Allows the SlidingMenu to be opened with a swipe
* gesture on the screen's margin
*/
/**使用setTouchModeAbove()的常量.允许SlidingMenu在屏幕的边缘通过一个猛击的手势打开.
*/
public static final int TOUCHMODE_MARGIN = 0;
/** Constant value for use with setTouchModeAbove(). Allows the SlidingMenu to be opened with a swipe
* gesture anywhere on the screen
*/
/**使用setTouchModeAbove()的常量.允许SlidingMenu在屏幕的任何地方通过一个猛击的手势打开.
*/
public static final int TOUCHMODE_FULLSCREEN = 1;
/** Constant value for use with setTouchModeAbove(). Denies the SlidingMenu to be opened with a swipe
* gesture
*/
/**使用setTouchModeAbove()的常量.不允许SlidingMenu通过一个猛击的手势打开.
*/
public static final int TOUCHMODE_NONE = 2;
c.设置显示菜单栏动画效果
// 设置渐入渐出效果的值
slidingMenu.setFadeDegree(0.35f);
slidingMenu.setFadeEnabled(true);
2.3将Activity视图添加到SlidingMenu下的上层视图
//使SlidingMenu附加在Activity上
slidingMenu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);
public void attachToActivity(Activity activity, int slideStyle, boolean actionbarOverlay) {
if(slideStyle != 0 && slideStyle != 1) {
throw new IllegalArgumentException("slideStyle must be either SLIDING_WINDOW or SLIDING_CONTENT");
} else if(this.getParent() != null) {
throw new IllegalStateException("This SlidingMenu appears to already be attached");
} else {
TypedArray a = activity.getTheme().obtainStyledAttributes(new int[]{16842836});
int background = a.getResourceId(0, 0);
a.recycle();
switch(slideStyle) {
case 0:
this.mActionbarOverlay = false;
ViewGroup decor = (ViewGroup)activity.getWindow().getDecorView();
ViewGroup decorChild = (ViewGroup)decor.getChildAt(0);
decorChild.setBackgroundResource(background);
decor.removeView(decorChild);
decor.addView(this);
this.setContent(decorChild);
break;
case 1:
this.mActionbarOverlay = actionbarOverlay;
ViewGroup contentParent = (ViewGroup)activity.findViewById(16908290);//获取Activity根视图
View content = contentParent.getChildAt(0); //获取Activity根视图下的第一个视图(setContentView添加的)
contentParent.removeView(content);//Activity根视图下的第一个视图(setContentView添加的)
contentParent.addView(this);将SlidingMenu视图添加到根视图
this.setContent(content);//将Activity根视图下的第一个视图(setContentView添加的)添加到SlidingMenu下的上层视图
if(content.getBackground() == null) {
content.setBackgroundResource(background);
}
}
}
}
2.3设置SlidingView菜单栏视图
slidingMenu.setMenu(R.layout.left_menu);
leftMenu = new LeftMenuFragment();
getSupportFragmentManager().beginTransaction().add(R.id.left_menu, leftMenu).commit();
slidingMenu.setSecondaryMenu(R.layout.right_menu);
rightMenu = new RightMenuFragment();
getSupportFragmentManager().beginTransaction().add(R.id.right_menu, rightMenu).commit();
在SlidingView将菜单栏视图添加到底层视图
SlidingView
public void setMenu(int res) {
this.setMenu(LayoutInflater.from(this.getContext()).inflate(res, (ViewGroup)null));
}
public void setMenu(View v) {
this.mViewBehind.setContent(v);
}
public void setSecondaryMenu(int res) {
this.setSecondaryMenu(LayoutInflater.from(this.getContext()).inflate(res, (ViewGroup)null));
}
public void setSecondaryMenu(View v) {
this.mViewBehind.setSecondaryContent(v);
}
2.4显示SlidingView内容
slidingMenu.showContent();
3.SlidingMenu下实现滑动显示左右菜单栏源码分析
CustomViewAbove.java
主要处理界面的touch事件,解决滑动冲突,控制着整个控件当前的滑动状态,大部分方法属性也传递到slidingmenu.java暴露给了用户;
CustomViewBehind.java
主要处理界面的一些基本属性及状态,如滑动时视差滚动,透明度渐变,对画布的操作,阴影的绘制等,大部分方法也通过slidingmenu.java暴露给了用户,Behind View 中的touch事件也延用了Above View 的touch 事件,相当于统一交予Above View来处理;
CustomViewAbove同时处理CustomViewAbove和CustomViewBehind触摸事件;CustomViewAbove重点主要实现onInterceptTouchEvent()和onTouchEvent()处理滑动事件,onInterceptTouchEvent()主要决定是否进行事件拦截,onTouchEvent()实际处理触摸事件,监听执行视图滑动;
在CustomViewAbove处理事件主要处理按下,滑动,抬起;
public static final int ACTION_DOWN
= 0; //按下
public static final int ACTION_UP
= 1; //抬起
public static final int ACTION_MOVE
= 2; //滑动
3.1按下事件监听
case MotionEvent.ACTION_DOWN:
this.completeScroll();
int index = MotionEventCompat.getActionIndex(ev);
this.mActivePointerId = MotionEventCompat.getPointerId(ev, index);
this.mLastMotionX = this.mInitialMotionX = ev.getX();
break;
this.completeScroll();
如果视图还在滚动,则停止;
this.mActivePointerId = MotionEventCompat.getPointerId(ev, index);
获取第一个按下的触控点(手势)ID,mActivePointerId是记录多点触碰的activity第一个点的id,总之是用来处理多点除控时拖动的稳定性;
this.mLastMotionX = this.mInitialMotionX = ev.getX();
只需要实现水平滚动,记录按下时x轴坐标;
3.2滑动事件监听
case MotionEvent.ACTION_MOVE:
if(!this.mIsBeingDragged) {
this.determineDrag(ev);
if(this.mIsUnableToDrag) {
return false;
}
}
if(this.mIsBeingDragged) {
activePointerIndex = this.getPointerIndex(ev, this.mActivePointerId);
if(this.mActivePointerId != -1) {
float x = MotionEventCompat.getX(ev, activePointerIndex);
float deltaX = this.mLastMotionX - x;
this.mLastMotionX = x;
oldScrollX = (float)this.getScrollX();
float scrollX = oldScrollX + deltaX;
leftBound = (float)this.getLeftBound();
float rightBound = (float)this.getRightBound();
if(scrollX < leftBound) {
scrollX = leftBound;
} else if(scrollX > rightBound) {
scrollX = rightBound;
}
this.mLastMotionX += scrollX - (float)((int)scrollX);
this.scrollTo((int)scrollX, this.getScrollY());
this.pageScrolled((int)scrollX);
}
}
break;
case 3:
1)this.determineDrag(ev);
private void determineDrag(MotionEvent ev) {
//多点触碰的activity第一个点的id
int activePointerId = this.mActivePointerId;
//多点触碰的activity第一个点的id对应的索引,代表每个手指的索引值
int pointerIndex = this.getPointerIndex(ev, activePointerId);
if(activePointerId != -1 && pointerIndex != -1) {
float x = MotionEventCompat.getX(ev, pointerIndex);
float dx = x - this.mLastMotionX;
float xDiff = Math.abs(dx);
float y = MotionEventCompat.getY(ev, pointerIndex);
float dy = y - this.mLastMotionY;
float yDiff = Math.abs(dy);
if(xDiff > (float)(this.isMenuOpen()?this.mTouchSlop / 2:this.mTouchSlop) && xDiff > yDiff && this.thisSlideAllowed(dx)) {
this.startDrag();
this.mLastMotionX = x;
this.mLastMotionY = y;
this.setScrollingCacheEnabled(true);
} else if(xDiff > (float)this.mTouchSlop) {
this.mIsUnableToDrag = true;
}
}
}
if(xDiff > (float)(this.isMenuOpen()?this.mTouchSlop / 2:this.mTouchSlop) && xDiff > yDiff && this.thisSlideAllowed(dx))
xDiff/yDiff代表水平和垂直方向移动的距离;
mTouchSlop是系统对于拖动的最低长度判断,即至少移动多少距离,才算是一个拖动的动作;
判断拖动条件:
水平移动距离xDiff大于拖动的最低长度(若菜单栏打开,xDiff大于拖动的最低长度/2),水平方向xDiff移动距离大于垂直方向yDiff移动距离,this.thisSlideAllowed(dx)判断是否允许滑动显示菜单栏(x轴移动的距离(正:向右滑动,负:向左滑动));
//CustomViewAbove
private boolean thisSlideAllowed(float dx) {
boolean allowed = false;
if(this.isMenuOpen()) {
allowed = this.mViewBehind.menuOpenSlideAllowed(dx);
} else {
allowed = this.mViewBehind.menuClosedSlideAllowed(dx);
}
return allowed;
}
//CustomViewBehind
public boolean menuClosedSlideAllowed(float dx) {
return this.mMode == 0?dx > 0.0F:(this.mMode == 1?dx < 0.0F:this.mMode == 2);
}
public boolean menuOpenSlideAllowed(float dx) {
return this.mMode == 0?dx < 0.0F:(this.mMode == 1?dx > 0.0F:this.mMode == 2);
}
根据滑动方向确认是否需要允许滑动显示菜单栏,例如菜单栏关闭状态判断this.mMode == 0(仅有左侧菜单栏)?dx > 0.0F,dx为正表示向右滑动,允许显示左侧菜单栏;
在x水平移动距离大于y垂直移动距离,同时移动的方向有设置了相应的菜单栏则允许滑动(判断是否允许拖动操作),则判断为触摸事件;
private void determineDrag(MotionEvent ev){
this.startDrag(); //主要设置mIsBeingDragged变量为true,表示滑动准备显示菜单栏
this.mLastMotionX = x;//记录滑动的x轴坐标
this.mLastMotionY = y;//记录滑动的y轴坐标
}
2)this.mIsBeingDragged==true
a.this.scrollTo((int)scrollX, this.getScrollY());
随着手指滑动动态计算滑动位置,执行视图滚动;
public void scrollTo(int x, int y) {
super.scrollTo(x, y);
this.mScrollX = (float)x;
this.mViewBehind.scrollBehindTo(this.mContent, x, y);
((SlidingMenu)this.getParent()).manageLayers(this.getPercentOpen());
}
super.scrollTo(x, y);执行视图滚动,
this.mViewBehind.scrollBehindTo(this.mContent, x, y);调用菜单栏视图执行滚动;
manageLayers是提高性能用的,不深入研究了;
b.this.pageScrolled((int)scrollX);
在SlidingMenu类中会设置监听器,在pageScrolled会调用监听器回调,回传滚动变化;
3.3手势抬起
case MotionEvent.ACTION_UP:
if(this.mIsBeingDragged) {
VelocityTracker velocityTracker = this.mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, (float)this.mMaximumVelocity);
int initialVelocity = (int)VelocityTrackerCompat.getXVelocity(velocityTracker, this.mActivePointerId);
int scrollX = this.getScrollX();
oldScrollX = (float)(scrollX - this.getDestScrollX(this.mCurItem)) / (float)this.getBehindWidth();
int activePointerIndex = this.getPointerIndex(ev, this.mActivePointerId);
if(this.mActivePointerId != -1) {
leftBound = MotionEventCompat.getX(ev, activePointerIndex);
int totalDelta = (int)(leftBound - this.mInitialMotionX);
int nextPage = this.determineTargetPage(oldScrollX, initialVelocity, totalDelta);
this.setCurrentItemInternal(nextPage, true, true, initialVelocity);
} else {
this.setCurrentItemInternal(this.mCurItem, true, true, initialVelocity);
}
this.mActivePointerId = -1;
this.endDrag();
} else if(this.mQuickReturn && this.mViewBehind.menuTouchInQuickReturn(this.mContent, this.mCurItem, ev.getX() + this.mScrollX)) {
this.setCurrentItem(1);
this.endDrag();
}
break;
手势抬起时检测是否是显示菜单栏滑动操作;
主要包含两部分实现,一个是计算滑动以后要显示页号(0:表示左菜单,1:表示当前主页,2:表示右侧菜单),另一部分是执行页面滚动,完成目标页号的显示;
a.int nextPage = this.determineTargetPage(oldScrollX, initialVelocity, totalDelta);
private int determineTargetPage(float pageOffset, int velocity, int deltaX) {
int targetPage = this.mCurItem;
if(Math.abs(deltaX) > this.mFlingDistance && Math.abs(velocity) > this.mMinimumVelocity) {
if(velocity > 0 && deltaX > 0) {
--targetPage;
} else if(velocity < 0 && deltaX < 0) {
++targetPage;
}
} else {
targetPage = Math.round((float)this.mCurItem + pageOffset);
}
return targetPage;
}
pageOffset:表示页面偏移比例(范围:-1-1),负值表示表示向右滑动,正值表示向左滑动;
velocity:表示滑动速度;
deltaX:总的滑动距离;
条件判断根据距离和速度判断,若移动的那个的距离大于this.mFlingDistance(float density = context.getResources().getDisplayMetrics().density;
this.mFlingDistance = (int)(25.0F * density);)同时滑动速度大于最小滑动速度,则根据滑动速度和具体计算目标页号,同时为正,显示左侧,同理相反;
否则采用四舍五入方式,例如pageOffset=-0.7;则1+(-0.7)=0.3,目标页号为0;
b.this.setCurrentItemInternal(nextPage, true, true, initialVelocity);
实际执行滚动显示目标页面;
void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
if(!always && this.mCurItem == item) {
this.setScrollingCacheEnabled(false);
} else {
item = this.mViewBehind.getMenuPage(item);
boolean dispatchSelected = this.mCurItem != item;
this.mCurItem = item;
int destX = this.getDestScrollX(this.mCurItem);
if(dispatchSelected && this.mOnPageChangeListener != null) {
this.mOnPageChangeListener.onPageSelected(item);
}
if(dispatchSelected && this.mInternalPageChangeListener != null) {
this.mInternalPageChangeListener.onPageSelected(item);
}
if(smoothScroll) {
this.smoothScrollTo(destX, 0, velocity);
} else {
this.completeScroll();
this.scrollTo(destX, 0);
}
}
}
a.页面变化监听;
b.滚动到目标页面
this.smoothScrollTo(destX, 0, velocity);
void smoothScrollTo(int x, int y, int velocity) {
if(this.getChildCount() == 0) {
this.setScrollingCacheEnabled(false);
} else {
int sx = this.getScrollX();
int sy = this.getScrollY();
int dx = x - sx;
int dy = y - sy;
if(dx == 0 && dy == 0) {
this.completeScroll();
if(this.isMenuOpen()) {
if(this.mOpenedListener != null) {
this.mOpenedListener.onOpened();
}
} else if(this.mClosedListener != null) {
this.mClosedListener.onClosed();
}
} else {
this.setScrollingCacheEnabled(true);
this.mScrolling = true;
int width = this.getBehindWidth();
int halfWidth = width / 2;
float distanceRatio = Math.min(1.0F, 1.0F * (float)Math.abs(dx) / (float)width);
float distance = (float)halfWidth + (float)halfWidth * this.distanceInfluenceForSnapDuration(distanceRatio);
int duration = false;
velocity = Math.abs(velocity);
int duration;
if(velocity > 0) {
duration = 4 * Math.round(1000.0F * Math.abs(distance / (float)velocity));
} else {
float pageDelta = (float)Math.abs(dx) / (float)width;
duration = (int)((pageDelta + 1.0F) * 100.0F);
duration = 600;
}
duration = Math.min(duration, 600);
this.mScroller.startScroll(sx, sy, dx, dy, duration);
this.invalidate();
}
}
}
判断条件若当前需要滚动的距离为0,则直接通知菜单栏打开或者额关闭;
否则根据duration执行滚动:
this.mScroller.startScroll(sx, sy, dx, dy, duration);
this.invalidate();
3.4事件的分发和拦截
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(!this.mEnabled) {
return false;
} else {
int action = ev.getAction() & 255;
if(action != 3 && action != 1 && (action == 0 || !this.mIsUnableToDrag)) {
switch(action) {
case 0:
int index = MotionEventCompat.getActionIndex(ev);
this.mActivePointerId = MotionEventCompat.getPointerId(ev, index);
if(this.mActivePointerId != -1) {
this.mLastMotionX = this.mInitialMotionX = MotionEventCompat.getX(ev, index);
this.mLastMotionY = MotionEventCompat.getY(ev, index);
if(this.thisTouchAllowed(ev)) {
this.mIsBeingDragged = false;
this.mIsUnableToDrag = false;
if(this.isMenuOpen() && this.mViewBehind.menuTouchInQuickReturn(this.mContent, this.mCurItem, ev.getX() + this.mScrollX)) {
this.mQuickReturn = true;
}
} else {
this.mIsUnableToDrag = true;
}
}
break;
case 2:
this.determineDrag(ev);
break;
case 6:
this.onSecondaryPointerUp(ev);
}
if(!this.mIsBeingDragged) {
if(this.mVelocityTracker == null) {
this.mVelocityTracker = VelocityTracker.obtain();
}
this.mVelocityTracker.addMovement(ev);
}
return this.mIsBeingDragged || this.mQuickReturn;
} else {
this.endDrag();
return false;
}
}
}
onInterceptTouchEvent返回true表示当前视图onTouchEvent处理触摸事件,false表示继续向下传递;
在菜单打开的时候要进行判断:
if(this.isMenuOpen() && this.mViewBehind.menuTouchInQuickReturn(this.mContent, this.mCurItem, ev.getX() + this.mScrollX)) {
this.mQuickReturn = true;
}
如果点击在主体部分,则自动收回菜单,即让主体部分消费这个touch事件;
如果此时点击在菜单部分,那above中应该不消费此事件,要将其传给behind部分,然后让behind中的listview或者button等自定义设置的控件去获取处理touch事件;
3.5当前页面点击返回按钮时关闭菜单栏
@Override
public void onBackPressed() {
if (slidingMenu.isMenuShowing() || slidingMenu.isSecondaryMenuShowing()) {
slidingMenu.showContent();
} else {
if (System.currentTimeMillis() - mLastClickTime <= 2000L) {
super.onBackPressed();
} else {
mLastClickTime = System.currentTimeMillis();
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
}
}
}
4.CustomViewBehind
CustomViewBehind主要实现菜单栏的滚动显示/隐藏和菜单栏的阴影绘制等;
4.1菜单栏的滚动显示/隐藏
public void scrollBehindTo(View content, int x, int y) {
int vis = 0;
if(this.mMode == 0) {
if(x >= content.getLeft()) {
vis = 4;
}
this.scrollTo((int)((float)(x + this.getBehindWidth()) * this.mScrollScale), y);
} else if(this.mMode == 1) {
if(x <= content.getLeft()) {
vis = 4;
}
this.scrollTo((int)((float)(this.getBehindWidth() - this.getWidth()) + (float)(x - this.getBehindWidth()) * this.mScrollScale), y);
} else if(this.mMode == 2) {
this.mContent.setVisibility(x >= content.getLeft()?4:0);
this.mSecondaryContent.setVisibility(x <= content.getLeft()?4:0);
vis = x == 0?4:0;
if(x <= content.getLeft()) {
this.scrollTo((int)((float)(x + this.getBehindWidth()) * this.mScrollScale), y);
} else {
this.scrollTo((int)((float)(this.getBehindWidth() - this.getWidth()) + (float)(x - this.getBehindWidth()) * this.mScrollScale), y);
}
}
if(vis == 4) {
Log.v("CustomViewBehind", "behind INVISIBLE");
}
this.setVisibility(vis);
}
4.1.1只分析LEFT的情况(请他情况同理)
检测滚动的x坐标大于主视图CustomViewAbove左侧则显示菜单栏视图;
if(x >= content.getLeft()) {
vis = 4;
}
实现菜单栏内滚动
this.scrollTo((int)((float)(x + this.getBehindWidth()) * this.mScrollScale), y);
比如菜单的width即getBehindWidth的宽度为300
那从菜单关闭状态一直滚到菜单完全打开状态,above主体内容x轴上的scroll变化就是0 ~ -300
此时如果mScrollScale即滚动比例为0.3
那按照上面scrollBehindTo中的算法, behind菜单部分x轴上的scroll变化就是
(0+300)*0.3 ~ (-300+300)*0.3即100~0
如果比例换成0.6,那behind的x变化就是180~0
极端情况下
1.mScrollScale=0, behind的x变化为 0~0, 此时会发现behind菜单部分在打开关闭的过程中无任何滚动
2.mScrollScale=1,behind的换标为300~0, 此时的效果就是主体和菜单紧挨着以同一个速度滚动
注意,这里的x为滚动的偏移量,即scrollTo使用的参数,
比如scrollView中,scrollTo(0, 100)会往下滚动到y偏移量100的位置,
此时scrollView里面的内容视觉上看其实是向上移了~
这里同理
虽然朝右拖动的时候view看上去是朝右运动,但x轴上滚动偏移量是减少的
4.1.2在CustomViewAbove重写dispatchDraw方法,调用CustomViewAbove绘制阴影,淡入和选择器方法实现绘制
CustomViewAbove
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
this.mViewBehind.drawShadow(this.mContent, canvas);
this.mViewBehind.drawFade(this.mContent, canvas, this.getPercentOpen());
this.mViewBehind.drawSelector(this.mContent, canvas, this.getPercentOpen());
}
CustomViewAbove
public void drawShadow(View content, Canvas canvas) {
if(this.mShadowDrawable != null && this.mShadowWidth > 0) {
int left = 0;
if(this.mMode == 0) {
left = content.getLeft() - this.mShadowWidth;
} else if(this.mMode == 1) {
left = content.getRight();
} else if(this.mMode == 2) {
if(this.mSecondaryShadowDrawable != null) {
left = content.getRight();
this.mSecondaryShadowDrawable.setBounds(left, 0, left + this.mShadowWidth, this.getHeight());
this.mSecondaryShadowDrawable.draw(canvas);
}
left = content.getLeft() - this.mShadowWidth;
}
this.mShadowDrawable.setBounds(left, 0, left + this.mShadowWidth, this.getHeight());
this.mShadowDrawable.draw(canvas);
}
}
this.mShadowDrawable.draw(canvas);将阴影Drawable绘制到画布上;
public void drawFade(View content, Canvas canvas, float openPercent) {
if(this.mFadeEnabled) {
int alpha = (int)(this.mFadeDegree * 255.0F * Math.abs(1.0F - openPercent));
this.mFadePaint.setColor(Color.argb(alpha, 0, 0, 0));
int left = 0;
int right = 0;
if(this.mMode == 0) {
left = content.getLeft() - this.getBehindWidth();
right = content.getLeft();
} else if(this.mMode == 1) {
left = content.getRight();
right = content.getRight() + this.getBehindWidth();
} else if(this.mMode == 2) {
left = content.getLeft() - this.getBehindWidth();
right = content.getLeft();
canvas.drawRect((float)left, 0.0F, (float)right, (float)this.getHeight(), this.mFadePaint);
left = content.getRight();
right = content.getRight() + this.getBehindWidth();
}
canvas.drawRect((float)left, 0.0F, (float)right, (float)this.getHeight(), this.mFadePaint);
}
}
在画布上绘制阴影效果;
参考:
https://blog.csdn.net/u011494050/article/details/43531087
https://github.com/jfeinstein10/SlidingMenu
最后
以上就是仁爱自行车为你收集整理的Android之SlidingMenu源码分析1.SlidingMenu是什么2.SlidingMenu使用及源码分析3.SlidingMenu下实现滑动显示左右菜单栏源码分析4.CustomViewBehind的全部内容,希望文章能够帮你解决Android之SlidingMenu源码分析1.SlidingMenu是什么2.SlidingMenu使用及源码分析3.SlidingMenu下实现滑动显示左右菜单栏源码分析4.CustomViewBehind所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复