Android 实现形态各异的双向侧滑菜单 自定义控件来袭 1、概述 2、目标效果 3、代码是最好的老师 4、打造抽屉式双向侧滑 5、打造菜单在内容之下的双向侧滑

转载请标明出处:http://blog.****.net/lmj623565791/article/details/39670935,本文出自:【张鸿洋的博客】

关于自定义控件侧滑已经写了两篇了~~今天决定把之前的单向改成双向,当然了,单纯的改动之前的代码也没意思,今天不仅会把之前的单向改为双向,还会多添加一种侧滑效果,给大家带来若干种形态各异的双向侧滑菜单,不过请放心,代码会很简单~~然后根据这若干种,只要你喜欢,相信你可以打造任何绚(bian)丽(tai)效果的双向侧滑菜单~~

首先回顾一下,之前写过的各种侧滑菜单,为了不占据篇幅,就不贴图片了:

1、最普通的侧滑效果,请参考:Android 自定义控件打造史上最简单的侧滑菜单

2、仿QQ5.0侧滑效果,请参考:Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭

3、菜单在内容之后的侧滑效果,请参考:Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭

2、目标效果


1、最普通的双向侧滑

Android  实现形态各异的双向侧滑菜单 自定义控件来袭
1、概述
2、目标效果
3、代码是最好的老师
4、打造抽屉式双向侧滑
5、打造菜单在内容之下的双向侧滑

是不是很模糊,嗯,没办法,电脑显卡弱。。。。

2、抽屉式双向侧滑

Android  实现形态各异的双向侧滑菜单 自定义控件来袭
1、概述
2、目标效果
3、代码是最好的老师
4、打造抽屉式双向侧滑
5、打造菜单在内容之下的双向侧滑

3、菜单在内容之下的双向侧滑

Android  实现形态各异的双向侧滑菜单 自定义控件来袭
1、概述
2、目标效果
3、代码是最好的老师
4、打造抽屉式双向侧滑
5、打造菜单在内容之下的双向侧滑

凑合看下,文章最后会提供源码下载,大家可以安装体验一下~

所有的代码的内容区域都是一个ListView,两侧菜单都包含按钮,基本的冲突都检测过~~~当然如果有bug在所难免,请直接留言;如果你解决了某些未知bug,希望你也可以留言,或许可以帮助到其他人~~

下面就开始我们的代码了。


3、代码是最好的老师

1、布局文件

既然是双向菜单,那么我们的布局文件是这样的:

<com.zhy.view.BinarySlidingMenu xmlns:andro
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu02"
    android:
    android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:scrollbars="none"
    zhy:rightPadding="100dp" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:orientation="horizontal" >

        <include layout="@layout/layout_menu" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@drawable/eee"
            android:gravity="center"
            android:orientation="horizontal" >

            <ListView
                android:
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" >
            </ListView>
        </LinearLayout>

        <include layout="@layout/layout_menu2" />
    </LinearLayout>

</com.zhy.view.BinarySlidingMenu>


最外层是我们的自定义的BinarySlidingMenu,内部一个水平方向的LinearLayout,然后是左边的菜单,内容区域,右边的菜单布局~~

关键就是我们的BinarySlidingMenu

2、BinarySlidingMenu的构造方法

/**
	 * 屏幕宽度
	 */
	private int mScreenWidth;

	/**
	 * dp 菜单距离屏幕的右边距
	 */
	private int mMenuRightPadding;

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

		TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
				R.styleable.BinarySlidingMenu, defStyle, 0);
		int n = a.getIndexCount();
		for (int i = 0; i < n; i++)
		{
			int attr = a.getIndex(i);
			switch (attr)
			{
			case R.styleable.BinarySlidingMenu_rightPadding:
				// 默认50
				mMenuRightPadding = a.getDimensionPixelSize(attr,
						(int) TypedValue.applyDimension(
								TypedValue.COMPLEX_UNIT_DIP, 50f,
								getResources().getDisplayMetrics()));// 默认为10DP
				break;
			}
		}
		a.recycle();
	}
我们在构造方法中,获取我们自定义的一个属性rightPadding,然后赋值给我们的成员变量mMenuRightPadding;关于如何自定义属性参考侧滑菜单的第一篇博文,这里就不赘述了。

3、onMeasure

onMeasure中肯定是对侧滑菜单的宽度、高度等进行设置:

