android左右侧滑菜单控件的简易实现

本文介绍了一种基于HorizontalScrollView的双向侧滑菜单实现方法。通过重写关键方法,实现了菜单的左右滑动显示及隐藏功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    转载请注明出处:http://blog.youkuaiyun.com/sctu_vroy/article/details/46947723

    最近由于项目需要实现类似手机QQ左右侧滑菜单的页面,在网上找到很多资料都是通过fragment来实现的,比较麻烦。偶然机会,在慕课网看到鸿洋大神的QQ5.0侧滑菜单实现,受到启发,将其实现单边侧滑的自定义控件重构成双向侧滑菜单控件。

    双向侧滑菜单实现的整体思路:

1)继承HorizontalScrollView

2)将左右菜单以及正文内容布局在SlidingMenu布局文件中

    <com.example.double_slidingmenu.SlidingMenu
        android:id="@+id/id_menu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/img_frame_background"
        android:scrollbars="none" >

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:orientation="horizontal" >
		<!-- 左菜单 -->
            <include layout="@layout/left_menu" />
		<!-- 正文内容 -->
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@drawable/qq" >
            </LinearLayout>
		<!-- 右菜单 -->
<span style="white-space:pre">		</span><include layout="@layout/left_menu"/> 
<span style="white-space: pre;">	</span></LinearLayout> 
    </com.example.double_slidingmenu.SlidingMenu>
 

3)SlidingMenu构造函数

<span style="white-space:pre">	</span>public SlidingMenu(Context context) {
		super(context, null);
	}

	public SlidingMenu(Context context, AttributeSet attrs) {
		super(context, attrs);
		
		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		DisplayMetrics metrics = new DisplayMetrics();
		wm.getDefaultDisplay().getMetrics(metrics);
		mScreenWidth = metrics.widthPixels;
		//dp转px
		mMenuPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, metrics);
	}
    布局文件中使用自定义控件时,会自动调用带属性attrs的构造函数(非自定义属性)。构造函数首先获取手机屏幕宽度mScreenWidth,并将菜单侧滑后保留正文内容区域宽度(50dp)转化为px单位mMenuPadding。

4)重写view的onMeasure()和onLayout()方法

<span style="white-space:pre">	</span>@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		if(!once) {
			mWapper = (LinearLayout) getChildAt(0);
			mLeftMenu = (ViewGroup) mWapper.getChildAt(0);
			mContent = (ViewGroup) mWapper.getChildAt(1);
			mRightMenu = (ViewGroup) mWapper.getChildAt(2);
			mMenuWidth = mLeftMenu.getLayoutParams().width = mRightMenu.getLayoutParams().width 
					= mScreenWidth - mMenuPadding;
			half = mMenuWidth / 2;
			mContent.getLayoutParams().width = mScreenWidth;
			once = true;
		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	}

	@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);
		}
	}
    onMeasure()方法在自定义控件中必须被重写,用于 测量view及其内容来确定view的宽度和高度。方法中实现对左右菜单以及正文内容部分的宽度设置。对于onLayout()方法,调用场景:在view给其孩子设置尺寸和位置时被调用;参数changed表示view尺寸发生变化,一旦changed为true,按一般习惯,应该把画面切回正文区域,此处调用ScrollView中的方法scrollTo(x,y)方法,一旦view有新的尺寸或位置时,隐藏左右菜单,只显示正文区域部分。

5)重写onTouchEvent()方法,实现隐藏、显示菜单功能

<span style="white-space:pre">	</span>@Override
	public boolean onTouchEvent(MotionEvent ev) {
		int action = ev.getAction();
		if(action == MotionEvent.ACTION_UP) {
			int left = getScrollX();
			if(left <= half) {
				//显示左菜单
				this.smoothScrollTo(0, 0);
			}
			else if(left > mMenuWidth + mScreenWidth / 2) {
				//显示右菜单
				this.smoothScrollTo(mScreenWidth + mMenuWidth - mMenuPadding, 0);
			}
			else {
				//隐藏左右菜单
				this.smoothScrollTo(mMenuWidth, 0);
			}
			return true;
		}
		return super.onTouchEvent(ev);
	}

    根据安卓源码,getScrollX()获取到的是HorizontalScrollView隐藏在左边部分的宽度值。获取手指按压抬起时的隐藏在view左边的宽度值,根据数值做判断,实现左右菜单显示和隐藏的效果。参数说明:half(菜单宽度的一半)。判断条件说明:以当前显示页面宽度一半为滑动阈值,显示左菜单较好理解,显示右菜单的条件是当getScrollX()大于已经隐藏的左菜单宽度+当前显示页面(正文)宽度一半。

