Android左右滑出菜单实例分析

Android左右滑出菜单实例分析,第1张

概述现在的Android应用,只要有一个什么新的创意,过不了多久,几乎所有的应用都带这个创意。这不,咱们公司最近的一个持续性的项目,想在首页加个从左滑动出来的菜单,我查阅网上资料,并自己摸索,实现了左、右两边都能 现在的AndroID应用,只要有一个什么新的创意,过不了多久,几乎所有的应用都带这个创意。这不,咱们公司最近的一个持续性的项目,想在首页加个从左滑动出来的菜单,我查阅网上资料,并自己摸索,实现了左、右两边都能滑出菜单,并且,左、右菜单中,都可以加ListVIEw等这类需要解决GestureDetector冲突的问题(如在首页面中,含有ListVIEw,上下滚动时,左右不动,相反,左右滑动菜单时,上下不动,听着头就大了吧!)

先上几张图,给大家瞧瞧,对整体有个了解:

 

一、首页布局:
复制代码 代码如下:
<relativeLayout xmlns:androID="http://schemas.androID.com/apk/res/androID"
xmlns:tools="http://schemas.androID.com/tools"
androID:layout_wIDth="match_parent"
androID:layout_height="match_parent"
tools:context=".MainActivity" >
<!-- 主布局 -->
<relativeLayout
androID:ID="@+ID/mainLayout"
androID:layout_wIDth="match_parent"
androID:layout_height="match_parent">
<relativeLayout
androID:ID="@+ID/Titlebar"
androID:layout_wIDth="match_parent"
androID:layout_height="wrap_content"
androID:background="#ffffff"
androID:padding="5dip">
<ImageVIEw
androID:ID="@+ID/ivMore"
androID:src="@drawable/nav_more_normal"
androID:contentDescription="@string/img_desc"
androID:layout_wIDth="wrap_content"
androID:layout_height="wrap_content"
androID:layout_centerVertical="true"
androID:layout_alignParentleft="true"
androID:layout_marginleft="10dip"/>
<TextVIEw
androID:layout_wIDth="wrap_content"
androID:layout_height="wrap_content"
androID:layout_centerHorizontal="true"
androID:layout_centerVertical="true"
androID:text="@string/Title"
androID:textSize="20sp"
androID:textcolor="#000000"/>
<ImageVIEw
androID:ID="@+ID/ivSettings"
androID:src="@drawable/nav_setting_normal"
androID:contentDescription="@string/img_desc"
androID:layout_wIDth="wrap_content"
androID:layout_height="wrap_content"
androID:layout_centerVertical="true"
androID:layout_alignParentRight="true"
androID:layout_marginRight="10dip"/>
</relativeLayout>

<ImageVIEw
androID:src="@drawable/picture"
androID:contentDescription="@string/img_desc"
androID:scaleType="fitXY"
androID:layout_wIDth="match_parent"
androID:layout_height="match_parent"
androID:layout_below="@ID/Titlebar"/>
</relativeLayout>
<!-- 左侧菜单导航 -->
<relativeLayout
androID:ID="@+ID/leftLayout"
androID:layout_wIDth="140dip"
androID:layout_height="match_parent"
androID:background="#000000">
<relativeLayout
androID:ID="@+ID/leftTitlebar"
androID:layout_wIDth="match_parent"
androID:layout_height="wrap_content"
androID:background="@color/grey21"
androID:padding="5dip">
<TextVIEw
androID:layout_marginleft="5dip"
androID:layout_wIDth="wrap_content"
androID:layout_height="wrap_content"
androID:layout_alignParentleft="true"
androID:layout_centerVertical="true"
androID:text="@string/leftNav"
androID:textSize="20sp"
androID:textcolor="#ffffff"/>
</relativeLayout>
<com.chris.lr.slIDemenu.Layoutrelative
androID:ID="@+ID/layoutSlIDeMenu"
androID:layout_wIDth="match_parent"
androID:layout_height="match_parent"
androID:layout_below="@ID/leftTitlebar">
<ListVIEw
androID:ID="@+ID/ListMore"
androID:cachecolorHint="#00000000"
androID:layout_wIDth="match_parent"
androID:layout_height="match_parent"/>
</com.chris.lr.slIDemenu.Layoutrelative>
</relativeLayout>
<!-- 右侧菜单导航 -->
<relativeLayout
androID:ID="@+ID/rightLayout"
androID:layout_wIDth="140dip"
androID:layout_height="match_parent"
androID:background="#000000">
<relativeLayout
androID:ID="@+ID/rightTitlebar"
androID:layout_wIDth="match_parent"
androID:layout_height="wrap_content"
androID:background="@color/gold"
androID:padding="5dip">
<TextVIEw
androID:layout_marginleft="5dip"
androID:layout_wIDth="wrap_content"
androID:layout_height="wrap_content"
androID:layout_alignParentleft="true"
androID:layout_centerVertical="true"
androID:text="@string/right_Title"
androID:textSize="20sp"
androID:textcolor="#ffffff"/>
</relativeLayout>