@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
	{
		/**
		 * 显示的设置一个宽度
		 */
		if (!once)
		{

			mWrapper = (LinearLayout) getChildAt(0);
			mLeftMenu = (ViewGroup) mWrapper.getChildAt(0);
			mContent = (ViewGroup) mWrapper.getChildAt(1);
			mRightMenu = (ViewGroup) mWrapper.getChildAt(2);

			mMenuWidth = mScreenWidth - mMenuRightPadding;
			mHalfMenuWidth = mMenuWidth / 2;
			mLeftMenu.getLayoutParams().width = mMenuWidth;
			mContent.getLayoutParams().width = mScreenWidth;
			mRightMenu.getLayoutParams().width = mMenuWidth;

		}
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);

	}

可以看到我们分别给左侧、右侧的菜单设置了宽度(mScreenWidth - mMenuRightPadding);

宽度设置完成以后,肯定就是定位了,把左边的菜单弄到左边去,右边的菜单放置到右边,中间依然是我们的内容区域,那么请看onLayout方法~

4、onLayout

@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;
		}

	}

哈,出奇的简单,因为我们的内部是个横向的线性布局,所以直接把左侧滑出去即可~~定位也完成了,那么此时应该到了我们的处理触摸了。

5、onTouchEvent

	@Override
	public boolean onTouchEvent(MotionEvent ev)
	{
		int action = ev.getAction();
		switch (action)
		{
		// Up时,进行判断,如果显示区域大于菜单宽度一半则完全显示,否则隐藏
		case MotionEvent.ACTION_UP:
			int scrollX = getScrollX();
			// 如果是操作左侧菜单
			if (isOperateLeft)
			{
				// 如果影藏的区域大于菜单一半,则影藏菜单
				if (scrollX > mHalfMenuWidth)
				{
					this.smoothScrollTo(mMenuWidth, 0);
					// 如果当前左侧菜单是开启状态,且mOnMenuOpenListener不为空,则回调关闭菜单
					if (isLeftMenuOpen && mOnMenuOpenListener != null)
					{
						// 第一个参数true:打开菜单,false:关闭菜单;第二个参数 0 代表左侧;1代表右侧
						mOnMenuOpenListener.onMenuOpen(false, 0);
					}
					isLeftMenuOpen = false;

				} else
				// 关闭左侧菜单
				{
					this.smoothScrollTo(0, 0);
					// 如果当前左侧菜单是关闭状态,且mOnMenuOpenListener不为空,则回调打开菜单
					if (!isLeftMenuOpen && mOnMenuOpenListener != null)
					{
						mOnMenuOpenListener.onMenuOpen(true, 0);
					}
					isLeftMenuOpen = true;
				}
			}

			// 操作右侧
			if (isOperateRight)
			{
				// 打开右侧侧滑菜单
				if (scrollX > mHalfMenuWidth + mMenuWidth)
				{
					this.smoothScrollTo(mMenuWidth + mMenuWidth, 0);
				} else
				// 关闭右侧侧滑菜单
				{
					this.smoothScrollTo(mMenuWidth, 0);
				}
			}

			return true;
		}
		return super.onTouchEvent(ev);
	}


依然是简单~~~我们只需要关注ACTION_UP,然后得到手指抬起后的scrollX,然后我们通过一个布尔值,判断用户现在操作是针对左侧菜单,还是右侧菜单?

如果是操作左侧,那么判断scorllX是否超过了菜单宽度的一半,然后做相应的操作。

如果是操作右侧,那么判断scrollX与 mHalfMenuWidth + mMenuWidth ( 注意下,右侧菜单完全影藏的时候,scrollX 就等于 mMenuWidth ),然后做对应的操作。

我们还给左侧的菜单加上了一个回调:

if (isLeftMenuOpen && mOnMenuOpenListener != null)
{
//第一个参数true:打开菜单,false:关闭菜单;第二个参数 0 代表左侧;1代表右侧
mOnMenuOpenListener.onMenuOpen(false, 0);
}

扫一眼我们的回调接口:

/**
	 * 回调的接口
	 * @author zhy
	 *
	 */
	public interface OnMenuOpenListener
	{
		/**
		 * 
		 * @param isOpen true打开菜单,false关闭菜单
		 * @param flag 0 左侧, 1右侧
		 */
		void onMenuOpen(boolean isOpen, int flag);
	}

右侧菜单我没有添加回调,大家按照左侧的形式自己添加下就ok ; 

好了,接下来,看下我们判断用户操作是左侧还是右侧的代码写在哪。

6、onScrollChanged

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt)
	{
		super.onScrollChanged(l, t, oldl, oldt);

		if (l > mMenuWidth)
		{
			isOperateRight = true;
			isOperateLeft = false;
		} else
		{
			isOperateRight = false;
			isOperateLeft = true;
		}
	}

