Android之自定义侧滑菜单
先来上图:
我们把主界面从左向右拉动,可以看到地下有一层菜单页,从透明渐渐变得不透明,从小渐渐变大,感觉上觉得菜单页是从屏幕外面被拉到屏幕中的。下面的代码实现这个DEMO:
首先是自定义控件SlidingMenu控件的代码:
1 public class SlidingMenu extends HorizontalScrollView { 2 // 自定义View的步骤: 3 // 1、onMeasure():决定子View的宽和高和自己的宽和高 4 // 2、onLayout():决定子View放置的位置 5 // 3、onTouchEvent:判断用户手指的滑动状态 6 // 自定义属性的步骤: 7 // 1、书写XML文件(values/attrs.xml) 8 // 2、在布局文件中进行使用,注意xmlns命名空间 9 // 3、在三个参数的构造方法中获得我们设置的值 10 11 private LinearLayout wrapper; // 总容器 12 private ViewGroup menu, content; // 菜单页,内容页 13 private int screenWidth; // 屏幕的宽度 14 private int menuWidth; // menu的宽度 15 private int menuRightPadding = 50; // menu菜单距离屏幕右侧的距离(单位是DIP) 16 private boolean once; // onMeasure()方法是不是第一次调用 17 private boolean isOpen; // 侧滑菜单是否是开启状态 18 19 // 在界面上通过上下文直接生成控件时,调用这个构造方法 20 public SlidingMenu(Context context) { 21 this(context, null); 22 } 23 24 // 当没有使用自定义属性时,调用这个构造方法 25 public SlidingMenu(Context context, AttributeSet attrs) { 26 this(context, attrs, 0); 27 } 28 29 // 当使用了自定义属性(values/attrs.xml)时,调用这个构造方法 30 public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { 31 super(context, attrs, defStyleAttr); 32 // 获取我们自定义的属性 33 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyleAttr, 0); 34 for (int i = 0; i < a.getIndexCount(); i++) { 35 int attr = a.getIndex(i); 36 switch (attr) { 37 case R.styleable.SlidingMenu_rightPadding: 38 // getDimensionPixelSize:为attr下标的属性设置默认值(第二个参数) 39 menuRightPadding = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40 50, context.getResources().getDisplayMetrics())); 41 break; 42 } 43 } 44 a.recycle(); 45 46 // 获取屏幕的宽度 47 WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 48 DisplayMetrics metrics = new DisplayMetrics(); 49 manager.getDefaultDisplay().getMetrics(metrics); 50 screenWidth = metrics.widthPixels; 51 } 52 53 @Override 54 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 55 if (!once) { 56 wrapper = (LinearLayout) getChildAt(0); 57 menu = (ViewGroup) wrapper.getChildAt(0); 58 content = (ViewGroup) wrapper.getChildAt(1); 59 // 设置菜单和主页的宽度 60 menuWidth = menu.getLayoutParams().width = screenWidth - menuRightPadding; 61 content.getLayoutParams().width = screenWidth; 62 63 once = true; 64 } 65 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 66 } 67 68 // 通过设置偏移量,将Menu隐藏 69 @Override 70 protected void onLayout(boolean changed, int l, int t, int r, int b) { 71 super.onLayout(changed, l, t, r, b); 72 if (changed) { 73 this.scrollTo(menuWidth, 0); 74 } 75 } 76 77 @Override 78 public boolean onTouchEvent(MotionEvent ev) { 79 int action = ev.getAction(); 80 switch (action) { 81 case MotionEvent.ACTION_UP: 82 int scrollX = getScrollX(); // 隐藏在左边的宽度 83 if (scrollX >= menuWidth / 2) { 84 this.smoothScrollTo(menuWidth, 0); 85 isOpen = false; 86 } else { 87 this.smoothScrollTo(0, 0); 88 isOpen = true; 89 } 90 return true; 91 } 92 return super.onTouchEvent(ev); 93 } 94 95 // 打开菜单 96 public void openMenu() { 97 if (isOpen) 98 return; 99 this.smoothScrollTo(0, 0); 100 isOpen = false; 101 } 102 103 // 关闭菜单 104 public void closeMenu() { 105 if (!isOpen) 106 return; 107 this.smoothScrollTo(menuWidth, 0); 108 isOpen = true; 109 } 110 111 // 管理菜单的状态和动作(如果菜单是关闭的就打开它,如果是打开的就关闭它) 112 public void toggle() { 113 if (isOpen) { 114 closeMenu(); 115 } else { 116 openMenu(); 117 } 118 } 119 120 // 抽屉式侧滑(这个方法监听滚动的全过程) 121 @Override 122 protected void onScrollChanged(int l, int t, int oldl, int oldt) { 123 super.onScrollChanged(l, t, oldl, oldt); 124 float scale = l * 1.0f / menuWidth; 125 // 实现仿QQ5.0的侧滑界面:在滑动时,滑动朝向的ViewGroup不断缩小,另一个ViewGroup不断放大 126 float rightScrollScale = 0.85f + 0.15f * scale; // 主界面的滑动缩放比例 127 float leftScrollScale = 1.0f - 0.4f * scale; // 菜单的滑动缩放比例 128 float leftAlphaScale = 0.6f + 0.4f * (1 - scale); // 菜单透明度的变化比例 129 // 调用属性动画(Android3.0时引入),设置TranslationX(这里需要引入nineoldandroids.jar包) 130 ViewHelper.setTranslationX(menu, menuWidth * scale * 0.75f); 131 // 设置Menu的缩放和透明度 132 ViewHelper.setScaleX(menu, leftScrollScale); 133 ViewHelper.setScaleY(menu, leftScrollScale); 134 ViewHelper.setAlpha(menu, leftAlphaScale); 135 // 设置Content的缩放 136 ViewHelper.setPivotX(content, 0); 137 ViewHelper.setPivotY(content, content.getHeight() / 2); 138 ViewHelper.setScaleX(content, rightScrollScale); 139 ViewHelper.setScaleY(content, rightScrollScale); 140 } 141 }
下面是主界面布局的代码:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 xmlns:xgz="http://schemas.android.com/apk/res/com.activity" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:background="@drawable/menu_bg" > 7 8 <com.view.SlidingMenu 9 android:id="@+id/main_slidingmenu" 10 android:layout_width="match_parent" 11 android:layout_height="match_parent" 12 xgz:rightPadding="75.0dip" > 13 14 <LinearLayout 15 android:layout_width="wrap_content" 16 android:layout_height="match_parent" 17 android:orientation="horizontal" > 18 19 <include layout="@layout/sideworks_menu" /> 20 21 <LinearLayout 22 android:layout_width="match_parent" 23 android:layout_height="match_parent" 24 android:background="@drawable/main_bg" > 25 26 <Button 27 android:id="@+id/main_togglebtn" 28 android:layout_width="wrap_content" 29 android:layout_height="30.0dip" 30 android:layout_marginLeft="10.0dip" 31 android:layout_marginTop="10.0dip" 32 android:background="#00000000" 33 android:text="@string/main_toggle_btn" 34 android:textColor="#ffffff" 35 android:textSize="16.0sp" /> 36 </LinearLayout> 37 </LinearLayout> 38 </com.view.SlidingMenu> 39 40 </RelativeLayout>
下面是主界面中引用的菜单页的布局代码:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:background="#00000000" 6 android:gravity="center_vertical" 7 android:orientation="vertical" > 8 9 <RelativeLayout 10 android:layout_width="match_parent" 11 android:layout_height="wrap_content" > 12 13 <ImageView 14 android:id="@+id/menu_icon1" 15 android:layout_width="40.0dip" 16 android:layout_height="40.0dip" 17 android:layout_marginLeft="10.0dip" 18 android:contentDescription="@string/app_name" 19 android:src="@drawable/img_1" /> 20 21 <TextView 22 android:layout_width="wrap_content" 23 android:layout_height="wrap_content" 24 android:layout_centerVertical="true" 25 android:layout_marginLeft="10.0dip" 26 android:layout_toRightOf="@id/menu_icon1" 27 android:text="@string/menu_icon1_text" 28 android:textColor="#ffffff" 29 android:textSize="15.0sp" /> 30 </RelativeLayout> 31 32 <RelativeLayout 33 android:layout_width="match_parent" 34 android:layout_height="wrap_content" 35 android:layout_marginTop="15.0dip" > 36 37 <ImageView 38 android:id="@+id/menu_icon2" 39 android:layout_width="40.0dip" 40 android:layout_height="40.0dip" 41 android:layout_marginLeft="10.0dip" 42 android:contentDescription="@string/app_name" 43 android:src="@drawable/img_2" /> 44 45 <TextView 46 android:layout_width="wrap_content" 47 android:layout_height="wrap_content" 48 android:layout_centerVertical="true" 49 android:layout_marginLeft="10.0dip" 50 android:layout_toRightOf="@id/menu_icon2" 51 android:text="@string/menu_icon2_text" 52 android:textColor="#ffffff" 53 android:textSize="15.0sp" /> 54 </RelativeLayout> 55 56 <RelativeLayout 57 android:layout_width="match_parent" 58 android:layout_height="wrap_content" 59 android:layout_marginTop="15.0dip" > 60 61 <ImageView 62 android:id="@+id/menu_icon3" 63 android:layout_width="40.0dip" 64 android:layout_height="40.0dip" 65 android:layout_marginLeft="10.0dip" 66 android:contentDescription="@string/app_name" 67 android:src="@drawable/img_3" /> 68 69 <TextView 70 android:layout_width="wrap_content" 71 android:layout_height="wrap_content" 72 android:layout_centerVertical="true" 73 android:layout_marginLeft="10.0dip" 74 android:layout_toRightOf="@id/menu_icon3" 75 android:text="@string/menu_icon3_text" 76 android:textColor="#ffffff" 77 android:textSize="15.0sp" /> 78 </RelativeLayout> 79 80 <RelativeLayout 81 android:layout_width="match_parent" 82 android:layout_height="wrap_content" 83 android:layout_marginTop="15.0dip" > 84 85 <ImageView 86 android:id="@+id/menu_icon4" 87 android:layout_width="40.0dip" 88 android:layout_height="40.0dip" 89 android:layout_marginLeft="10.0dip" 90 android:contentDescription="@string/app_name" 91 android:src="@drawable/img_4" /> 92 93 <TextView 94 android:layout_width="wrap_content" 95 android:layout_height="wrap_content" 96 android:layout_centerVertical="true" 97 android:layout_marginLeft="10.0dip" 98 android:layout_toRightOf="@id/menu_icon4" 99 android:text="@string/menu_icon4_text" 100 android:textColor="#ffffff" 101 android:textSize="15.0sp" /> 102 </RelativeLayout> 103 104 <RelativeLayout 105 android:layout_width="match_parent" 106 android:layout_height="wrap_content" 107 android:layout_marginTop="15.0dip" > 108 109 <ImageView 110 android:id="@+id/menu_icon5" 111 android:layout_width="40.0dip" 112 android:layout_height="40.0dip" 113 android:layout_marginLeft="10.0dip" 114 android:contentDescription="@string/app_name" 115 android:src="@drawable/img_5" /> 116 117 <TextView 118 android:layout_width="wrap_content" 119 android:layout_height="wrap_content" 120 android:layout_centerVertical="true" 121 android:layout_marginLeft="10.0dip" 122 android:layout_toRightOf="@id/menu_icon5" 123 android:text="@string/menu_icon5_text" 124 android:textColor="#ffffff" 125 android:textSize="15.0sp" /> 126 </RelativeLayout> 127 128 </LinearLayout>
下面是自定义属性attrs.xml文件中的代码:
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 4 <!-- 自定义属性:菜单离屏幕右侧的间距 --> 5 <attr name="rightPadding" format="dimension"></attr> 6 7 <declare-styleable name="SlidingMenu"> 8 <attr name="rightPadding"></attr> 9 </declare-styleable> 10 11 </resources>
下面是主界面MainActivity.java中的代码:
1 public class MainActivity extends Activity { 2 private SlidingMenu slidingMenu; 3 private Button toggleBtn; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 slidingMenu = (SlidingMenu) findViewById(R.id.main_slidingmenu); 10 toggleBtn = (Button) findViewById(R.id.main_togglebtn); 11 12 toggleBtn.setOnClickListener(new OnClickListener() { 13 @Override 14 public void onClick(View v) { 15 slidingMenu.toggle(); 16 } 17 }); 18 } 19 }