转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935,本文出自:【张鸿洋的博客】
1、概述
关于自定义控件侧滑已经写了两篇了~~今天决定把之前的单向改成双向,当然了,单纯的改动之前的代码也没意思,今天不仅会把之前的单向改为双向,还会多添加一种侧滑效果,给大家带来若干种形态各异的双向侧滑菜单,不过请放心,代码会很简单~~然后根据这若干种,只要你喜欢,相信你可以打造任何绚(bian)丽(tai)效果的双向侧滑菜单~~
首先回顾一下,之前写过的各种侧滑菜单,为了不占据篇幅,就不贴图片了:
1、最普通的侧滑效果,请参考:Android 自定义控件打造史上最简单的侧滑菜单
2、仿QQ5.0侧滑效果,请参考:Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭
3、菜单在内容之后的侧滑效果,请参考:Android 高仿 QQ5.0 侧滑菜单效果 自定义控件来袭
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);
}
}
效果图:
到此,我们的形态各异的双向侧滑就结束了~~~
从最普通的双向,到抽屉式,再到我们的菜单在内容之下的侧滑都已经搞定;希望大家通过这三个侧滑,可以举一反三,打造各种变态的侧滑效果~~~~
最后我把3个侧滑的源码都会共享出来,大家自行下载:
Android普通双向侧滑
Android抽屉式双向侧滑
Android菜单在内容之下的双向侧滑
ps:本人测试手机,小米2s,尽量真机进行测试。
分享到:
相关推荐
形态各异的不规则窗体(程序), 本文所附带的例子全部以BeginPath,EndPath和PathToRegion为中心来讲解如何实现不规则窗体。
Android上的菜单展示风格各异,其中用得最多且体验最好的莫过于左右滑动来显示隐藏的菜单,android-menudrawer是一个滑动 式菜单实现,允许用户在应用当中实现无缝导航。该项目具有多种菜单展示效果,其中最常见的...
PS制作形态各异的烟雾效果图.doc
Android上的菜单展示风格各异,其中用得最多且体验最好的莫过于左右滑动来显示隐藏的菜单,android-menudrawer是一个滑动 式菜单实现,允许用户在应用当中实现无缝导航。该项目具有多种菜单展示效果,其中最常见的...
幼儿园大班美术优质教案《形态各异的杯子》润新教育.txt
aRes 是一个功能极佳的标准ocx报表控件,采用VC开发,可以像操作Word方式一样徒手设计形式各异的表格,并提供了灵活方便的开发接口。目前在国内制造业中已有着很好的成功范例。针对各种中国式的表格有着很好的解决...
aResreport是一个功能极佳的标准ocx报表控件,采用VC开发,可以像操作Word方式一样徒手设计形式各异的表格,并提供了灵活方便的开发接口。目前在国内制造业中已有着很好的成功范例。针对各种中国式的表格有着很好的...
menudrawer: Android上的菜单展示风格各异,其中用得最多且体验最好的莫过于左右滑动来显示隐藏的菜单,android-menudrawer是一个滑动 式菜单实现,允许用户在应用当中实现无缝导航。该项目具有多种菜单展示效果,...
Android上的菜单展示风格各异,其中用得最多且体验最好的莫过于左右滑动来显示隐藏的菜单,android-menudrawer是一个滑动式菜单实现,允许用户在应用当中实现无缝导航。该项目具有多种菜单展示效果,其中最常见的...
作为一名开发者,大家应该都知道在浏览器中存在一些内置的控件:Alert,Confirm等,但是这些控件通常根据浏览器产商的不同而形态各异,视觉效果往往达不到UI设计师的要求。更重要的是,这类内置控件的风格很难与...
皮肤控件研究文档,破解后的库文件,皮肤设计工具使用教程 皮肤控件 skin++ skincrafter SkinFeature IrisSkin 我共享的都是本人实际验证过的精品,有文档,破解后的库文件,皮肤设计工具使用教程, 1 软件界面 每...
支持自定义弹出视图功能源码,源码DLAlertView,DLAlertView是UIAlertView的替代选择,提供了多种弹出视图,高度自定义。功能:和UIAlertView相同的API;使用委托或者blocks;动画效果和外观非常匹配iOS 7中的...
法中引入随机数,实现大小不同、形态各异的树木生成. 绘制时,根据视点距离的远近,调整场景的 精细程度,以符合视觉效果,且加快了图形生成速度. 以此方法构造森林,可改变观察者的视点位 置,实现对森林景观的实时模拟.
之前我们分享过很多款样式各异的jQuery和HTML5日历控件,但今天要介绍的这款日历控件绝对是超炫酷的,它非常富有创意,利用了HTML5的3D特性实现了魔方的日历动画控件,每一个日期就是一个小的魔方立方体,鼠标滑过...
多位世界顶级的web设计师和开发者带给你基于web标准的风格各异的网页作品、他们的创作心得以及涉及其中的方方面面,相信能给致力于web标准开发设计的你带来帮助和启迪。 第一个是本书的完整文件,第二个压缩包是...
多年前写的IE下的打印控件,还是很多朋友用上了,这么多年过去了,当初在IE7下做的开发,可能在新的IE下不太适用了,或者因为大家的应用环境各异,出现了各种情况,为了大家都能很好的完成任务决定把源码放出,如果...
不久前,我们刚刚为大家分享过很多外观各异的CSS3菜单,很多都是比较实用的,比如这款jQuery/CSS3波浪形弹性下拉菜单就非常具有创意,也很实用。今天要介绍的也是一款基于纯CSS3的3D菜单,当我们将鼠标滑过菜单项时...