返回顶部
首页 > 资讯 > 移动开发 >Android左右滑出菜单实例分析
  • 224
分享到

Android左右滑出菜单实例分析

菜单Android 2022-06-06 10:06:29 224人浏览 泡泡鱼
摘要

现在的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/rig htLayout"
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, attrs, defStyle);
mGestureDetector = new GestureDetector(new LayoutGestureListener());
}

public void setOnScrollListener(OnScrollListener l){
mOnScrollListenerCallback = l;
}

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

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, MotionEvent e2,
float distanceX, float distanceY);
void doOnRelease();
}
}

这个控件中,含有一个接口,当用户手势为左右时,则需要将滚动数据回传到主视图中去处理,而自己拦截不往下传递消息。里面有个消息流程图,讲的比较详细了,大家可以先看看,有什么问题,可以问我。
三、MainActivity的实现:
这个需要着讲解,毕竟,左、右滑动的实现都在这里。
代码如下:

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, "error, 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, MotionEvent e2,
float distanceX, 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移动到屏幕最右边之外:
代码如下:

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) {

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, MotionEvent e2, 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, MotionEvent e2,
float distanceX, 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();
}

private void resizeLayout(){
DisplayMetrics dm = getResources().getDisp layMetrics();
// 固定 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);
}
@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, "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);
}
}
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);
}
}
}
///////////////////// 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, MotionEvent e2, float distanceX,
float distanceY) {
onScroll(distanceX);
return false;
}
@Override
public void onShowPress(MotionEvent arg0) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {

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;
}
/////////////////// GestureDetector Override End ///////////////////

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, "error, 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底部菜单栏实现原理与代码android popwindow实现左侧弹出菜单层及PopupWindow主要方法介绍基于Android实现点击某个按钮让菜单选项从按钮周围指定位置弹出Android ListView长按弹出菜单二种实现方式示例Android界面设计(APP设计趋势 左侧隐藏菜单右边显示content)Android开发技巧之我的菜单我做主(自定义菜单)Android仿QQ空间底部菜单示例代码Android实现原生侧滑菜单的超简单方式Android之用PopupWindow实现弹出菜单的方法详解Android实现爆炸式菜单按钮弹出效果


--结束END--

本文标题: Android左右滑出菜单实例分析

