Android 实现形态各异的双向侧滑菜单 自定义控件来袭(转载)
1、概述
关于自定义控件侧滑已经写了两篇了~~今天决定把之前的单向改成双向,当然了,单纯的改动之前的代码也没意思,今天不仅会把之前的单向改为双向,还会多添加一种侧滑效果,给大家带来若干种形态各异的双向侧滑菜单,不过请放心,代码会很简单~~然后根据这若干种,只要你喜欢,相信你可以打造任何绚(bian)丽(tai)效果的双向侧滑菜单~~
2、目标效果
1、最普通的双向侧滑
是不是很模糊,嗯,没办法,电脑显卡弱。。。。
2、抽屉式双向侧滑
3、菜单在内容之下的双向侧滑
凑合看下,文章最后会提供源码下载,大家可以安装体验一下~
所有的代码的内容区域都是一个ListView,两侧菜单都包含按钮,基本的冲突都检测过~~~当然如果有bug在所难免,请直接留言;如果你解决了某些未知bug,希望你也可以留言,或许可以帮助到其他人~~
下面就开始我们的代码了。
3、代码是最好的老师
1、布局文件
既然是双向菜单,那么我们的布局文件是这样的:
- <com.zhy.view.BinarySlidingMenu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu02"
- android:id="@+id/id_menu"
- 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:id="@android:id/list"
- 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,为了测试我们是否有冲突~~不过有咱们也不怕~
效果图:
当然了,最简单的双向侧滑怎么能满足大家的好(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:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- xmlns:zhy="http://schemas.android.com/apk/res/com.zhy.zhy_bin_slidingmenu03"
- android:id="@+id/id_menu"
- 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:id="@android:id/list"
- 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)始终是最先绘制。
现在再运行,效果图:
效果是不是很赞,请允许我把图挪过来了~~~
现在,还有最后一个效果,如果让,菜单在内容之下呢?
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 实现形态各异的双向侧滑菜单 自定义控件来袭(转载)的更多相关文章
- Android 实现形态各异的双向侧滑菜单 自定义控件来袭
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935,本文出自:[张鸿洋的博客] 1.概述 关于自定义控件侧滑已经写了两 ...
- Android DrawerLayout 高仿QQ5.2双向侧滑菜单
1.概述 之前写了一个Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭 ,恰逢QQ5.2又加了一个右侧菜单,刚好看了下DrawerLayout,一方面官方的东西,我都比较感兴趣:另一方面 ...
- Android 实现形态各异的双向側滑菜单 自己定义控件来袭
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935.本文出自:[张鸿洋的博客] 1.概述 关于自己定义控件側滑已经写了 ...
- Xamarin.Android中使用ResideMenu实现侧滑菜单
上次使用Xamarin.Android实现了一个比较常用的功能PullToRefresh,详情见:Xamarin. Android实现下拉刷新功能 这次将实现另外一个手机App中比较常用的功能:侧滑菜 ...
- android L 新控件侧滑菜单DrawerLayout 使用教程
介绍 drawerLayout是Support Library包中实现了侧滑菜单效果的控件,可以说drawerLayout是因为第三方控件如MenuDrawer等的出现之后,google借鉴而出现的产 ...
- Android自定义顶部栏及侧滑菜单和fragment+viewpag滑动切换的实现
嘿嘿嘿,关于android滑动的操作,是不是经常都会用到呢. 我肯定也要学习一下啦. https://blog.csdn.net/u013184970/article/details/82882107 ...
- Android开源库--SlidingMenu左右侧滑菜单
如果说我比别人看得更远些,那是因为我站在了巨人的肩上. github地址:https://github.com/jfeinstein10/SlidingMenu 设置: 1.下载之后以依赖项的 ...
- android119 侧滑菜单
MainActivity.java package com.heima52.slidemenu; import com.heima52.slidemenu.view.SlideMenu; import ...
- DrawerLayoutDemo【侧边栏(侧滑菜单)简单实现】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 简单实现侧边栏(侧滑菜单)效果: 点击触发打开左侧侧边栏,手势滑动关闭左侧侧边栏: 手势滑动打开右侧侧边栏,手势滑动关闭右侧侧边栏: ...
随机推荐
- bzoj4513 储能表
求 $\sum\limits_{i=0}^{n-1} \sum\limits_{j=0}^{m-1} max((x \space xor \space j) - k,0)$ ,膜 $p$ $n,m \ ...
- [TopCoder12727]FoxAndCity
vjudge 题意 你有一张\(n\)点的无向图,每个点有一个点权\(w_i\).图中原来存在一些边,你可以任意给这张图加上一些边. 记点\(i\)到点\(1\)的距离为\(d_i\),你需要最小化\ ...
- Java 时间工具类
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 1.Calendar 转化 String ...
- Linux 终端 忽略大小写
忘了在哪里看到的了,记录一下. 在-/.inputrc中加入一行 set completion-ignore-case on 搞定! 这样在终端输入.补全时就忽略大小写了.当然,Linux本身还是区分 ...
- HIVE-几道经典的hive题目
建表相关语句在此,具体的数据自己制作吧 create table student(Sno int,Sname string,Sex string,Sage int,Sdept string)row f ...
- uptime命令查看系统启动时间和运行时间、查看linux系统负载
1.uptime命令输出:16:11:40 up 59 days, 4:21, 2 users, load average: 0.00, 0.01, 0.00 2.查看/proc/uptime文件计算 ...
- Oracle 内存结构
内存结构 Oracle内存,进程与数据库的图 sga:系统全局区,用来存放操作的数据,库缓存,数据字典等控制信息的内存区域, pga:进程全局区,服务进程专用的内存区域,大多数内容非共享 uga:用 ...
- SpringMVC—对Ajax的处理(含 JSON 类型)(2)
这里编写了一个通用的类型转换器: 用来转换形如: firstName=jack&lastName=lily&gender=1&foods=Steak&foods=Piz ...
- 2015.3.4 VS2005调用MFC dll时报错及解决
今天在用VS2005调用MFCdll时报错: 正试图在 os 加载程序锁内执行托管代码.不要尝试在 DllMain 或映像初始化函数内运行托管代码... 原因是我在dll的CSpaceApp::CSp ...
- jsonp实现跨域请求的本质demo[无法发送post请求]
views.py def get_data(request): return HttpResponse("机密数据") urls.py urlpatterns = [ url(r' ...