6)重写ScrollView的onScrollChanged()方法,实现滑动动画和最终的隐藏效果

<span style="white-space:pre">	</span>/* 滚动发生时属性动画*/
	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		//l = getScrollX()
		/**
		 * 区别1:内容区域1.0~0.7 缩放的效果 scale : 1.0~0.0 0.7 + 0.3 * scale
		 * 
		 * 区别2:菜单的偏移量需要修改
		 * 
		 * 区别3:菜单的显示时有缩放以及透明度变化 缩放:0.7 ~1.0 1.0 - scale * 0.3 透明度 0.6 ~ 1.0 
		 * 0.6+ 0.4 * (1- scale) ;
		 * 
		 */
		if(l <= mMenuWidth ) {
			isLeft = true;
		}
		else if(l > mMenuWidth) {
			isLeft = false;
		}
		
		float lScale = l * 1.0f / mMenuWidth; //1.0 ~ 0.0
		float rScale = (mScreenWidth + mMenuWidth - mMenuPadding - l) * 1.0f / mMenuWidth;
		
		float rightScale = 0.7f + 0.3f * lScale;
		float leftScale = 1.0f - lScale * 0.3f;
		float leftAlpha = 0.6f + 0.4f * (1 - lScale);
		
		float rightScale_ = 0.7f + 0.3f * rScale;
		float leftScale_ = 1.0f - rScale * 0.3f;
		float leftAlpha_ = 0.6f + 0.4f * (1 - rScale);
		
		// 调用属性动画,设置TranslationX
		if(isLeft) {
			ViewHelper.setTranslationX(mLeftMenu, mMenuWidth * lScale * 0.7f);
			
			ViewHelper.setScaleX(mLeftMenu, leftScale);
			ViewHelper.setScaleY(mLeftMenu, leftScale);
			ViewHelper.setAlpha(mLeftMenu, leftAlpha);
			
			// 设置content的缩放的中心点
			ViewHelper.setPivotX(mContent, 0);
			ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
			
			ViewHelper.setScaleX(mContent, rightScale);
			ViewHelper.setScaleY(mContent, rightScale);
		}
		else {
			ViewHelper.setTranslationX(mRightMenu, mMenuWidth * rScale * 0.7f);
			
			ViewHelper.setScaleX(mRightMenu, leftScale_);
			ViewHelper.setScaleY(mRightMenu, leftScale_);
			ViewHelper.setAlpha(mRightMenu, leftAlpha_);
			
			// 设置content的缩放的中心点
			ViewHelper.setPivotX(mContent, mContent.getWidth());
			ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
			
			ViewHelper.setScaleX(mContent, rightScale_);
			ViewHelper.setScaleY(mContent, rightScale_);
		}
	}
    为了兼容Android3.0以下版本,此处属性动画调用nineoldandroids-2.4.0的jar包,实现动画效果与Android3.0以上一样。此处通过对比getSrollX()和mMenuWidth的大小,判断当前滑动操作是向哪个方向。需要注意的是:

    (1)左滑和右滑的缩放比例的实现不一致,lScale逻辑较简单,rScale的逻辑(由于mMenuPadding+mMenuWidth=mScreenWidth,一开始getScrollX(),也即l=mMenuWidth,则mScreenWidth-mMenuPadding+mMenuWidth=2个mMenuWidth=2个l,所以初值为1.0;随着手指向左滑动,getScrollX()越来越大,则差值越来越接近于0);

    (2)设置content的缩放中心点需要注意,显示左菜单时中心点须设置在content的左纵边中心点;显示右菜单时则必须设置在content的右纵边中心点,否则默认缩放以content区域中心为中心点,达不到我们需要的效果。

    最终效果:

最后感谢鸿洋大神视频的启发!!!

源码下载





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值