文档章节

Android 高仿 QQ5.0 侧滑菜单效果 HorizontalScrollView

欧吧在线
 欧吧在线
发布于 2015/06/10 14:28
字数 2050
阅读 128
收藏 3

上一篇博客带大家实现了:Android 自定义控件打造史上最简单的侧滑菜单 ,有兄弟看了以后说,你这滑动菜单过时了呀~QQ5.0的效果还不错~~嗯,的确,上一篇也承诺过,稍微修改上一篇的代码,实现QQ5.0侧滑菜单~~好了,下面就开始为大家展示写一个类QQ的侧滑有多easy ~!

1、原理分析

首先对比一下我们上篇的实现距离QQ的效果还有多远:

差距还是蛮大的

区别1、QQ的内容区域会伴随菜单的出现而缩小

区别2、QQ的侧滑菜单给人的感觉是隐藏在内容的后面,而不是拖出来的感觉

区别3、QQ的侧滑菜单有一个缩放以及透明度的效果~


那么我们如何能做到呢:

对于区别1:这个好办,我们可以在滑动的时候,不断的改变内容区域的大小;如何改变呢?我们在菜单出现的整个过程中,不断记录菜单显示的宽度与其总宽度的比值,是个从0到1的过程,然后把0~1转化为1~0.7(假设内容区域缩小至0.7);不断去缩小内容区域;

对于区别3:也比较好办,上面已经可以得到0到1的这个值了,那么缩放和透明度的动画就不在话下了;

对 于区别2:我们使用的HorizontalScrollView,然后水平放置了菜单和内容,如何让菜单可以隐藏到内容的后面呢?其实也比较简单,在菜单 出现的过程中,不断设置菜单的x方向的偏移量;0的时候完全隐藏,0.3的时候,隐藏x方向偏移量为0.7个宽度,类推~~~


好了,分析完毕,那么对于这些动画用什么实现最好呢?

想都不用想,属性动画哈,如果你对属性动画不了解,可以参:Android 属性动画(Property Animation) 完全解析 (上)Android 属性动画(Property Animation) 完全解析 (下)

2、实现


1、初步的代码

布局文件神马的,都和上一篇一模一样,这里就不重复贴代码了,不了解的,先看下上一篇;

先看一下上一篇我们已经实现的完整代码:

package com.example.zhy_slidingmenu;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import com.zhy.utils.ScreenUtils;

public class SlidingMenu extends HorizontalScrollView
{
	/**
	 * 屏幕宽度
	 */
	private int mScreenWidth;
	/**
	 * dp
	 */
	private int mMenuRightPadding;
	/**
	 * 菜单的宽度
	 */
	private int mMenuWidth;
	private int mHalfMenuWidth;

	private boolean isOpen;

	private boolean once;

	public SlidingMenu(Context context, AttributeSet attrs)
	{
		this(context, attrs, 0);

	}

	public SlidingMenu(Context context, AttributeSet attrs, int defStyle)
	{
		super(context, attrs, defStyle);
		mScreenWidth = ScreenUtils.getScreenWidth(context);

		TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
				R.styleable.SlidingMenu, defStyle, 0);
		int n = a.getIndexCount();
		for (int i = 0; i < n; i++)
		{
			int attr = a.getIndex(i);
			switch (attr)
			{
			case R.styleable.SlidingMenu_rightPadding:
				// 默认50
				mMenuRightPadding = a.getDimensionPixelSize(attr,
						(int) TypedValue.applyDimension(
								TypedValue.COMPLEX_UNIT_DIP, 50f,
								getResources().getDisplayMetrics()));// 默认为10DP
				break;
			}
		}
		a.recycle();
	}

	public SlidingMenu(Context context)
	{
		this(context, null, 0);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		/**
		 * 显示的设置一个宽度
		 */
		if (!once)
		{
			LinearLayout wrapper = (LinearLayout) getChildAt(0);
			ViewGroup menu = (ViewGroup) wrapper.getChildAt(0);
			ViewGroup content = (ViewGroup) wrapper.getChildAt(1);

			mMenuWidth = mScreenWidth - mMenuRightPadding;
			mHalfMenuWidth = mMenuWidth / 2;
			menu.getLayoutParams().width = mMenuWidth;
			content.getLayoutParams().width = mScreenWidth;

		}
		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);
			once = true;
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev)
	{
		int action = ev.getAction();
		switch (action)
		{
		// Up时,进行判断,如果显示区域大于菜单宽度一半则完全显示,否则隐藏
		case MotionEvent.ACTION_UP:
			int scrollX = getScrollX();
			if (scrollX > mHalfMenuWidth)
			{
				this.smoothScrollTo(mMenuWidth, 0);
				isOpen = false;
			} else
			{
				this.smoothScrollTo(0, 0);
				isOpen = true;
			}
			return true;
		}
		return super.onTouchEvent(ev);
	}

	/**
	 * 打开菜单
	 */
	public void openMenu()
	{
		if (isOpen)
			return;
		this.smoothScrollTo(0, 0);
		isOpen = true;
	}

	/**
	 * 关闭菜单
	 */
	public void closeMenu()
	{
		if (isOpen)
		{
			this.smoothScrollTo(mMenuWidth, 0);
			isOpen = false;
		}
	}

	/**
	 * 切换菜单状态
	 */
	public void toggle()
	{
		if (isOpen)
		{
			closeMenu();
		} else
		{
			openMenu();
		}
	}
	
	

}