如果看过前两篇,对这个方法应该很眼熟了吧。我们直接通过 l 和 菜单宽度进行比较, 如果大于菜单宽度,那么肯定是想操作右侧菜单,否则那么就是想操作左侧菜单;

到此,我们的双向侧滑菜单已经大功告成了,至于你信不信,反正我有效果图。看效果图前,贴一下MainActivity的代码:

7、MainActivity

package com.zhy.zhy_bin_slidingmenu02;

import java.util.ArrayList;
import java.util.List;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Toast;

import com.zhy.view.BinarySlidingMenu;
import com.zhy.view.BinarySlidingMenu.OnMenuOpenListener;

public class MainActivity extends ListActivity
{
	private BinarySlidingMenu mMenu;
	private List<String> mDatas = new ArrayList<String>();

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);

		mMenu = (BinarySlidingMenu) findViewById(R.id.id_menu);
		mMenu.setOnMenuOpenListener(new OnMenuOpenListener()
		{
			@Override
			public void onMenuOpen(boolean isOpen, int flag)
			{
				if (isOpen)
				{
					
					Toast.makeText(getApplicationContext(),
							flag == 0 ? "LeftMenu Open" : "RightMenu Open",
							Toast.LENGTH_SHORT).show();
				} else
				{
					
					Toast.makeText(getApplicationContext(),
							flag == 0 ? "LeftMenu Close" : "RightMenu Close",
							Toast.LENGTH_SHORT).show();
				}

			}
		});
		// 初始化数据
		for (int i = 'A'; i <= 'Z'; i++)
		{
			mDatas.add((char) i + "");
		}
		// 设置适配器
		setListAdapter(new ArrayAdapter<String>(this, R.layout.item, mDatas));
	}
}

没撒好说的,为了方便直接继承了ListActivity,然后设置了一下回调,布局文件一定要有ListView,为了测试我们是否有冲突~~不过有咱们也不怕~

效果图:

Android  实现形态各异的双向侧滑菜单 自定义控件来袭
1、概述
2、目标效果
3、代码是最好的老师
4、打造抽屉式双向侧滑
5、打造菜单在内容之下的双向侧滑

当然了,最简单的双向侧滑怎么能满足大家的好(Zhao)奇(Nue)心呢,所以我们准备玩点神奇的花样~~

4、打造抽屉式双向侧滑

我们在onScrollChanged添加两行代码~~为mContent设置一个属性动画

@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt)
	{
		super.onScrollChanged(l, t, oldl, oldt);
		
		if (l > mMenuWidth)
		{
			isOperateRight = true;
			isOperateLeft = false;
		} else
		{
			isOperateRight = false;
			isOperateLeft = true;
		}
		
		float scale = l * 1.0f / mMenuWidth;
		ViewHelper.setTranslationX(mContent, mMenuWidth * (scale - 1));
		
	}
简单分析一下哈:

1、scale,在滑动左侧菜单时:值为1.0~0.0;mMenuWidth * (scale - 1) 的值就是从 0.0~ -mMenuWidth(注意:负的) ; 那么mContent的向偏移量,就是0到mMenuWidth ;也就是说,整个滑动的过程,我们强制让内容区域固定了。

2、scale,在滑动右侧菜单时:值为:1.0~2.0;mMenuWidth * (scale - 1) 的值就是从 0.0~ mMenuWidth(注意:正数) ;那么mContent的向偏移量,就是0到mMenuWidth ;也就是说,整个滑动的过程,我们强制让内容区域固定了。


好了,内容固定了,那么我们此刻的两边菜单应该是在内容之上显示出来~~这不就是我们的抽屉效果么~

嗯,这次木有效果图了,因为测试结果发现,左侧的菜单会被内容区域遮盖住,看不到;右侧菜单符合预期效果;因为,左侧菜单滑动出来以后,被内容区域遮盖住了,这个也很容易理解,毕竟我们的布局,内容在左侧菜单后面,肯定会挡住它的。那么,怎么办呢?

起初,我准备使用bringToFont方法,在拖动的时候,让菜单在上面~~~不过呢,问题大大的,有兴趣可以试试~~

于是乎,我换了个方法,我将BinarySlidingMenu内部的Linearlayout进行了自定义,现在布局文件是这样的:

<com.zhy.view.BinarySlidingMenu xmlns:andro
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu03"
    android:
    android:layout_width="wrap_content"
    android:layout_height="fill_parent"
    android:scrollbars="none"
    zhy:rightPadding="100dp" >

    <com.zhy.view.MyLinearLayout
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:orientation="horizontal" >

        <include layout="@layout/layout_menu" />

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@drawable/eee"
            android:gravity="center"
            android:orientation="horizontal" >

            <ListView
                android:
                android:layout_width="fill_parent"
                android:layout_height="fill_parent" >
            </ListView>
        </LinearLayout>

        <include layout="@layout/layout_menu2" />
    </com.zhy.view.MyLinearLayout>

</com.zhy.view.BinarySlidingMenu>

MyLinearlayout的代码:

package com.zhy.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;

public class MyLinearLayout extends LinearLayout
{

	public MyLinearLayout(Context context, AttributeSet attrs)
	{
		super(context, attrs);
//		Log.e("TAG", "MyLinearLayout");
		setChildrenDrawingOrderEnabled(true);
	}

	@Override
	protected int getChildDrawingOrder(int childCount, int i)
	{
//		Log.e("tag", "getChildDrawingOrder" + i + " , " + childCount);

		if (i == 0)
			return 1;
		if (i == 2)
			return 2;
		if (i == 1)
			return 0;
		return super.getChildDrawingOrder(childCount, i);

	}

}

在构造方法设置setChildrenDrawingOrderEnabled(true);然后getChildDrawingOrder复写一下绘制子View的顺序,让内容(i==0)始终是最先绘制。

现在再运行,效果图:

Android  实现形态各异的双向侧滑菜单 自定义控件来袭
1、概述
2、目标效果
3、代码是最好的老师
4、打造抽屉式双向侧滑
5、打造菜单在内容之下的双向侧滑

效果是不是很赞,请允许我把图挪过来了~~~

现在,还有最后一个效果,如果让,菜单在内容之下呢?

5、打造菜单在内容之下的双向侧滑

不用说,大家都能想到,无非就是在onScrollChanged改改属性动画呗,说得对!

1、改写onScrollChanged方法

@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt)
	{
		super.onScrollChanged(l, t, oldl, oldt);

		if (l > mMenuWidth)
		{
			// 1.0 ~2.0 1.0~0.0
			// (2-scale)
			float scale = l * 1.0f / mMenuWidth;
			isOperateRight = true;
			isOperateLeft = false;
			ViewHelper.setTranslationX(mRightMenu, -mMenuWidth * (2 - scale));

		} else
		{
			float scale = l * 1.0f / mMenuWidth;
			isOperateRight = false;
			isOperateLeft = true;
			ViewHelper.setTranslationX(mLeftMenu, mMenuWidth * scale);

		}
	}

也就是拉的时候,尽量让菜单保证在内容之下~~~代码自己琢磨下

2、改写MyLinearLayout

当然了,仅仅这些是不够的,既然我们的样式变化了,那么改写View的绘制顺序肯定也是必须的。

看下我们的MyLinearLayout

package com.zhy.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;

public class MyLinearLayout extends LinearLayout
{

	public MyLinearLayout(Context context, AttributeSet attrs)
	{
		super(context, attrs);
		Log.e("TAG", "MyLinearLayout");
		setChildrenDrawingOrderEnabled(true);
	}

	@Override
	protected int getChildDrawingOrder(int childCount, int i)
	{

		if (i == 0)
			return 0;
		if (i == 2)
			return 1;
		if (i == 1)
			return 2;
		return super.getChildDrawingOrder(childCount, i);

	}

}
效果图:

Android  实现形态各异的双向侧滑菜单 自定义控件来袭
1、概述
2、目标效果
3、代码是最好的老师
4、打造抽屉式双向侧滑
5、打造菜单在内容之下的双向侧滑


到此,我们的形态各异的双向侧滑就结束了~~~

从最普通的双向,到抽屉式,再到我们的菜单在内容之下的侧滑都已经搞定;希望大家通过这三个侧滑,可以举一反三,打造各种变态的侧滑效果~~~~

最后我把3个侧滑的源码都会共享出来,大家自行下载:


Android普通双向侧滑


Android抽屉式双向侧滑


Android菜单在内容之下的双向侧滑


ps:本人测试手机,小米2s,尽量真机进行测试。


----------------------------------------------------------------------------------------------------------

博主部分视频已经上线,如果你不喜欢枯燥的文本,请猛戳(初录,期待您的支持):

1、高仿微信5.2.1主界面及消息提醒

2、高仿QQ5.0侧滑

















版权声明:本文为博主原创文章,未经博主允许不得转载。