本文链接: https://lsjlt.com/news/27599.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Android左右滑出菜单实例分析
    现在的Android应用,只要有一个什么新的创意,过不了多久,几乎所有的应用都带这个创意。这不,咱们公司最近的一个持续性的项目,想在首页加个从左滑动出来的菜单,我查阅网上资料,...
    99+
    2022-06-06
    菜单 Android
  • Android怎么实现菜单左右滑动
    在Android中,实现菜单左右滑动可以通过使用ViewPager和Fragment来实现。下面是实现的步骤:1. 在布局文件中,添...
    99+
    2023-09-28
    Android
  • Android实现顶部导航菜单左右滑动效果
    本文给大家介绍在Android中如何实现顶部导航菜单左右滑动效果,具体内容如下 第一种解决方案: 实现原理是使用android-support-v4.jar包中ViewPag...
    99+
    2022-06-06
    导航菜单 菜单 动效 Android
  • Android实现左侧滑动菜单
    本文实例为大家分享了Android实现左侧滑动菜单的具体代码,供大家参考,具体内容如下 效果图: SlideActivity.java: package com.demo.slid...
    99+
    2024-04-02
  • android自定义左侧滑出菜单效果
    这里给大家提供一个类似QQ聊天那种可以左侧滑出菜单的自定义控件。希望对大家有帮助。参考了一些网友的做法,自己整理优化了一下,用法非常简单,就一个类,不需要自己写任何的代码,只要添加上...
    99+
    2024-04-02
  • Android PopupWindow实现右侧、左侧和底部弹出菜单
    本教程为大家分享了Android PopupWindow弹出菜单的具体代码,供大家参考,具体内容如下 项目代码:http://xiazai.jb51.net/20161...
    99+
    2022-06-06
    菜单 弹出菜单 popupwindow Android
  • 如何使用android实现左右侧滑菜单效果的方法
    这篇文章主要介绍了如何使用android实现左右侧滑菜单效果的方法,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在android开发中,左右侧滑菜单的开发已成为我们现在开发的...
    99+
    2023-05-30
    android
  • html如何实现左右菜单左右互换
    这篇文章将为大家详细讲解有关html如何实现左右菜单左右互换,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。<meta charset="UTF-...
    99+
    2024-04-02
  • 代码分析Android实现侧滑菜单
    Android 侧滑菜单的实现,参考网上的代码,实现侧滑菜单。最重要的是这个动画类UgcAnimations,如何使用动画类来侧滑的封装FlipperLayout。 1、实现效...
    99+
    2022-06-06
    菜单 Android
  • Android三种菜单实例分析
    本文实例讲述了Android的三种菜单。分享给大家供大家参考。具体分析如下: Android的菜单分为三种类型:选项菜单(Option Menu)、上下文菜单(Context ...
    99+
    2022-06-06
    菜单 Android
  • Android实现图片左右滑动效果
    关于滑动效果,在Android中用得比较多,本示例实现的滑动效果是使用ViewFlipper来实现的,当然也可以使用其它的View来实现。接下来就让我们开始实现这种效果。 接...
    99+
    2022-06-06
    图片 动效 Android
  • Android实现左右滑动切换图片
    简要说明 本文采用ImageSwitcher实现左右滑动切换图片。首先调用setFactory方法,设置视图工厂;然后设置手指触碰监听,判断左滑右滑进而切换图片。 本地图片 xml ...
    99+
    2024-04-02
  • Android选项菜单用法实例分析
    本文实例讲述了Android选项菜单用法。分享给大家供大家参考。具体如下: Android平台下所提供的菜单大体上可分为三类:选项菜单、上下文菜单和子菜单。 当Activity...
    99+
    2022-06-06
    菜单 Android
  • HTML如何实现移动端手指操控左右滑动的菜单
    这篇文章主要介绍HTML如何实现移动端手指操控左右滑动的菜单,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体内容如下<!DOCTYPE html> ...
    99+
    2024-04-02
  • Android开发之开发者头条(二)实现左滑菜单
    在上篇文章给大家介绍了Android开发之开发者头条(一)启动页实现,感兴趣的朋友可以参考下。 title: 带你实现开发者头条(二) 实现左滑菜单 tags: 左滑菜单,a...
    99+
    2022-06-06
    开发者 菜单 android开发 Android
  • Android组件banner实现左右滑屏效果
    什么是banner组件?在许多Android应用上,比如爱奇艺客户端、百度美拍、应用宝等上面,都有一个可以手动滑动的小广告条,这就是banner,实际应用中的banner,其信...
    99+
    2022-06-06
    Android
  • Android自定义View实现左右滑动选择出生年份
    自定义view的第三篇,模仿的是微博运动界面的个人出生日期设置view,先看看我的效果图: 支持设置初始年份,左右滑动选择出生年份,对应的TextView的值也会改变。这...
    99+
    2022-06-06
    view 选择 Android
  • Android上下文菜单用法实例分析
    本文实例讲述了Android上下文菜单用法。分享给大家供大家参考。具体如下: 上下文菜单不同于选项菜单,选项菜单服务于Activity,而上下文菜单则是注册到某个View对象上...
    99+
    2022-06-06
    菜单 Android
  • vue实现左右滑动效果实例代码
    前言 个人实际开发中用到的效果问题总结出来便于自己以后开发查看调用,如果也适用其他人请随意拿走勿喷就行! vue.js是现在流行的js框架之一,vue 是一套用于构建用户界面的渐进式...
    99+
    2024-04-02
  • vue实现右键弹出菜单
    在最近工作中,有一个需求,需要做一个表格,并且对该表格右键弹出菜单支持增删改,这里做下总结,功能截图如下: 首先定义菜单结构 <!-- 菜单 --> <div c...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作