利用HorizontalScrollView,监听了ACTION_UP的事件,当用户抬起手指时,根据当前菜单显示的宽度值,判断是缩回还是完全展开;给用户提供了一个rightPadding属性,用于设置菜单离屏幕的距离;以及对外提供了打开,关闭,切换的几个方法;具体的讲解看下上篇博客了;

2、实现的思路

现在我们开始解决那3个区别,已经选择了使用属性动画,现在决定动画效果应该加在哪儿?

不用说,我用大腿想一想都应该是在ACTION_MOVE中,是的,ACTION_MOVE中的确可以,不断获取当前的getScrollX / mMenuWidth,不断改变菜单的透明度,缩放,X方向的偏移量;不断改变内容区域的宽度和高度;

说一下,起初我也是在MOVE中这么做的,但是呢,出现两个问题:

1、动画效果并不是很流畅,特别是菜单,有抖动的效果;

2、用户抬起后,还需要在UP里面,继续未完成的动画,就是说,你的透明度、缩放神马的,当用户抬起以后就需要自动变化了;

于是乎,我就开始换了个方向,既然是SrollView,肯定有一个ScrollChanged方法,功夫不负有心人,真心这么个方法:

@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt)
	{
		
	}

这个方法只要scrollChanged就会触发,l就是我们需要的scrollX,太赞了~~~

3、动画比例的计算

我们在onScrollChanged里面,拿到 l 也就是个getScrollX,即菜单已经显示的宽度值;

float scale = l * 1.0f / mMenuWidth;


与菜单的宽度做除法运算,在菜单隐藏到显示整个过程,会得到1.0~0.0这么个变化的区间;

有了这个区间,就可以根据这个区间设置动画了;

1、首先是内容区域的缩放比例计算:

我们准备让在菜单出现的过程中,让内容区域从1.0~0.8进行变化~~

那么怎么把1.0~0.0转化为1.0~0.8呢,其实很简单了:

float rightScale = 0.8f + scale * 0.2f; (scale 从1到0 ),是不是哦了~

接下来还有3个动画:

2、菜单的缩放比例计算

仔细观察了下QQ,菜单大概缩放变化是0.7~1.0

float leftScale = 1 - 0.3f * scale;

3、菜单的透明度比例:

我们设置为0.6~1.0;即:0.6f + 0.4f * (1 - scale)

4、菜单的x方向偏移量:

看一下QQ,并非完全从被内容区域覆盖,还是有一点拖出的感觉,所以我们的偏移量这么设置:

tranlateX = mMenuWidth * scale * 0.6f ;刚开始还是让它隐藏一点点~~~

4、完整的实现

说了这么多,其实到上一篇史上最简单的侧滑,到QQ5.0的效果的转变,只需要几行代码~~

@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt)
	{
		super.onScrollChanged(l, t, oldl, oldt);
		float scale = l * 1.0f / mMenuWidth;
		float leftScale = 1 - 0.3f * scale;
		float rightScale = 0.8f + scale * 0.2f;
		
		ViewHelper.setScaleX(mMenu, leftScale);
		ViewHelper.setScaleY(mMenu, leftScale);
		ViewHelper.setAlpha(mMenu, 0.6f + 0.4f * (1 - scale));
		ViewHelper.setTranslationX(mMenu, mMenuWidth * scale * 0.6f);

		ViewHelper.setPivotX(mContent, 0);
		ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
		ViewHelper.setScaleX(mContent, rightScale);
		ViewHelper.setScaleY(mContent, rightScale);

	}


就这么几行。这里属性动画用的nineoldandroids为了保持向下的兼容;主要就是设置了各种动画,上面都详细说了~~~
然后,记得把我们的菜单和内容的布局,单独声明出来为我们的mMenu ,mContent ~没了,就改了这么几行~

