为扩展屏幕尺寸所带来的局限性,很多APP都有侧滑菜单。所谓的侧滑菜单就是将一些菜单选项隐藏起来,而不是放置在主屏幕上,然后可以通过滑动的方式将菜单显示出来。
本文将通过android提供的HorizontalScrollView来实现一个仿QQ5.0侧滑菜单的案例,通过自定义控件和属性动画来实现侧滑效果。
1. 最终效果图
2. 布局文件
left_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/call"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="160dp"
android:drawableLeft="@drawable/nav_call"
android:drawablePadding="20dp"
android:paddingLeft="20dp"
android:text="Call"
android:gravity="center_vertical"
android:background="@null"/>
<Button
android:id="@+id/friends"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/nav_friends"
android:drawablePadding="20dp"
android:text="Friends"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:background="@null"/>
<Button
android:id="@+id/location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/nav_location"
android:drawablePadding="20dp"
android:text="Location"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:background="@null"/>
<Button
android:id="@+id/mail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/nav_mail"
android:drawablePadding="20dp"
android:text="Mail"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:background="@null"/>
<Button
android:id="@+id/tasks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/nav_task"
android:drawablePadding="20dp"
android:text="Tasks"
android:gravity="center_vertical"
android:paddingLeft="20dp"
android:background="@null"/>
</LinearLayout>
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:yxman="http://schemas.android.com/apk/res/com.yx.slidingmenu"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.yx.slidingmenu.view.SlidingMenu
android:id="@+id/id_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/sliding_menu_background"
yxman:rightPadding="80dp" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal" >
<include layout="@layout/left_menu" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/home_background_image"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#13b7f6" >
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ic_menu"
android:onClick="toggleMenu" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</com.yx.slidingmenu.view.SlidingMenu>
</RelativeLayout>
首先自定义一个用于显示侧滑菜单的left_menu.xml布局文件,然后在activity_main.xml主布局文件中通过一个水平的LinearLayout将子布局文件加载进来,以及将主布局文件编写在其中。
3. 添加自定义属性
a. 首先在values下新建attr.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="rightPadding" format="dimension"></attr>
<declare-styleable name="SlidingMenu">
<attr name="rightPadding"></attr>
</declare-styleable>
</resources>
b. 在主布局activity_main.xml中声明命名空间和使用自定的属性:
<com.yx.slidingmenu.view.SlidingMenu
android:id="@+id/id_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/sliding_menu_background"
yxman:rightPadding="80dp" >
......
</com.yx.slidingmenu.view.SlidingMenu>
我的命名空间是xmlns:yxman="http://schemas.android.com/apk/res/com.yx.slidingmenu"。格式是:http://schemas.android.com/apk/res/后面再加上项目的包名。
属性yxman:rightPadding="80dp"右侧内边距设置为80dp。
4. 自定义SlidingMenu
SlidingMenu.java
package com.yx.slidingmenu.view;
import com.nineoldandroids.view.ViewHelper;
import com.yx.slidingmenu.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
public class SlidingMenu extends HorizontalScrollView {
private LinearLayout mWapper;
private ViewGroup mMenu;
private ViewGroup mContent;
private int mScreenWidth;
private int mMenuWidth;
// 单位是dp
private int mMenuRightPadding = 50;
private boolean flag;
private boolean isOpen;
public SlidingMenu(Context context) {
this(context, null);
}
/**
* 未使用自定义属性时调用
*
* @param context
* @param attrs
*/
public SlidingMenu(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 当使用了自定义属性时会调用此构造方法
*
* @param context
* @param attrs
* @param defStyle
*/
public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// 获取自定义定义的属性
TypedArray attribute = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyle, 0);
int n = attribute.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = attribute.getIndex(i);
switch (attr) {
case R.styleable.SlidingMenu_rightPadding:
int defValue = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics());
mMenuRightPadding = attribute.getDimensionPixelSize(attr, defValue);
break;
}
}
attribute.recycle();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mScreenWidth = outMetrics.widthPixels;
}
/**
* 设置子View的宽和高,设置自己的宽和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!flag) {
mWapper = (LinearLayout) getChildAt(0);
mMenu = (ViewGroup) mWapper.getChildAt(0);
mContent = (ViewGroup) mWapper.getChildAt(1);
mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding;
mContent.getLayoutParams().width = mScreenWidth;
flag = true;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 通过设置偏移量,将menu隐藏
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed) {
this.scrollTo(mMenuWidth, 0);
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_UP:
// 隐藏在左边的宽度
int scrollX = getScrollX();
if (scrollX >= mMenuWidth / 2) {
this.smoothScrollTo(mMenuWidth, 0);
isOpen = false;
} else {
this.smoothScrollTo(0, 0);
isOpen = true;
}
return true;
}
return super.onTouchEvent(ev);
}
/**
* 切换菜单
*/
public void toggle() {
if (isOpen) {
closeMenu();
} else {
openMenu();
}
}
/**
* 打开菜单
*/
public void openMenu() {
if (isOpen)
return;
this.smoothScrollTo(0, 0);
isOpen = true;
}
/**
* 关闭菜单
*/
public void closeMenu() {
if (!isOpen)
return;
this.smoothScrollTo(mMenuWidth, 0);
isOpen = false;
}
/**
* 滚动发生时
*/
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
// scale取值范围:0~1
float scale = l * 1.0f / mMenuWidth;
/**
* 1. 内容区域0.7~1.0 缩放的效果 scale: (0.7 + 0.3 * scale)
* 2. 菜单的偏移量需要修改
* 3. 菜单的显示时有缩放以及透明度变化:
* 缩放: 0.7 ~1.0 -> 1.0 - scale * 0.3
* 透明度: 0.6 ~ 1.0 -> 1.0 - scale * 0.4
*/
float rightScale = 0.7f + 0.3f * scale;
float leftScale = 1.0f - scale * 0.3f;
float leftAlpha = 1.0f - scale * 0.4f;
// 调用属性动画,设置TranslationX
ViewHelper.setTranslationX(mMenu, mMenuWidth * scale * 0.8f);
ViewHelper.setScaleX(mMenu, leftScale);
ViewHelper.setScaleY(mMenu, leftScale);
ViewHelper.setAlpha(mMenu, leftAlpha);
// 设置content的缩放的中心点
ViewHelper.setPivotX(mContent, 0);
ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
ViewHelper.setScaleX(mContent, rightScale);
ViewHelper.setScaleY(mContent, rightScale);
}
}
以上代码看着很复杂,但都有详细的注释,主要是用于处理滑动事件,不过多解释。
5. MainActivity.java
package com.yx.slidingmenu;
import com.yx.slidingmenu.view.SlidingMenu;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
private SlidingMenu mLeftMenu;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLeftMenu = (SlidingMenu) findViewById(R.id.id_menu);
}
public void toggleMenu(View view) {
mLeftMenu.toggle();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
至此,核心代码和主要演示过程以完毕。是不是感觉很炫酷,就是这么简单。如有疑问下载源码自行研究。