标签:
今天我们来继承 HorizontalScrollView 实现比较炫酷的侧滑菜单效果
继承HorizontalScrollView的好处有两点:
1、不用在写 MOVE 事件
2、不用解决 和 ListView 的冲突
以下是实现步骤:
1、onMeasure 决定内部 View (子 View) 的宽和高,以及呢,自己的宽和高
/** * 设置 自己的宽和高 子 View 的宽和高 * * @param widthMeasureSpec * @param heightMeasureSpec */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (!once) { mWapper = (LinearLayout) getChildAt(0); mMenu = (ViewGroup) mWapper.getChildAt(0); mContent = (ViewGroup) mWapper.getChildAt(1); // 子 View 的宽和高 mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding; mMenuWidth = mMenu.getLayoutParams().width; mContent.getLayoutParams().width = mScreenWidth; once = true; } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
2、onLayout 决定子 View 的放置位置
/** * 通过设置 偏移量 将 Menu 隐藏 * * @param changed * @param l * @param t * @param r * @param b */ @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); isOpen = false ; } }
3、onTouchEvent 决定菜单是否打开
@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); }
允许用户设置菜单离屏幕右侧的边距
1、书写 xml 文件 values/attr.xml
<attr name="rightPadding" format="dimension"></attr> <declare-styleable name="SlidingMenu"> <attr name="rightPadding"/> </declare-styleable>
2、在布局文件中进行使用,特别注意 xmlns
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:negro="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity" android:background="@drawable/img_frame_background"> <com.negro.qq_50_slidingmenu.view.SlidingMenu android:id="@+id/id_menu" android:layout_width="match_parent" android:layout_height="match_parent" negro:rightPadding="70dp"> <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/qq"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="切换菜单" android:onClick="toggleMenu"/> </LinearLayout> </LinearLayout> </com.negro.qq_50_slidingmenu.view.SlidingMenu> </RelativeLayout>
3、在构造方法中(3个参数的构造方法)获得我们设置的值
/** * 当使用了自定义的属性时,会用此构造方法 * @param context * @param attrs * @param defStyleAttr */ public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrice = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrice); // 计算屏幕的宽度 mScreenWidth = outMetrice.widthPixels; // 菜单的右边距为 整个屏幕宽的三分之一 mMenuRightPadding = mScreenWidth / 3 ; // 获取我们定义的属性 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyleAttr, 0) ; int indexCount = a.getIndexCount(); for(int i = 0; i < indexCount; i ++) { int attr = a.getIndex(i) ; switch (attr) { case R.styleable.SlidingMenu_rightPadding: mMenuRightPadding = a.getDimensionPixelSize(attr, mMenuRightPadding) ; break ; } } a.recycle(); }
------------------------------------------
区别:菜单仿佛在内容区域底下
实现原理:
当 getScrollX 为 0 的时候,左侧菜单的偏移量为 0 (也就是菜单完全的显示出来)
当 getScrollX 为 mMenuWidth 的时候,左侧菜单的偏移量为 mMenuWidth (也就是菜单隐藏的时候,菜单的位置应该在内容区的下面)
偏移量可以用属性动画 TraslationX 来实现。
设置调用动画的时机: onScrollChanged
---------------------------------
设置一个变量,当菜单完全隐藏的时候,为0 当菜单完全显示出来的时候,为1
这个变量为 scale 计算方法为: (mMenuWidth - getScrollX) / mMenuWidth ;
当菜单完全隐藏的时候,getScrollX 为 mMenuWidth , 所以 scale 为 0
当菜单完全显示出来的时候,getScrollX 为 0 , 所以 scale 为 1
菜单从不显示到显示时 scale 的变化趋势: 0.0~1.0
现在在现实菜单的时候需要实现以下几个动画效果:
一、内容区域 缩放效果(内容区域的缩放需要注意 设置缩放的中心点位置为 左侧的中点位置)
缩放效果:1.0~0.7 计算方法 1.0 - 0.3 * scale
二、菜单的偏移量需要修改
偏移量:0.7~0.0 * mMenuWidth 计算方法 (0.7 - 0.7 * scale)* mMenuWidth
三、菜单的显示时有缩放,以及透明度的变化
缩放:0.7~1.0 计算方法 0.7 + 0.3 * scale
透明度:0.6~1.0 计算方法 0.6 + 0.4 * scale
实现代码如下:
/** * 滚动发生的时候,会调用此方法 * @param l * @param t * @param oldl * @param oldt */ @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); float scale = (mMenuWidth - l) * 1.0f / mMenuWidth ; float rightScale = 1.0f - 0.3f * scale ; float leftScale = 0.7f + 0.3f * scale ; float leftTrans = (0.7f - 0.7f * scale) * mMenuWidth ; float leftAlpha = 0.6f + 0.4f * scale ; ViewHelper.setTranslationX(mMenu, leftTrans); ViewHelper.setScaleX(mMenu, leftScale); ViewHelper.setScaleY(mMenu, leftScale); // 设置内容区域的缩放中心点 ViewHelper.setPivotX(mContent, 0); ViewHelper.setPivotY(mContent, mContent.getHeight() / 2); ViewHelper.setScaleX(mContent, rightScale); ViewHelper.setScaleY(mContent, rightScale); ViewHelper.setAlpha(mMenu, leftAlpha); }
然后再写几个操控菜单打开或者关闭的功能:
/** * 打开菜单 */ public void openMenu() { if(isOpen) return ; this.smoothScrollTo(0, 0); isOpen = true ; } /** * 关闭菜单 */ public void closeMenu() { if(!isOpen) return ; this.smoothScrollTo(mMenuWidth, 0); isOpen = false ; } /** * 切换菜单 */ public void toggle() { if(isOpen) { closeMenu(); } else { openMenu(); } } }
然后就大功告成了!
想直接运行代码卡效果的童鞋,可以点这里下载源代码
------------------------------------
一、自定义ViewGroup
1、构造方法的选择,获得一些需要用到的值
2、onMeasure 计算子 View 的宽和高,以及设置自己的宽和高
3、onLayout 决定子 View 的布局的位置
4、onTouchEvent
二、构造方法
1、context 用户在代码中 new 的时候调用的方法
2、context, attr 布局文件中声明(没有用到自定义属性时,调用此方法)
3、context,attr,defStyle (有用到自定义属性时调用此方法)
三、自定义属性
1、attr.xml
2、布局文件中 xmlns
3、在三个参数的构造方法中,获得我们自定义属性的值
四、属性动画
Android 3.0才有
向下兼容,请用 nineoldanimation.jar
标签:
原文地址:http://www.cnblogs.com/niulinguo/p/4697349.html