<TextVIEw
androID:text="@string/rightNav"
androID:textcolor="#ff00ff"
androID:textSize="18sp"
androID:layout_wIDth="match_parent"
androID:layout_height="30dip"
androID:layout_below="@ID/rightTitlebar"
androID:background="#000000"/>
</relativeLayout>
</relativeLayout>

布局很简单,我个人比较推荐用relativeLayout,因为这个是几个Layout中,性能最好的,而linearLayout则不好,原因在于,某个子视图的宽高变动,会引起这个布局中其它地方也需要重新调整。

布局中,有com.chris.lr.slIDemenu.Layoutrelative这个自定义控件是继承relativeLayout的,里面只是加了些手势的处理,它的作用实际上就是最开始讲到的,如果含有ListVIEw这类需要判断手势的,则就用到它,先由它来判断,然后在视情况是否拦截由自己来处理。

二、自定义控件:
复制代码 代码如下:
package com.chris.lr.slIDemenu;
import androID.content.Context;
import androID.util.AttributeSet;
import androID.util.Log;
import androID.vIEw.GestureDetector;
import androID.vIEw.GestureDetector.SimpleOnGestureListener;
import androID.vIEw.MotionEvent;
import androID.Widget.relativeLayout;
public class Layoutrelative extends relativeLayout {
private static final String TAG = "ChrisSlIDeMenu";
private GestureDetector mGestureDetector;
private boolean bLockScrollX = false;
private boolean btouchIntercept = false;

private OnScrollListener mOnScrollListenerCallback = null;

public Layoutrelative(Context context) {
this(context,null);
}

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

public Layoutrelative(Context context,AttributeSet attrs,int defStyle) {
super(context,defStyle);

mGestureDetector = new GestureDetector(new LayoutGestureListener());
}

/**
* 设置滚动监听接口
* @param l
*/
public voID setonScrollListener(OnScrollListener l){
mOnScrollListenerCallback = l;
}

/*
* Progress:
* 1. 重载dispatchtouchEvent,将消息传递给GestureDetector;
* 2. 重载手势中onDown 和 onScroll两个函数;
* 3. 在onDown中,默认对水平滚动方向加锁;
* 4. 在onScroll中,判断e1与e2的水平方向与垂直方向距离:
* a. 如果垂直方向大,则表明是上下滚动,且返回false表明当前手势不用拦截;
* b. 如果水平方向大,则表明是左右滚动,且返回true表明当前手势需要拦截;
* 5. 重载onIntercepttouchEvent,如果手势返回为true,则onIntercepttouchEvent也返回true;
* 6. 如果要拦截手势消息,则需要重载ontouchEvent,或子视图中重载这个函数,来处理这条消息;
* 7. 如果自己处理,则对水平方向滚动去锁(表明当前用户想左右滚动);
*
* ----------
* ---------------------- ------>| onDown |
* | | | ----------
* | dispatchtouchEvent | <---- ------ false: 上下滚动
* | | | ------------ /
* ---------------------- ------>| onScroll | ----
* | ------------ \
* | ------ true : 左右滚动
* | intercept = true ----------------
* |----------------------| ontouchEvent |
* | ----------------
* -------------------------
* | |
* | onIntercepttouchEvent |
* | |
* -------------------------
*
*/
@OverrIDe
public boolean dispatchtouchEvent(MotionEvent ev) {
btouchIntercept = mGestureDetector.ontouchEvent(ev);

if(MotionEvent.ACTION_UP == ev.getAction() && !bLockScrollX){
if(mOnScrollListenerCallback != null){
mOnScrollListenerCallback.doOnRelease();
}
}

return super.dispatchtouchEvent(ev);
}

// vIEwgroup.onIntercepttouchEvent
@OverrIDe
public boolean onIntercepttouchEvent(MotionEvent ev) {
super.onIntercepttouchEvent(ev);
return btouchIntercept;
}
// vIEw.ontouchEvent
@OverrIDe
public boolean ontouchEvent(MotionEvent event) {
bLockScrollX = false;
return super.ontouchEvent(event);
}
/**
* @author cheng.yang
*
* 自定义手势监听
*/
public class LayoutGestureListener extends SimpleOnGestureListener {
@OverrIDe
public boolean onDown(MotionEvent e) {
bLockScrollX = true;
return super.onDown(e);
}
@OverrIDe
public boolean onScroll(MotionEvent e1,MotionEvent e2,
float distanceX,float distanceY) {
if(!bLockScrollX){
if(mOnScrollListenerCallback != null){
mOnScrollListenerCallback.doOnScroll(e1,e2,distanceX,distanceY);
}
}

if(Math.abs(e1.getY() - e2.getY()) > Math.abs(e1.getX() - e2.getX())){
return false;
}else{
return true;
}
}
}

public interface OnScrollListener {
voID doOnScroll(MotionEvent e1,float distanceY);
voID doOnRelease();
}
}

