原创博客地址:点击传送
AndroidResideMenu
github:https://github.com/SpecialCyCi/AndroidResideMenu csdn:http://download.youkuaiyun.com/detail/cym492224103/7887801
先看看如何使用:
把项目源码下载下来导入工程,可以看到
ResideMenu为引用工程,再看看如何使用这个引用工程来构建出ResideMenu,
1.先new一个ResideMenu对象
1.
resideMenu =
new
ResideMenu(
this
);
1.
resideMenu.setBackground(R.drawable.menu_background);
1.
resideMenu.attachToActivity(
this
);
1.
resideMenu.setMenuListener(menuListener);
01.
private
ResideMenu.OnMenuListener menuListener =
new
ResideMenu.OnMenuListener() {
02.
@Override
03.
public
void
openMenu() {
04.
Toast.makeText(mContext,
"Menu is opened!"
, Toast.LENGTH_SHORT).show();
05.
}
06.
07.
@Override
08.
public
void
closeMenu() {
09.
Toast.makeText(mContext,
"Menu is closed!"
, Toast.LENGTH_SHORT).show();
10.
}
11.
};
1.
//valid scale factor is between 0.0f and 1.0f. leftmenu'width is 150dip.
2.
resideMenu.setScaleValue(
0
.6f);
1.
// create menu items;
2.
itemHome =
new
ResideMenuItem(
this
, R.drawable.icon_home,
"Home"
);
3.
itemProfile =
new
ResideMenuItem(
this
, R.drawable.icon_profile,
"Profile"
);
4.
itemCalendar =
new
ResideMenuItem(
this
, R.drawable.icon_calendar,
"Calendar"
);
5.
itemSettings =
new
ResideMenuItem(
this
, R.drawable.icon_settings,
"Settings"
);
01.
itemHome.setOnClickListener(
this
);
02.
itemProfile.setOnClickListener(
this
);
03.
itemCalendar.setOnClickListener(
this
);
04.
itemSettings.setOnClickListener(
this
);
05.
06.
resideMenu.addMenuItem(itemHome, ResideMenu.DIRECTION_LEFT);
07.
resideMenu.addMenuItem(itemProfile, ResideMenu.DIRECTION_LEFT);
08.
resideMenu.addMenuItem(itemCalendar, ResideMenu.DIRECTION_RIGHT);
09.
resideMenu.addMenuItem(itemSettings, ResideMenu.DIRECTION_RIGHT);
01.
// You can disable a direction by setting ->
02.
// resideMenu.setSwipeDirectionDisable(ResideMenu.DIRECTION_RIGHT);
03.
04.
findViewById(R.id.title_bar_left_menu).setOnClickListener(
new
View.OnClickListener() {
05.
@Override
06.
public
void
onClick(View view) {
07.
resideMenu.openMenu(ResideMenu.DIRECTION_LEFT);
08.
}
09.
});
10.
findViewById(R.id.title_bar_right_menu).setOnClickListener(
new
View.OnClickListener() {
11.
@Override
12.
public
void
onClick(View view) {
13.
resideMenu.openMenu(ResideMenu.DIRECTION_RIGHT);
14.
}
15.
});
1.
@Override
2.
public
boolean
dispatchTouchEvent(MotionEvent ev) {
3.
return
resideMenu.dispatchTouchEvent(ev);
4.
}
1.
resideMenu.closeMenu();
11.屏蔽菜单方法
1.
// You can disable a direction by setting ->
2.
// resideMenu.setSwipeDirectionDisable(ResideMenu.DIRECTION_RIGHT);
使用方法已经说完了,接下来,看看它的源码,先看看源码的项目结构。
很多人初学者都曾纠结,看源码,如何从何看起,我个人建议从上面使用的顺序看起,并且在看的时候要带个问题去看去思考,这样更容易理解。
上面的第一步是,创建ResideMenu对象,我们就看看ResideMenu的构造。
1.
public
ResideMenu(Context context) {
2.
super
(context);
3.
initViews(context);
4.
}
01.
private
void
initViews(Context context){
02.
LayoutInflater inflater = (LayoutInflater)
03.
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
04.
inflater.inflate(R.layout.residemenu,
this
);
05.
scrollViewLeftMenu = (ScrollView) findViewById(R.id.sv_left_menu);
06.
scrollViewRightMenu = (ScrollView) findViewById(R.id.sv_right_menu);
07.
imageViewShadow = (ImageView) findViewById(R.id.iv_shadow);
08.
layoutLeftMenu = (LinearLayout) findViewById(R.id.layout_left_menu);
09.
layoutRightMenu = (LinearLayout) findViewById(R.id.layout_right_menu);
10.
imageViewBackground = (ImageView) findViewById(R.id.iv_background);
11.
}
01.
<?xml version=
"1.0"
encoding=
"utf-8"
?>
02.
03.
<FrameLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
04.
android:layout_width=
"match_parent"
05.
android:layout_height=
"match_parent"
>
06.
<ImageView
07.
android:id=
"@+id/iv_background"
08.
android:adjustViewBounds=
"true"
09.
android:scaleType=
"centerCrop"
10.
android:layout_width=
"match_parent"
11.
android:layout_height=
"match_parent"
/>
12.
13.
<ImageView
14.
android:id=
"@+id/iv_shadow"
15.
android:background=
"@drawable/shadow"
16.
android:layout_width=
"fill_parent"
17.
android:layout_height=
"fill_parent"
18.
android:scaleType=
"fitXY"
/>
19.
20.
<ScrollView
21.
android:id=
"@+id/sv_left_menu"
22.
android:scrollbars=
"none"
23.
android:paddingLeft=
"30dp"
24.
android:layout_width=
"150dp"
25.
android:layout_height=
"fill_parent"
>
26.
<LinearLayout
27.
android:id=
"@+id/layout_left_menu"
28.
android:orientation=
"vertical"
29.
android:layout_gravity=
"center_vertical"
30.
android:layout_width=
"wrap_content"
31.
android:layout_height=
"wrap_content"
>
32.
33.
</LinearLayout>
34.
</ScrollView>
35.
36.
<ScrollView
37.
android:id=
"@+id/sv_right_menu"
38.
android:scrollbars=
"none"
39.
android:paddingRight=
"30dp"
40.
android:layout_width=
"150dp"
41.
android:layout_height=
"fill_parent"
42.
android:layout_gravity=
"right"
>
43.
<LinearLayout
44.
android:id=
"@+id/layout_right_menu"
45.
android:orientation=
"vertical"
46.
android:layout_gravity=
"center_vertical"
47.
android:layout_width=
"wrap_content"
48.
android:layout_height=
"wrap_content"
49.
android:gravity=
"right"
>
50.
51.
</LinearLayout>
52.
</ScrollView>
53.
54.
</FrameLayout>
从布局文件,以及显示效果我们可以看到,它是一个帧布局,第一个ImageView是背景,第二个ImageView是.9的阴影效果的图片(看下面的图),
两个(ScrollView包裹着一个LinerLayout),可以从上面图看到结构分别是左菜单和右菜单
1.
<img src=
"https://img-blog.youkuaiyun.com/20140910100807704?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast"
style=
"font-family: Arial; background-color: rgb(255, 255, 255);"
alt=
""
/>
1.初始化布局以及布局文件分析完毕,2.接下来是设置背景图,初始化view的时候就已经拿到了背景控件,所以设置背景图也是非常好实现的事情了。
1.
public
void
setBackground(
int
imageResrouce){
2.
imageViewBackground.setImageResource(imageResrouce);
3.
}
01.
/**
02.
* use the method to set up the activity which residemenu need to show;
03.
*
04.
* @param activity
05.
*/
06.
public
void
attachToActivity(Activity activity){
07.
initValue(activity);
08.
setShadowAdjustScaleXByOrientation();
09.
viewDecor.addView(
this
,
0
);
10.
setViewPadding();
11.
}
1.初始化参数:
01.
private
void
initValue(Activity activity){
02.
this
.activity = activity;
03.
leftMenuItems =
new
ArrayList<ResideMenuItem>();
04.
rightMenuItems =
new
ArrayList<ResideMenuItem>();
05.
ignoredViews =
new
ArrayList<View>();
06.
viewDecor = (ViewGroup) activity.getWindow().getDecorView();
07.
viewActivity =
new
TouchDisableView(
this
.activity);
08.
09.
View mContent = viewDecor.getChildAt(
0
);
10.
viewDecor.removeViewAt(
0
);
11.
viewActivity.setContent(mContent);
12.
addView(viewActivity);
13.
14.
ViewGroup parent = (ViewGroup) scrollViewLeftMenu.getParent();
15.
parent.removeView(scrollViewLeftMenu);
16.
parent.removeView(scrollViewRightMenu);
17.
}
2.正对横竖屏缩放比例进行调整
01.
private
void
setShadowAdjustScaleXByOrientation(){
02.
int
orientation = getResources().getConfiguration().orientation;
03.
if
(orientation == Configuration.ORIENTATION_LANDSCAPE) {
04.
shadowAdjustScaleX =
0
.034f;
05.
shadowAdjustScaleY =
0
.12f;
06.
}
else
if
(orientation == Configuration.ORIENTATION_PORTRAIT) {
07.
shadowAdjustScaleX =
0
.06f;
08.
shadowAdjustScaleY =
0
.07f;
09.
}
10.
}
3.添加当前view
1.
viewDecor.addView(
this
,
0
);
4.设置view边距
01.
/**
02.
* we need the call the method before the menu show, because the
03.
* padding of activity can't get at the moment of onCreateView();
04.
*/
05.
private
void
setViewPadding(){
06.
this
.setPadding(viewActivity.getPaddingLeft(),
07.
viewActivity.getPaddingTop(),
08.
viewActivity.getPaddingRight(),
09.
viewActivity.getPaddingBottom());
10.
}
01.
private
Animator.AnimatorListener animationListener =
new
Animator.AnimatorListener() {
02.
@Override
03.
public
void
onAnimationStart(Animator animation) {
04.
if
(isOpened()){
05.
showScrollViewMenu();
06.
if
(menuListener !=
null
)
07.
menuListener.openMenu();
08.
}
09.
}
10.
11.
@Override
12.
public
void
onAnimationEnd(Animator animation) {
13.
// reset the view;
14.
if
(isOpened()){
15.
viewActivity.setTouchDisable(
true
);
16.
viewActivity.setOnClickListener(viewActivityOnClickListener);
17.
}
else
{
18.
viewActivity.setTouchDisable(
false
);
19.
viewActivity.setOnClickListener(
null
);
20.
hideScrollViewMenu();
21.
if
(menuListener !=
null
)
22.
menuListener.closeMenu();
23.
}
24.
}
25.
26.
@Override
27.
public
void
onAnimationCancel(Animator animation) {
28.
29.
}
30.
31.
@Override
32.
public
void
onAnimationRepeat(Animator animation) {
33.
34.
}
35.
};
01.
private
float
getTargetScale(
float
currentRawX){
02.
float
scaleFloatX = ((currentRawX - lastRawX) / getScreenWidth()) *
0
.75f;
03.
scaleFloatX = scaleDirection == DIRECTION_RIGHT ? - scaleFloatX : scaleFloatX;
04.
05.
float
targetScale = ViewHelper.getScaleX(viewActivity) - scaleFloatX;
06.
targetScale = targetScale >
1
.0f ?
1
.0f : targetScale;
07.
targetScale = targetScale <
0
.5f ?
0
.5f : targetScale;
08.
return
targetScale;
09.
}
默认缩放比例:
1.
//valid scale factor is between 0.0f and 1.0f.
2.
private
float
mScaleValue =
0
.5f;
1.
AnimatorSet scaleDown_activity = buildScaleDownAnimation(viewActivity, mScaleValue, mScaleValue);
01.
/**
02.
* a helper method to build scale down animation;
03.
*
04.
* @param target
05.
* @param targetScaleX
06.
* @param targetScaleY
07.
* @return
08.
*/
09.
private
AnimatorSet buildScaleDownAnimation(View target,
float
targetScaleX,
float
targetScaleY){
10.
11.
AnimatorSet scaleDown =
new
AnimatorSet();
12.
scaleDown.playTogether(
13.
ObjectAnimator.ofFloat(target,
"scaleX"
, targetScaleX),
14.
ObjectAnimator.ofFloat(target,
"scaleY"
, targetScaleY)
15.
);
16.
17.
scaleDown.setInterpolator(AnimationUtils.loadInterpolator(activity,
18.
android.R.anim.decelerate_interpolator));
19.
scaleDown.setDuration(
250
);
20.
return
scaleDown;
21.
}
01.
public
ResideMenuItem(Context context,
int
icon, String title) {
02.
super
(context);
03.
initViews(context);
04.
iv_icon.setImageResource(icon);
05.
tv_title.setText(title);
06.
}
07.
08.
private
void
initViews(Context context){
09.
LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
10.
inflater.inflate(R.layout.residemenu_item,
this
);
11.
iv_icon = (ImageView) findViewById(R.id.iv_icon);
12.
tv_title = (TextView) findViewById(R.id.tv_title);
13.
}
01.
<?xml version=
"1.0"
encoding=
"utf-8"
?>
02.
03.
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
04.
android:orientation=
"horizontal"
05.
android:layout_width=
"match_parent"
06.
android:layout_height=
"wrap_content"
07.
android:gravity=
"center_vertical"
08.
android:paddingTop=
"30dp"
>
09.
10.
<ImageView
11.
android:layout_width=
"30dp"
12.
android:layout_height=
"30dp"
13.
android:scaleType=
"centerCrop"
14.
android:id=
"@+id/iv_icon"
/>
15.
16.
<TextView
17.
android:layout_width=
"match_parent"
18.
android:layout_height=
"wrap_content"
19.
android:textColor=
"@android:color/white"
20.
android:textSize=
"18sp"
21.
android:layout_marginLeft=
"10dp"
22.
android:id=
"@+id/tv_title"
/>
23.
24.
</LinearLayout>
显示效果图:
01.
/**
02.
* add a single items;
03.
*
04.
* @param menuItem
05.
* @param direction
06.
*/
07.
public
void
addMenuItem(ResideMenuItem menuItem,
int
direction){
08.
if
(direction == DIRECTION_LEFT){
09.
this
.leftMenuItems.add(menuItem);
10.
layoutLeftMenu.addView(menuItem);
11.
}
else
{
12.
this
.rightMenuItems.add(menuItem);
13.
layoutRightMenu.addView(menuItem);
14.
}
15.
}
01.
/**
02.
* show the reside menu;
03.
*/
04.
public
void
openMenu(
int
direction){
05.
06.
setScaleDirection(direction);
07.
08.
isOpened =
true
;
09.
AnimatorSet scaleDown_activity = buildScaleDownAnimation(viewActivity, mScaleValue, mScaleValue);
10.
AnimatorSet scaleDown_shadow = buildScaleDownAnimation(imageViewShadow,
11.
mScaleValue + shadowAdjustScaleX, mScaleValue + shadowAdjustScaleY);
12.
AnimatorSet alpha_menu = buildMenuAnimation(scrollViewMenu,
1
.0f);
13.
scaleDown_shadow.addListener(animationListener);
14.
scaleDown_activity.playTogether(scaleDown_shadow);
15.
scaleDown_activity.playTogether(alpha_menu);
16.
scaleDown_activity.start();
17.
}
01.
private
void
setScaleDirection(
int
direction){
02.
03.
int
screenWidth = getScreenWidth();
04.
float
pivotX;
05.
float
pivotY = getScreenHeight() *
0
.5f;
06.
07.
if
(direction == DIRECTION_LEFT){
08.
scrollViewMenu = scrollViewLeftMenu;
09.
pivotX = screenWidth *
1
.5f;
10.
}
else
{
11.
scrollViewMenu = scrollViewRightMenu;
12.
pivotX = screenWidth * -
0
.5f;
13.
}
14.
15.
ViewHelper.setPivotX(viewActivity, pivotX);
16.
ViewHelper.setPivotY(viewActivity, pivotY);
17.
ViewHelper.setPivotX(imageViewShadow, pivotX);
18.
ViewHelper.setPivotY(imageViewShadow, pivotY);
19.
scaleDirection = direction;
20.
}
如果还不了解,dispatchTouchEvent这个函数如何调用?什么时候调用?请先看看http://blog.youkuaiyun.com/cym492224103/article/details/39179311
01.
@Override
02.
public
boolean
dispatchTouchEvent(MotionEvent ev) {
03.
float
currentActivityScaleX = ViewHelper.getScaleX(viewActivity);
04.
if
(currentActivityScaleX ==
1
.0f)
05.
setScaleDirectionByRawX(ev.getRawX());
06.
07.
switch
(ev.getAction()){
08.
case
MotionEvent.ACTION_DOWN:
09.
lastActionDownX = ev.getX();
10.
lastActionDownY = ev.getY();
11.
isInIgnoredView = isInIgnoredView(ev) && !isOpened();
12.
pressedState = PRESSED_DOWN;
13.
break
;
14.
15.
case
MotionEvent.ACTION_MOVE:
16.
if
(isInIgnoredView || isInDisableDirection(scaleDirection))
17.
break
;
18.
19.
if
(pressedState != PRESSED_DOWN &&
20.
pressedState != PRESSED_MOVE_HORIZANTAL)
21.
break
;
22.
23.
int
xOffset = (
int
) (ev.getX() - lastActionDownX);
24.
int
yOffset = (
int
) (ev.getY() - lastActionDownY);
25.
26.
if
(pressedState == PRESSED_DOWN) {
27.
if
(yOffset >
25
|| yOffset < -
25
) {
28.
pressedState = PRESSED_MOVE_VERTICAL;
29.
break
;
30.
}
31.
if
(xOffset < -
50
|| xOffset >
50
) {
32.
pressedState = PRESSED_MOVE_HORIZANTAL;
33.
ev.setAction(MotionEvent.ACTION_CANCEL);
34.
}
35.
}
else
if
(pressedState == PRESSED_MOVE_HORIZANTAL) {
36.
if
(currentActivityScaleX <
0.95
)
37.
showScrollViewMenu();
38.
39.
float
targetScale = getTargetScale(ev.getRawX());
40.
ViewHelper.setScaleX(viewActivity, targetScale);
41.
ViewHelper.setScaleY(viewActivity, targetScale);
42.
ViewHelper.setScaleX(imageViewShadow, targetScale + shadowAdjustScaleX);
43.
ViewHelper.setScaleY(imageViewShadow, targetScale + shadowAdjustScaleY);
44.
ViewHelper.setAlpha(scrollViewMenu, (
1
- targetScale) *
2
.0f);
45.
46.
lastRawX = ev.getRawX();
47.
return
true
;
48.
}
49.
50.
break
;
51.
52.
case
MotionEvent.ACTION_UP:
53.
54.
if
(isInIgnoredView)
break
;
55.
if
(pressedState != PRESSED_MOVE_HORIZANTAL)
break
;
56.
57.
pressedState = PRESSED_DONE;
58.
if
(isOpened()){
59.
if
(currentActivityScaleX >
0
.56f)
60.
closeMenu();
61.
else
62.
openMenu(scaleDirection);
63.
}
else
{
64.
if
(currentActivityScaleX <
0
.94f){
65.
openMenu(scaleDirection);
66.
}
else
{
67.
closeMenu();
68.
}
69.
}
70.
71.
break
;
72.
73.
}
74.
lastRawX = ev.getRawX();
75.
return
super
.dispatchTouchEvent(ev);
76.
}
MotionEvent.ACTION_DOWN:
记录了X,Y轴的坐标点,判断是否打开,设置了按下的状态为PRESSED_DOWN
MotionEvent.ACTION_MOVE:
拿到当前X,Y减去DOWN下记录下来的X,Y,这样得到了移动的X,Y,然后判断如果如果移动的X,Y大于25或者小于-25就改变按下状态为PRESSED_MOVE_VERTICAL
如果移动的X,Y大于50或者小于-50就改变状态为PRESSED_MOVE_HORIZANTAL
状态为PRESSED_MOVE_HORIZANTAL就改变菜单主视图内容以及阴影图片大小,在改变的同时还设置了当前菜单的透明度。
MotionEvent.ACTION_UP:
判断是否菜单是否打开状态,在获取当前缩放的X比例,
判断比例小于0.56f,则关闭菜单,反正开启菜单。
看完后,我们在回去看看代码,就会发现其实也不过如此~!
10.菜单关闭方法,同样也设置了动画监听之前的想法也是成立的。
01.
/**
02.
* close the reslide menu;
03.
*/
04.
public
void
closeMenu(){
05.
06.
isOpened =
false
;
07.
AnimatorSet scaleUp_activity = buildScaleUpAnimation(viewActivity,
1
.0f,
1
.0f);
08.
AnimatorSet scaleUp_shadow = buildScaleUpAnimation(imageViewShadow,
1
.0f,
1
.0f);
09.
AnimatorSet alpha_menu = buildMenuAnimation(scrollViewMenu,
0
.0f);
10.
scaleUp_activity.addListener(animationListener);
11.
scaleUp_activity.playTogether(scaleUp_shadow);
12.
scaleUp_activity.playTogether(alpha_menu);
13.
scaleUp_activity.start();
14.
}
1.
public
void
setSwipeDirectionDisable(
int
direction){
2.
disabledSwipeDirection.add(direction);
3.
}
1.
private
boolean
isInDisableDirection(
int
direction){
2.
return
disabledSwipeDirection.contains(direction);
3.
}
1.
if
(isInIgnoredView || isInDisableDirection(scaleDirection))
最后我们会发现我们一直都没说到TouchDisableView,其实initValue的时候就初始化了,它就是viewActivity,是我们的内容视图。
我们来看看它做了什么?
01.
@Override
02.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
03.
04.
int
width = getDefaultSize(
0
, widthMeasureSpec);
05.
int
height = getDefaultSize(
0
, heightMeasureSpec);
06.
setMeasuredDimension(width, height);
07.
08.
final
int
contentWidth = getChildMeasureSpec(widthMeasureSpec,
0
, width);
09.
final
int
contentHeight = getChildMeasureSpec(heightMeasureSpec,
0
, height);
10.
mContent.measure(contentWidth, contentHeight);
11.
}
12.
13.
@Override
14.
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
15.
final
int
width = r - l;
16.
final
int
height = b - t;
17.
mContent.layout(
0
,
0
, width, height);
18.
}
19.
20.
@Override
21.
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
22.
return
mTouchDisabled;
23.
}
24.
25.
void
setTouchDisable(
boolean
disableTouch) {
26.
mTouchDisabled = disableTouch;
27.
}
28.
29.
boolean
isTouchDisabled() {
30.
return
mTouchDisabled;
31.
}
原创博客地址:点击传送
AndroidResideMenu
github:https://github.com/SpecialCyCi/AndroidResideMenu csdn:http://download.youkuaiyun.com/detail/cym492224103/7887801
先看看如何使用:
把项目源码下载下来导入工程,可以看到
ResideMenu为引用工程,再看看如何使用这个引用工程来构建出ResideMenu,
1.先new一个ResideMenu对象
1.
resideMenu =
new
ResideMenu(
this
);
1.
resideMenu.setBackground(R.drawable.menu_background);
1.
resideMenu.attachToActivity(
this
);
1.
resideMenu.setMenuListener(menuListener);
01.
private
ResideMenu.OnMenuListener menuListener =
new
ResideMenu.OnMenuListener() {
02.
@Override
03.
public
void
openMenu() {
04.
Toast.makeText(mContext,
"Menu is opened!"
, Toast.LENGTH_SHORT).show();
05.
}
06.
07.
@Override
08.
public
void
closeMenu() {
09.
Toast.makeText(mContext,
"Menu is closed!"
, Toast.LENGTH_SHORT).show();
10.
}
11.
};
1.
//valid scale factor is between 0.0f and 1.0f. leftmenu'width is 150dip.
2.
resideMenu.setScaleValue(
0
.6f);
1.
// create menu items;
2.
itemHome =
new
ResideMenuItem(
this
, R.drawable.icon_home,
"Home"
);
3.
itemProfile =
new
ResideMenuItem(
this
, R.drawable.icon_profile,
"Profile"
);
4.
itemCalendar =
new
ResideMenuItem(
this
, R.drawable.icon_calendar,
"Calendar"
);
5.
itemSettings =
new
ResideMenuItem(
this
, R.drawable.icon_settings,
"Settings"
);
01.
itemHome.setOnClickListener(
this
);
02.
itemProfile.setOnClickListener(
this
);
03.
itemCalendar.setOnClickListener(
this
);
04.
itemSettings.setOnClickListener(
this
);
05.
06.
resideMenu.addMenuItem(itemHome, ResideMenu.DIRECTION_LEFT);
07.
resideMenu.addMenuItem(itemProfile, ResideMenu.DIRECTION_LEFT);
08.
resideMenu.addMenuItem(itemCalendar, ResideMenu.DIRECTION_RIGHT);
09.
resideMenu.addMenuItem(itemSettings, ResideMenu.DIRECTION_RIGHT);
01.
// You can disable a direction by setting ->
02.
// resideMenu.setSwipeDirectionDisable(ResideMenu.DIRECTION_RIGHT);
03.
04.
findViewById(R.id.title_bar_left_menu).setOnClickListener(
new
View.OnClickListener() {
05.
@Override
06.
public
void
onClick(View view) {
07.
resideMenu.openMenu(ResideMenu.DIRECTION_LEFT);
08.
}
09.
});
10.
findViewById(R.id.title_bar_right_menu).setOnClickListener(
new
View.OnClickListener() {
11.
@Override
12.
public
void
onClick(View view) {
13.
resideMenu.openMenu(ResideMenu.DIRECTION_RIGHT);
14.
}
15.
});
1.
@Override
2.
public
boolean
dispatchTouchEvent(MotionEvent ev) {
3.
return
resideMenu.dispatchTouchEvent(ev);
4.
}
1.
resideMenu.closeMenu();
11.屏蔽菜单方法
1.
// You can disable a direction by setting ->
2.
// resideMenu.setSwipeDirectionDisable(ResideMenu.DIRECTION_RIGHT);
使用方法已经说完了,接下来,看看它的源码,先看看源码的项目结构。
很多人初学者都曾纠结,看源码,如何从何看起,我个人建议从上面使用的顺序看起,并且在看的时候要带个问题去看去思考,这样更容易理解。
上面的第一步是,创建ResideMenu对象,我们就看看ResideMenu的构造。
1.
public
ResideMenu(Context context) {
2.
super
(context);
3.
initViews(context);
4.
}
01.
private
void
initViews(Context context){
02.
LayoutInflater inflater = (LayoutInflater)
03.
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
04.
inflater.inflate(R.layout.residemenu,
this
);
05.
scrollViewLeftMenu = (ScrollView) findViewById(R.id.sv_left_menu);
06.
scrollViewRightMenu = (ScrollView) findViewById(R.id.sv_right_menu);
07.
imageViewShadow = (ImageView) findViewById(R.id.iv_shadow);
08.
layoutLeftMenu = (LinearLayout) findViewById(R.id.layout_left_menu);
09.
layoutRightMenu = (LinearLayout) findViewById(R.id.layout_right_menu);
10.
imageViewBackground = (ImageView) findViewById(R.id.iv_background);
11.
}
01.
<?xml version=
"1.0"
encoding=
"utf-8"
?>
02.
03.
<FrameLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
04.
android:layout_width=
"match_parent"
05.
android:layout_height=
"match_parent"
>
06.
<ImageView
07.
android:id=
"@+id/iv_background"
08.
android:adjustViewBounds=
"true"
09.
android:scaleType=
"centerCrop"
10.
android:layout_width=
"match_parent"
11.
android:layout_height=
"match_parent"
/>
12.
13.
<ImageView
14.
android:id=
"@+id/iv_shadow"
15.
android:background=
"@drawable/shadow"
16.
android:layout_width=
"fill_parent"
17.
android:layout_height=
"fill_parent"
18.
android:scaleType=
"fitXY"
/>
19.
20.
<ScrollView
21.
android:id=
"@+id/sv_left_menu"
22.
android:scrollbars=
"none"
23.
android:paddingLeft=
"30dp"
24.
android:layout_width=
"150dp"
25.
android:layout_height=
"fill_parent"
>
26.
<LinearLayout
27.
android:id=
"@+id/layout_left_menu"
28.
android:orientation=
"vertical"
29.
android:layout_gravity=
"center_vertical"
30.
android:layout_width=
"wrap_content"
31.
android:layout_height=
"wrap_content"
>
32.
33.
</LinearLayout>
34.
</ScrollView>
35.
36.
<ScrollView
37.
android:id=
"@+id/sv_right_menu"
38.
android:scrollbars=
"none"
39.
android:paddingRight=
"30dp"
40.
android:layout_width=
"150dp"
41.
android:layout_height=
"fill_parent"
42.
android:layout_gravity=
"right"
>
43.
<LinearLayout
44.
android:id=
"@+id/layout_right_menu"
45.
android:orientation=
"vertical"
46.
android:layout_gravity=
"center_vertical"
47.
android:layout_width=
"wrap_content"
48.
android:layout_height=
"wrap_content"
49.
android:gravity=
"right"
>
50.
51.
</LinearLayout>
52.
</ScrollView>
53.
54.
</FrameLayout>
从布局文件,以及显示效果我们可以看到,它是一个帧布局,第一个ImageView是背景,第二个ImageView是.9的阴影效果的图片(看下面的图),
两个(ScrollView包裹着一个LinerLayout),可以从上面图看到结构分别是左菜单和右菜单
1.
<img src=
"https://img-blog.youkuaiyun.com/20140910100807704?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3ltNDkyMjI0MTAz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast"
style=
"font-family: Arial; background-color: rgb(255, 255, 255);"
alt=
""
/>
1.初始化布局以及布局文件分析完毕,2.接下来是设置背景图,初始化view的时候就已经拿到了背景控件,所以设置背景图也是非常好实现的事情了。
1.
public
void
setBackground(
int
imageResrouce){
2.
imageViewBackground.setImageResource(imageResrouce);
3.
}
01.
/**
02.
* use the method to set up the activity which residemenu need to show;
03.
*
04.
* @param activity
05.
*/
06.
public
void
attachToActivity(Activity activity){
07.
initValue(activity);
08.
setShadowAdjustScaleXByOrientation();
09.
viewDecor.addView(
this
,
0
);
10.
setViewPadding();
11.
}
1.初始化参数:
01.
private
void
initValue(Activity activity){
02.
this
.activity = activity;
03.
leftMenuItems =
new
ArrayList<ResideMenuItem>();
04.
rightMenuItems =
new
ArrayList<ResideMenuItem>();
05.
ignoredViews =
new
ArrayList<View>();
06.
viewDecor = (ViewGroup) activity.getWindow().getDecorView();
07.
viewActivity =
new
TouchDisableView(
this
.activity);
08.
09.
View mContent = viewDecor.getChildAt(
0
);
10.
viewDecor.removeViewAt(
0
);
11.
viewActivity.setContent(mContent);
12.
addView(viewActivity);
13.
14.
ViewGroup parent = (ViewGroup) scrollViewLeftMenu.getParent();
15.
parent.removeView(scrollViewLeftMenu);
16.
parent.removeView(scrollViewRightMenu);
17.
}
2.正对横竖屏缩放比例进行调整
01.
private
void
setShadowAdjustScaleXByOrientation(){
02.
int
orientation = getResources().getConfiguration().orientation;
03.
if
(orientation == Configuration.ORIENTATION_LANDSCAPE) {
04.
shadowAdjustScaleX =
0
.034f;
05.
shadowAdjustScaleY =
0
.12f;
06.
}
else
if
(orientation == Configuration.ORIENTATION_PORTRAIT) {
07.
shadowAdjustScaleX =
0
.06f;
08.
shadowAdjustScaleY =
0
.07f;
09.
}
10.
}
3.添加当前view
1.
viewDecor.addView(
this
,
0
);
4.设置view边距
01.
/**
02.
* we need the call the method before the menu show, because the
03.
* padding of activity can't get at the moment of onCreateView();
04.
*/
05.
private
void
setViewPadding(){
06.
this
.setPadding(viewActivity.getPaddingLeft(),
07.
viewActivity.getPaddingTop(),
08.
viewActivity.getPaddingRight(),
09.
viewActivity.getPaddingBottom());
10.
}
01.
private
Animator.AnimatorListener animationListener =
new
Animator.AnimatorListener() {
02.
@Override
03.
public
void
onAnimationStart(Animator animation) {
04.
if
(isOpened()){
05.
showScrollViewMenu();
06.
if
(menuListener !=
null
)
07.
menuListener.openMenu();
08.
}
09.
}
10.
11.
@Override
12.
public
void
onAnimationEnd(Animator animation) {
13.
// reset the view;
14.
if
(isOpened()){
15.
viewActivity.setTouchDisable(
true
);
16.
viewActivity.setOnClickListener(viewActivityOnClickListener);
17.
}
else
{
18.
viewActivity.setTouchDisable(
false
);
19.
viewActivity.setOnClickListener(
null
);
20.
hideScrollViewMenu();
21.
if
(menuListener !=
null
)
22.
menuListener.closeMenu();
23.
}
24.
}
25.
26.
@Override
27.
public
void
onAnimationCancel(Animator animation) {
28.
29.
}
30.
31.
@Override
32.
public
void
onAnimationRepeat(Animator animation) {
33.
34.
}
35.
};
01.
private
float
getTargetScale(
float
currentRawX){
02.
float
scaleFloatX = ((currentRawX - lastRawX) / getScreenWidth()) *
0
.75f;
03.
scaleFloatX = scaleDirection == DIRECTION_RIGHT ? - scaleFloatX : scaleFloatX;
04.
05.
float
targetScale = ViewHelper.getScaleX(viewActivity) - scaleFloatX;
06.
targetScale = targetScale >
1
.0f ?
1
.0f : targetScale;
07.
targetScale = targetScale <
0
.5f ?
0
.5f : targetScale;
08.
return
targetScale;
09.
}
默认缩放比例:
1.
//valid scale factor is between 0.0f and 1.0f.
2.
private
float
mScaleValue =
0
.5f;
1.
AnimatorSet scaleDown_activity = buildScaleDownAnimation(viewActivity, mScaleValue, mScaleValue);
01.
/**
02.
* a helper method to build scale down animation;
03.
*
04.
* @param target
05.
* @param targetScaleX
06.
* @param targetScaleY
07.
* @return
08.
*/
09.
private
AnimatorSet buildScaleDownAnimation(View target,
float
targetScaleX,
float
targetScaleY){
10.
11.
AnimatorSet scaleDown =
new
AnimatorSet();
12.
scaleDown.playTogether(
13.
ObjectAnimator.ofFloat(target,
"scaleX"
, targetScaleX),
14.
ObjectAnimator.ofFloat(target,
"scaleY"
, targetScaleY)
15.
);
16.
17.
scaleDown.setInterpolator(AnimationUtils.loadInterpolator(activity,
18.
android.R.anim.decelerate_interpolator));
19.
scaleDown.setDuration(
250
);
20.
return
scaleDown;
21.
}
01.
public
ResideMenuItem(Context context,
int
icon, String title) {
02.
super
(context);
03.
initViews(context);
04.
iv_icon.setImageResource(icon);
05.
tv_title.setText(title);
06.
}
07.
08.
private
void
initViews(Context context){
09.
LayoutInflater inflater=(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
10.
inflater.inflate(R.layout.residemenu_item,
this
);
11.
iv_icon = (ImageView) findViewById(R.id.iv_icon);
12.
tv_title = (TextView) findViewById(R.id.tv_title);
13.
}
01.
<?xml version=
"1.0"
encoding=
"utf-8"
?>
02.
03.
<LinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
04.
android:orientation=
"horizontal"
05.
android:layout_width=
"match_parent"
06.
android:layout_height=
"wrap_content"
07.
android:gravity=
"center_vertical"
08.
android:paddingTop=
"30dp"
>
09.
10.
<ImageView
11.
android:layout_width=
"30dp"
12.
android:layout_height=
"30dp"
13.
android:scaleType=
"centerCrop"
14.
android:id=
"@+id/iv_icon"
/>
15.
16.
<TextView
17.
android:layout_width=
"match_parent"
18.
android:layout_height=
"wrap_content"
19.
android:textColor=
"@android:color/white"
20.
android:textSize=
"18sp"
21.
android:layout_marginLeft=
"10dp"
22.
android:id=
"@+id/tv_title"
/>
23.
24.
</LinearLayout>
显示效果图:
01.
/**
02.
* add a single items;
03.
*
04.
* @param menuItem
05.
* @param direction
06.
*/
07.
public
void
addMenuItem(ResideMenuItem menuItem,
int
direction){
08.
if
(direction == DIRECTION_LEFT){
09.
this
.leftMenuItems.add(menuItem);
10.
layoutLeftMenu.addView(menuItem);
11.
}
else
{
12.
this
.rightMenuItems.add(menuItem);
13.
layoutRightMenu.addView(menuItem);
14.
}
15.
}
01.
/**
02.
* show the reside menu;
03.
*/
04.
public
void
openMenu(
int
direction){
05.
06.
setScaleDirection(direction);
07.
08.
isOpened =
true
;
09.
AnimatorSet scaleDown_activity = buildScaleDownAnimation(viewActivity, mScaleValue, mScaleValue);
10.
AnimatorSet scaleDown_shadow = buildScaleDownAnimation(imageViewShadow,
11.
mScaleValue + shadowAdjustScaleX, mScaleValue + shadowAdjustScaleY);
12.
AnimatorSet alpha_menu = buildMenuAnimation(scrollViewMenu,
1
.0f);
13.
scaleDown_shadow.addListener(animationListener);
14.
scaleDown_activity.playTogether(scaleDown_shadow);
15.
scaleDown_activity.playTogether(alpha_menu);
16.
scaleDown_activity.start();
17.
}
01.
private
void
setScaleDirection(
int
direction){
02.
03.
int
screenWidth = getScreenWidth();
04.
float
pivotX;
05.
float
pivotY = getScreenHeight() *
0
.5f;
06.
07.
if
(direction == DIRECTION_LEFT){
08.
scrollViewMenu = scrollViewLeftMenu;
09.
pivotX = screenWidth *
1
.5f;
10.
}
else
{
11.
scrollViewMenu = scrollViewRightMenu;
12.
pivotX = screenWidth * -
0
.5f;
13.
}
14.
15.
ViewHelper.setPivotX(viewActivity, pivotX);
16.
ViewHelper.setPivotY(viewActivity, pivotY);
17.
ViewHelper.setPivotX(imageViewShadow, pivotX);
18.
ViewHelper.setPivotY(imageViewShadow, pivotY);
19.
scaleDirection = direction;
20.
}
如果还不了解,dispatchTouchEvent这个函数如何调用?什么时候调用?请先看看http://blog.youkuaiyun.com/cym492224103/article/details/39179311
01.
@Override
02.
public
boolean
dispatchTouchEvent(MotionEvent ev) {
03.
float
currentActivityScaleX = ViewHelper.getScaleX(viewActivity);
04.
if
(currentActivityScaleX ==
1
.0f)
05.
setScaleDirectionByRawX(ev.getRawX());
06.
07.
switch
(ev.getAction()){
08.
case
MotionEvent.ACTION_DOWN:
09.
lastActionDownX = ev.getX();
10.
lastActionDownY = ev.getY();
11.
isInIgnoredView = isInIgnoredView(ev) && !isOpened();
12.
pressedState = PRESSED_DOWN;
13.
break
;
14.
15.
case
MotionEvent.ACTION_MOVE:
16.
if
(isInIgnoredView || isInDisableDirection(scaleDirection))
17.
break
;
18.
19.
if
(pressedState != PRESSED_DOWN &&
20.
pressedState != PRESSED_MOVE_HORIZANTAL)
21.
break
;
22.
23.
int
xOffset = (
int
) (ev.getX() - lastActionDownX);
24.
int
yOffset = (
int
) (ev.getY() - lastActionDownY);
25.
26.
if
(pressedState == PRESSED_DOWN) {
27.
if
(yOffset >
25
|| yOffset < -
25
) {
28.
pressedState = PRESSED_MOVE_VERTICAL;
29.
break
;
30.
}
31.
if
(xOffset < -
50
|| xOffset >
50
) {
32.
pressedState = PRESSED_MOVE_HORIZANTAL;
33.
ev.setAction(MotionEvent.ACTION_CANCEL);
34.
}
35.
}
else
if
(pressedState == PRESSED_MOVE_HORIZANTAL) {
36.
if
(currentActivityScaleX <
0.95
)
37.
showScrollViewMenu();
38.
39.
float
targetScale = getTargetScale(ev.getRawX());
40.
ViewHelper.setScaleX(viewActivity, targetScale);
41.
ViewHelper.setScaleY(viewActivity, targetScale);
42.
ViewHelper.setScaleX(imageViewShadow, targetScale + shadowAdjustScaleX);
43.
ViewHelper.setScaleY(imageViewShadow, targetScale + shadowAdjustScaleY);
44.
ViewHelper.setAlpha(scrollViewMenu, (
1
- targetScale) *
2
.0f);
45.
46.
lastRawX = ev.getRawX();
47.
return
true
;
48.
}
49.
50.
break
;
51.
52.
case
MotionEvent.ACTION_UP:
53.
54.
if
(isInIgnoredView)
break
;
55.
if
(pressedState != PRESSED_MOVE_HORIZANTAL)
break
;
56.
57.
pressedState = PRESSED_DONE;
58.
if
(isOpened()){
59.
if
(currentActivityScaleX >
0
.56f)
60.
closeMenu();
61.
else
62.
openMenu(scaleDirection);
63.
}
else
{
64.
if
(currentActivityScaleX <
0
.94f){
65.
openMenu(scaleDirection);
66.
}
else
{
67.
closeMenu();
68.
}
69.
}
70.
71.
break
;
72.
73.
}
74.
lastRawX = ev.getRawX();
75.
return
super
.dispatchTouchEvent(ev);
76.
}
MotionEvent.ACTION_DOWN:
记录了X,Y轴的坐标点,判断是否打开,设置了按下的状态为PRESSED_DOWN
MotionEvent.ACTION_MOVE:
拿到当前X,Y减去DOWN下记录下来的X,Y,这样得到了移动的X,Y,然后判断如果如果移动的X,Y大于25或者小于-25就改变按下状态为PRESSED_MOVE_VERTICAL
如果移动的X,Y大于50或者小于-50就改变状态为PRESSED_MOVE_HORIZANTAL
状态为PRESSED_MOVE_HORIZANTAL就改变菜单主视图内容以及阴影图片大小,在改变的同时还设置了当前菜单的透明度。
MotionEvent.ACTION_UP:
判断是否菜单是否打开状态,在获取当前缩放的X比例,
判断比例小于0.56f,则关闭菜单,反正开启菜单。
看完后,我们在回去看看代码,就会发现其实也不过如此~!
10.菜单关闭方法,同样也设置了动画监听之前的想法也是成立的。
01.
/**
02.
* close the reslide menu;
03.
*/
04.
public
void
closeMenu(){
05.
06.
isOpened =
false
;
07.
AnimatorSet scaleUp_activity = buildScaleUpAnimation(viewActivity,
1
.0f,
1
.0f);
08.
AnimatorSet scaleUp_shadow = buildScaleUpAnimation(imageViewShadow,
1
.0f,
1
.0f);
09.
AnimatorSet alpha_menu = buildMenuAnimation(scrollViewMenu,
0
.0f);
10.
scaleUp_activity.addListener(animationListener);
11.
scaleUp_activity.playTogether(scaleUp_shadow);
12.
scaleUp_activity.playTogether(alpha_menu);
13.
scaleUp_activity.start();
14.
}
1.
public
void
setSwipeDirectionDisable(
int
direction){
2.
disabledSwipeDirection.add(direction);
3.
}
1.
private
boolean
isInDisableDirection(
int
direction){
2.
return
disabledSwipeDirection.contains(direction);
3.
}
1.
if
(isInIgnoredView || isInDisableDirection(scaleDirection))
最后我们会发现我们一直都没说到TouchDisableView,其实initValue的时候就初始化了,它就是viewActivity,是我们的内容视图。
我们来看看它做了什么?
01.
@Override
02.
protected
void
onMeasure(
int
widthMeasureSpec,
int
heightMeasureSpec) {
03.
04.
int
width = getDefaultSize(
0
, widthMeasureSpec);
05.
int
height = getDefaultSize(
0
, heightMeasureSpec);
06.
setMeasuredDimension(width, height);
07.
08.
final
int
contentWidth = getChildMeasureSpec(widthMeasureSpec,
0
, width);
09.
final
int
contentHeight = getChildMeasureSpec(heightMeasureSpec,
0
, height);
10.
mContent.measure(contentWidth, contentHeight);
11.
}
12.
13.
@Override
14.
protected
void
onLayout(
boolean
changed,
int
l,
int
t,
int
r,
int
b) {
15.
final
int
width = r - l;
16.
final
int
height = b - t;
17.
mContent.layout(
0
,
0
, width, height);
18.
}
19.
20.
@Override
21.
public
boolean
onInterceptTouchEvent(MotionEvent ev) {
22.
return
mTouchDisabled;
23.
}
24.
25.
void
setTouchDisable(
boolean
disableTouch) {
26.
mTouchDisabled = disableTouch;
27.
}
28.
29.
boolean
isTouchDisabled() {
30.
return
mTouchDisabled;
31.
}