3、效果图

是骡子是马,拉出来溜溜


菜单栏需要ListView的拖动也是不会冲突了,上篇已经测试了;

关于动画属性的范围:上面介绍的特别清楚,比如内容我们是最小显示0.8,你要是喜欢0.6,自己去修改一下;包括偏移量,透明度等范围;

因为上一篇已经写了如何把属性抽取成自定义的属性;所以这里就没有抽取了,不然总觉得是在重复~


嗯,最近还有写APP的侧滑,是这样的,就是菜单栏完全隐藏在内容区域下面,如果需要这样需求的:


其实我还满喜欢这样效果的。

实现呢,注释几行代码就实现了:

@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt)
	{
		super.onScrollChanged(l, t, oldl, oldt);
		float scale = l * 1.0f / mMenuWidth;
//		float leftScale = 1 - 0.3f * scale;
//		float rightScale = 0.8f + scale * 0.2f;
//		
//		ViewHelper.setScaleX(mMenu, leftScale);
//		ViewHelper.setScaleY(mMenu, leftScale);
//		ViewHelper.setAlpha(mMenu, 0.6f + 0.4f * (1 - scale));
		ViewHelper.setTranslationX(mMenu, mMenuWidth * scale );

//		ViewHelper.setPivotX(mContent, 0);
//		ViewHelper.setPivotY(mContent, mContent.getHeight() / 2);
//		ViewHelper.setScaleX(mContent, rightScale);
//		ViewHelper.setScaleY(mContent, rightScale);

	}

源码下载地址:http://download.csdn.net/detail/lmj623565791/7911785

本文转载自:http://blog.csdn.net/lmj623565791/article/details/39257409

欧吧在线
粉丝 0
博文 32
码字总数 4007
作品 0
沈阳
私信 提问
Android 自定义控件打造史上最简单的侧滑菜单

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lmj623565791/article/details/39185641 转载请标明出处:http://blog.csdn.net/lmj623565791/article/detai...

鸿洋_
2014/09/11
0
0
Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lmj623565791/article/details/39257409 转载请标明出处:http://blog.csdn.net/lmj623565791/article/detai...

鸿洋_
2014/09/15
0
0
Android 实现形态各异的双向侧滑菜单 自定义控件来袭

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lmj623565791/article/details/39670935 转载请标明出处:http://blog.csdn.net/lmj623565791/article/detai...

鸿洋_
2014/10/08
0
0
【压岁干货】精彩技术博客+优秀源码集锦

虽然2015年已经过了一月有余,但在中国,好像只有过了春节才算进入新的一年。眼看着这也春节倒计时了,大家好像又都忙了起来,赶项目赶项目…… 从DevStore整理了一些优秀的技术博客和源码作...

牵着蜗牛去西藏
2015/02/04
1K
4
Android UI 特效大全

Android UI特效大全 总体传送门:http://git.oschina.net/bob4j/Android-UI 基本上项目中都有效果图可自行查看 , 并且有些项目中都有README.md 文件,使用前请先阅读以下。 1.弧形(圆形)菜单...

不正经啊不正经
2015/07/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Angular 英雄编辑器

应用程序现在有了基本的标题。 接下来你要创建一个新的组件来显示英雄信息并且把这个组件放到应用程序的外壳里去。 创建英雄组件 使用 Angular CLI 创建一个名为 heroes 的新组件。 ng gener...

honeymoose
今天
5
0
Kernel DMA

为什么会有DMA(直接内存访问)?我们知道通常情况下,内存数据跟外设之间的通信是通过cpu来传递的。cpu运行io指令将数据从内存拷贝到外设的io端口,或者从外设的io端口拷贝到内存。由于外设...

yepanl
今天
6
0
hive

一、hive的定义: Hive是一个SQL解析引擎,将SQL语句转译成MR Job,然后再在Hadoop平台上运行,达到快速开发的目的 Hive中的表是纯逻辑表,就只是表的定义,即表的元数据。本质就是Hadoop的目...

霉男纸
今天
5
0
二、Spring Cloud—Eureka(Greenwich.SR1)

注:本系列文章所用工具及版本如下:开发工具(IDEA 2018.3.5),Spring Boot(2.1.3.RELEASE),Spring Cloud(Greenwich.SR1),Maven(3.6.0),JDK(1.8) Eureka: Eureka是Netflix开发...

倪伟伟
昨天
15
0
eclipse常用插件

amaterasUML https://takezoe.github.io/amateras-update-site/ https://github.com/takezoe/amateras-modeler modelGoon https://www.cnblogs.com/aademeng/articles/6890266.html......

大头鬼_yc
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部