这个控件中,含有一个接口,当用户手势为左右时,则需要将滚动数据回传到主视图中去处理,而自己拦截不往下传递消息。里面有个消息流程图,讲的比较详细了,大家可以先看看,有什么问题,可以问我。
三、MainActivity的实现:
这个需要着讲解,毕竟,左、右滑动的实现都在这里。
复制代码 代码如下:
/**
*
* @author cheng.yang
*
* 左、右菜单滑出
*
* params[0]: 滑动距离
* params[1]: 滑动速度,带方向
*/
public class SlIDeMenu extends AsyncTask<Integer,Integer,VoID>{
@OverrIDe
protected VoID doInBackground(Integer... params) {
if(params.length != 2){
Log.e(TAG,"error,params must have 2!");
}
int times = params[0] / Math.abs(params[1]);
if(params[0] % Math.abs(params[1]) != 0){
times ++;
}

for(int i = 0; i < times; i++){
this.publishProgress(params[0],params[1],i+1);
}

return null;
}
@OverrIDe
protected voID onProgressUpdate(Integer... values) {
if(values.length != 3){
Log.e(TAG,values must have 3!");
}
int distance = Math.abs(values[1]) * values[2];
int delta = values[0] - distance;
int leftmargin = 0;
if(values[1] < 0){ // 左移
leftmargin = (delta > 0 ? values[1] : -(Math.abs(values[1]) - Math.abs(delta)));
}else{
leftmargin = (delta > 0 ? values[1] : (Math.abs(values[1]) - Math.abs(delta)));
}

rollLayout(leftmargin);
}

首先,自定义一个继承于AsyncTask的类的线程,这个自定义类,就是用来实现动画效果,重在“滑动”,而不是硬生生的挤出来。关于AsyncTask的用法,大家可以看我的博客中关于它的讲解:
http://blog.csdn.net/qingye_love/article/details/8777508
自定义类的使用需要两个参数,一个是滑动的距离,一个是每次滑动多少且向哪个方向滑动:
1. 滑动距离:若是左侧菜单滑出来,距离就是左侧菜单的宽度;同理,右侧滑出就是右侧菜单的宽度;
2. 滑动速度:即动画滑动时,向哪个方向,且每次滑动多少像素;
在doInBackground中,计算需要滑动多少次,然后用for循环调用publishProgress,实际上就是调用的onProgressUpdate,在onProgressUpdate中,根据方向,以及当前main layout的 leftmargin来计算是滑动指定的距离(速度),还是当终点距离小于滑动速度时,速度就为终点距离,最终,调用rollLayout,来修改 leftLayout,mainLayout,rightLayout三者的布局,达到滑动的效果。
rollLayout的实现:
复制代码 代码如下:
private voID rollLayout(int margin){
relativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
lp.leftmargin += margin;
lp.rightmargin -= margin;
mainLayout.setLayoutParams(lp);
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftmargin += margin;
leftLayout.setLayoutParams(lp);
lp = (LayoutParams) rightLayout.getLayoutParams();
lp.leftmargin += margin;
lp.rightmargin -= margin;
rightLayout.setLayoutParams(lp);
}

这个就是修改三个layout的leftmargin和rightmargin。
初始化布局控件:
复制代码 代码如下:
private voID initVIEw(){
mainLayout = (relativeLayout) findVIEwByID(R.ID.mainLayout);
leftLayout = (relativeLayout) findVIEwByID(R.ID.leftLayout);
rightLayout = (relativeLayout) findVIEwByID(R.ID.rightLayout);
mainLayout.setontouchListener(this);
leftLayout.setontouchListener(this);
rightLayout.setontouchListener(this);

layoutSlIDeMenu = (Layoutrelative) findVIEwByID(R.ID.layoutSlIDeMenu);
layoutSlIDeMenu.setonScrollListener(new OnScrollListener(){
@OverrIDe
public voID doOnScroll(MotionEvent e1,float distanceY) {
onScroll(distanceX);
}

@OverrIDe
public voID doOnRelease(){
onRelease();
}
});

ivMore = (ImageVIEw) findVIEwByID(R.ID.ivMore);
ivSettings = (ImageVIEw) findVIEwByID(R.ID.ivSettings);
ivMore.setontouchListener(this);
ivSettings.setontouchListener(this);

mListMore = (ListVIEw) findVIEwByID(R.ID.ListMore);
mListMore.setAdapter(new ArrayAdapter<String>(this,R.layout.item,R.ID.tv_item,Title));
mListMore.setonItemClickListener(this);

mGestureDetector = new GestureDetector(this);
mGestureDetector.setIsLongpressEnabled(false);

resizeLayout();
}

调整三个layout,将leftLayout移动到屏幕最左边之外,现时将rightLayout移动到屏幕最右边之外:
复制代码 代码如下:
/*
* 使用leftmargin及rightmargin防止layout被挤压变形
* Math.abs(leftmargin - rightmargin) = layout.wIDth
*/
private voID resizeLayout(){
displayMetrics dm = getResources().getdisplayMetrics();

// 固定 main layout,防止被左、右挤压变形
relativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
lp.wIDth = dm.wIDthPixels;
mainLayout.setLayoutParams(lp);

// 将左layout调整至main layout左边
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftmargin = -lp.wIDth;
leftLayout.setLayoutParams(lp);
Log.d(TAG,"left l.margin = " + lp.leftmargin);

// 将左layout调整至main layout右边
lp = (LayoutParams) rightLayout.getLayoutParams();
lp.leftmargin = dm.wIDthPixels;
lp.rightmargin = -lp.wIDth;
rightLayout.setLayoutParams(lp);
Log.d(TAG,"right l.margin = " + lp.leftmargin);
}

重载ontouch,处理消息:
复制代码 代码如下:
////////////////////////////// ontouch ///////////////////////////////
@OverrIDe
public boolean ontouch(VIEw v,MotionEvent event) {
mClickedVIEw = v;

if(MotionEvent.ACTION_UP == event.getAction() && bIsScrolling){
onRelease();
}

return mGestureDetector.ontouchEvent(event);
}

记录选择的vIEw,并将消息传给GestureDetector;
重载GestureDetector的onDown,onScroll,onSingleTapUp这三个是主要的,其它的重载不做任何修改;
复制代码 代码如下:
/////////////////// GestureDetector OverrIDe Begin ///////////////////
@OverrIDe
public boolean onDown(MotionEvent e) {

bIsScrolling = false;
mScroll = 0;
ilimited = 0;
relativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
if(lp.leftmargin > 0){
ilimited = 1;
}else if(lp.leftmargin < 0){
ilimited = -1;
}

return true;
}

在onDown中,判断当前的 mainLayout 的 leftmargin 的值,并用 ilimited 记录下来,原因:
如果当前显示的是左侧菜单,则用户滚动屏幕时,最多只是将左侧菜单滑出到屏幕外,而不会继续滑动,将右侧菜单显示出来;同理,当前已经显示了右侧菜单。
复制代码 代码如下:
@OverrIDe
public boolean onSingleTapUp(MotionEvent e) {
/*
* 正常情况下,mainLayout的leftmargin为0,
* 当左/右菜单为打开中,此时就不为0,需要判断
*/
if(mClickedVIEw != null){
relativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();

if(mClickedVIEw == ivMore){
Log.d(TAG,"[onSingleTapUp] ivMore clicked! leftmargin = " + lp.leftmargin);

if(lp.leftmargin == 0){
new SlIDeMenu().execute(leftLayout.getLayoutParams().wIDth,SPEED);
}else{
new SlIDeMenu().execute(leftLayout.getLayoutParams().wIDth,-SPEED);
}
}else if(mClickedVIEw == ivSettings){
Log.d(TAG,"[onSingleTapUp] ivSettings clicked! leftmargin = " + lp.leftmargin);

if(lp.leftmargin == 0){
new SlIDeMenu().execute(rightLayout.getLayoutParams().wIDth,-SPEED);
}else{
new SlIDeMenu().execute(rightLayout.getLayoutParams().wIDth,SPEED);
}
}else if(mClickedVIEw == mainLayout){
Log.d(TAG,"[onSingleTapUp] mainLayout clicked!");
}
}
return true;
}

这个函数中,处理标题栏左、右两个按钮,点击一次,显示侧导航菜单,再点击一次,则关闭。
复制代码 代码如下:
@OverrIDe
public boolean onScroll(MotionEvent e1,float distanceX,
float distanceY) {
onScroll(distanceX);
return false;
}

滚动处理,直接将本次的水平滚动距离传给onScroll函数来处理,因为,侧边导航菜单的水平滚动也将通过OnScrollListener.doOnScroll来回调,所以,写个通用函数。
复制代码 代码如下:
private voID onScroll(float distanceX){
bIsScrolling = true;
mScroll += distanceX; // 向左为正
Log.d(TAG,"mScroll = " + mScroll + ",distanceX = " + distanceX);

relativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
relativeLayout.LayoutParams lpleft = (LayoutParams) leftLayout.getLayoutParams();
relativeLayout.LayoutParams lpRight = (LayoutParams) rightLayout.getLayoutParams();
Log.d(TAG,"lp.leftmargin = " + lp.leftmargin);

int distance = 0;
if(mScroll > 0){ // 向左移动
if(lp.leftmargin <= 0){ // 打开右导航菜单
if(ilimited > 0){
return;
}
distance = lpRight.wIDth - Math.abs(lp.leftmargin);
}else if(lp.leftmargin > 0){ // 关闭左导航菜单
distance = lp.leftmargin;
}
if(mScroll >= distance){
mScroll = distance;
}
}else if(mScroll < 0){ // 向右移动
if(lp.leftmargin >= 0){ // 打开左导航菜单
if(ilimited < 0){
return;
}
distance = lpleft.wIDth - Math.abs(lp.leftmargin);
}else if(lp.leftmargin < 0){ // 关闭右导航菜单
distance = Math.abs(lp.leftmargin);
}
if(mScroll <= -distance){
mScroll = -distance;
}
}
Log.d(TAG,"mScroll = " + mScroll);
if(mScroll != 0){
rollLayout(-mScroll);
}
}

接下来,我们看看当侧边导航菜单,水平滚动且用户松开手指时,回调OnScrollListener.doOnRelease,我们会调用一个onRelease来负责处理收尾工作:
复制代码 代码如下:
private voID onRelease(){
relativeLayout.LayoutParams lp = (LayoutParams) mainLayout.getLayoutParams();
if(lp.leftmargin < 0){ // 左移
/*
* 左移大于右导航宽度一半,则自动展开,否则自动缩回去
*/
if(Math.abs(lp.leftmargin) > rightLayout.getLayoutParams().wIDth/2){
new SlIDeMenu().execute(rightLayout.getLayoutParams().wIDth - Math.abs(lp.leftmargin),-SPEED);
}else{
new SlIDeMenu().execute(Math.abs(lp.leftmargin),SPEED);
}
}else if(lp.leftmargin > 0){
/*
* 右移大于左导航宽度一半,则自动展开,否则自动缩回去
*/
if(Math.abs(lp.leftmargin) > leftLayout.getLayoutParams().wIDth/2){
new SlIDeMenu().execute(leftLayout.getLayoutParams().wIDth - Math.abs(lp.leftmargin),SPEED);
}else{
new SlIDeMenu().execute(Math.abs(lp.leftmargin),-SPEED);
}
}
}

主要的代码块已经讲解完了,下面是完整的代码:
复制代码 代码如下:
package com.chris.lr.slIDemenu;
import com.chris.lr.slIDemenu.Layoutrelative.OnScrollListener;
import androID.os.AsyncTask;
import androID.os.Bundle;
import androID.util.displayMetrics;
import androID.util.Log;
import androID.vIEw.GestureDetector;
import androID.vIEw.GestureDetector.OnGestureListener;
import androID.vIEw.KeyEvent;
import androID.vIEw.MotionEvent;
import androID.vIEw.VIEw;
import androID.vIEw.VIEw.OntouchListener;
import androID.vIEw.Window;
import androID.Widget.AdapterVIEw;
import androID.Widget.AdapterVIEw.OnItemClickListener;
import androID.Widget.ArrayAdapter;
import androID.Widget.ImageVIEw;
import androID.Widget.ListVIEw;
import androID.Widget.relativeLayout;
import androID.Widget.Toast;
import androID.Widget.relativeLayout.LayoutParams;
import androID.app.Activity;
public class MainActivity extends Activity implements OnGestureListener,
OntouchListener,OnItemClickListener {
private static final String TAG = "ChrisSlIDeMenu";
private relativeLayout mainLayout;
private relativeLayout leftLayout;
private relativeLayout rightLayout;
private Layoutrelative layoutSlIDeMenu;
private ListVIEw mListMore;

private ImageVIEw ivMore;
private ImageVIEw ivSettings;
private GestureDetector mGestureDetector;

private static final int SPEED = 30;
private boolean bIsScrolling = false;
private int ilimited = 0;
private int mScroll = 0;
private VIEw mClickedVIEw = null;

private String Title[] = {"待发送队列",
"同步分享设置",
"编辑我的资料",
"找朋友",
"告诉朋友",
"节省流量",
"推送设置",
"版本更新",
"意见反馈",
"积分兑换",
"精品应用",
"常见问题",
"退出当前帐号",
"退出1",
"退出2",
"退出3",
"退出4"};

@OverrIDe
protected voID onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestwindowFeature(Window.FEATURE_NO_Title);
setContentVIEw(R.layout.activity_main);
initVIEw();
}

private voID initVIEw(){
mainLayout = (relativeLayout) findVIEwByID(R.ID.mainLayout);
leftLayout = (relativeLayout) findVIEwByID(R.ID.leftLayout);
rightLayout = (relativeLayout) findVIEwByID(R.ID.rightLayout);
mainLayout.setontouchListener(this);
leftLayout.setontouchListener(this);
rightLayout.setontouchListener(this);

layoutSlIDeMenu = (Layoutrelative) findVIEwByID(R.ID.layoutSlIDeMenu);
layoutSlIDeMenu.setonScrollListener(new OnScrollListener(){
@OverrIDe
public voID doOnScroll(MotionEvent e1,float distanceY) {
onScroll(distanceX);
}

@OverrIDe
public voID doOnRelease(){
onRelease();
}
});

ivMore = (ImageVIEw) findVIEwByID(R.ID.ivMore);
ivSettings = (ImageVIEw) findVIEwByID(R.ID.ivSettings);
ivMore.setontouchListener(this);
ivSettings.setontouchListener(this);

mListMore = (ListVIEw) findVIEwByID(R.ID.ListMore);
mListMore.setAdapter(new ArrayAdapter<String>(
this,Title));
mListMore.setonItemClickListener(this);

mGestureDetector = new GestureDetector(this);
mGestureDetector.setIsLongpressEnabled(false);

resizeLayout();
}

/*
* 使用leftmargin及rightmargin防止layout被挤压变形
* Math.abs(leftmargin - rightmargin) = layout.wIDth
*/
private voID resizeLayout(){
displayMetrics dm = getResources().getdisplayMetrics();

// 固定 main layout,防止被左、右挤压变形
relativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
lp.wIDth = dm.wIDthPixels;
mainLayout.setLayoutParams(lp);

// 将左layout调整至main layout左边
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftmargin = -lp.wIDth;
leftLayout.setLayoutParams(lp);
Log.d(TAG,"right l.margin = " + lp.leftmargin);
}

@OverrIDe
public boolean onKeyDown(int keyCode,KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0){
relativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();

if(lp.leftmargin != 0){
if(lp.leftmargin > 0){
new SlIDeMenu().execute(
leftLayout.getLayoutParams().wIDth,-SPEED);
}else if(lp.leftmargin < 0){
new SlIDeMenu().execute(
rightLayout.getLayoutParams().wIDth,SPEED);
}
return true;
}
}
return super.onKeyDown(keyCode,event);
}

private voID rollLayout(int margin){
relativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
lp.leftmargin += margin;
lp.rightmargin -= margin;
mainLayout.setLayoutParams(lp);
lp = (LayoutParams) leftLayout.getLayoutParams();
lp.leftmargin += margin;
leftLayout.setLayoutParams(lp);
lp = (LayoutParams) rightLayout.getLayoutParams();
lp.leftmargin += margin;
lp.rightmargin -= margin;
rightLayout.setLayoutParams(lp);
}
private voID onScroll(float distanceX){
bIsScrolling = true;
mScroll += distanceX; // 向左为正
Log.d(TAG,distanceX = " + distanceX);

relativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
relativeLayout.LayoutParams lpleft =
(LayoutParams) leftLayout.getLayoutParams();
relativeLayout.LayoutParams lpRight =
(LayoutParams) rightLayout.getLayoutParams();
Log.d(TAG,"mScroll = " + mScroll);
if(mScroll != 0){
rollLayout(-mScroll);
}
}

private voID onRelease(){
relativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
if(lp.leftmargin < 0){ // 左移
/*
* 左移大于右导航宽度一半,则自动展开,否则自动缩回去
*/
if(Math.abs(lp.leftmargin) > rightLayout.getLayoutParams().wIDth/2){
new SlIDeMenu().execute(rightLayout.getLayoutParams().wIDth -
Math.abs(lp.leftmargin),否则自动缩回去
*/
if(Math.abs(lp.leftmargin) > leftLayout.getLayoutParams().wIDth/2){
new SlIDeMenu().execute(leftLayout.getLayoutParams().wIDth -
Math.abs(lp.leftmargin),-SPEED);
}
}
}
///////////////////// ListVIEw.onItemClick ///////////////////////
@OverrIDe
public voID onItemClick(AdapterVIEw<?> arg0,VIEw arg1,int arg2,long arg3) {
Toast.makeText(this,Title[arg2],Toast.LENGTH_SHORT).show();
}

////////////////////////////// ontouch ///////////////////////////////
@OverrIDe
public boolean ontouch(VIEw v,MotionEvent event) {
mClickedVIEw = v;

if(MotionEvent.ACTION_UP == event.getAction() && bIsScrolling){
onRelease();
}

return mGestureDetector.ontouchEvent(event);
}

/////////////////// GestureDetector OverrIDe Begin ///////////////////
@OverrIDe
public boolean onDown(MotionEvent e) {

bIsScrolling = false;
mScroll = 0;
ilimited = 0;
relativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();
if(lp.leftmargin > 0){
ilimited = 1;
}else if(lp.leftmargin < 0){
ilimited = -1;
}

return true;
}
@OverrIDe
public boolean onFling(MotionEvent arg0,MotionEvent arg1,float arg2,
float arg3) {
return false;
}
@OverrIDe
public voID onLongPress(MotionEvent e) {

}
@OverrIDe
public boolean onScroll(MotionEvent e1,
float distanceY) {
onScroll(distanceX);
return false;
}
@OverrIDe
public voID onShowPress(MotionEvent arg0) {

}
@OverrIDe
public boolean onSingleTapUp(MotionEvent e) {
/*
* 正常情况下,mainLayout的leftmargin为0,
* 当左/右菜单为打开中,此时就不为0,需要判断
*/
if(mClickedVIEw != null){
relativeLayout.LayoutParams lp =
(LayoutParams) mainLayout.getLayoutParams();

if(mClickedVIEw == ivMore){
Log.d(TAG,"[onSingleTapUp] ivMore clicked! leftmargin = "
+ lp.leftmargin);

if(lp.leftmargin == 0){
new SlIDeMenu().execute(
leftLayout.getLayoutParams().wIDth,SPEED);
}else{
new SlIDeMenu().execute(
leftLayout.getLayoutParams().wIDth,"[onSingleTapUp] ivSettings clicked! leftmargin = "
+ lp.leftmargin);

if(lp.leftmargin == 0){
new SlIDeMenu().execute(
rightLayout.getLayoutParams().wIDth,-SPEED);
}else{
new SlIDeMenu().execute(
rightLayout.getLayoutParams().wIDth,"[onSingleTapUp] mainLayout clicked!");
}
}
return true;
}
/////////////////// GestureDetector OverrIDe End ///////////////////

/**
*
* @author cheng.yang
*
* 左、右菜单滑出
*
* params[0]: 滑动距离
* params[1]: 滑动速度,values must have 3!");
}
int distance = Math.abs(values[1]) * values[2];
int delta = values[0] - distance;
int leftmargin = 0;
if(values[1] < 0){ // 左移
leftmargin = (delta > 0 ? values[1] :
-(Math.abs(values[1]) - Math.abs(delta)));
}else{
leftmargin = (delta > 0 ? values[1] :
(Math.abs(values[1]) - Math.abs(delta)));
}

rollLayout(leftmargin);
}
}
}

总结:
本文左,右滑出菜单的原理,就是用到了leftmargin和rightmargin两个属性,并配合几个GestureDetector来完全手势判断的逻辑处理,其中,自定义的那个控件布局Layoutrelative,可以用在任何子视图中需要处理上下,左右滚动冲突的地方,良好的解决了冲突问题。
完整的代码下载地址:
http://download.csdn.net/detail/qingye_love/5237799
希望大家多来聊聊,同时,大家可以在我的基础上,实现onFling的方法,欢迎交流。 您可能感兴趣的文章:安卓(Android)实现3DTouch效果android底部菜单栏实现原理与代码android popwindow实现左侧d出菜单层及PopupWindow主要方法介绍基于Android实现点击某个按钮让菜单选项从按钮周围指定位置d出Android ListView长按d出菜单二种实现方式示例@L_403_18@Android开发技巧之我的菜单我做主(自定义菜单)Android仿QQ空间底部菜单示例代码Android实现原生侧滑菜单的超简单方式Android实现类似3D Touch菜单功能 总结

以上是内存溢出为你收集整理的Android左右滑出菜单实例分析全部内容,希望文章能够帮你解决Android左右滑出菜单实例分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1142896.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存