精华内容
下载资源
问答
  • 滑动菜单

    2014-07-18 21:04:45
    滑动菜单
  • 滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单滑动菜单...
  • 之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章Android仿人人客户端滑动菜单的侧滑特效实现代码,史上最简单的侧滑...
  • 之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章 Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8744400


    之前我向大家介绍了史上最简单的滑动菜单的实现方式,相信大家都还记得。如果忘记了其中的实现原理或者还没看过的朋友,请先去看一遍之前的文章 Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现 ,因为我们今天要实现的滑动菜单框架也是基于同样的原理的。


    之前的文章中在最后也提到了,如果是你的应用程序中有很多个Activity都需要加入滑动菜单的功能,那么每个Activity都要写上百行的代码才能实现效果,再简单的滑动菜单实现方案也没用。因此我们今天要实现一个滑动菜单的框架,然后在任何Activity中都可以一分钟引入滑动菜单功能。


    首先还是讲一下实现原理。说是滑动菜单的框架,其实说白了也很简单,就是我们自定义一个布局,在这个自定义布局中实现好滑动菜单的功能,然后只要在Activity的布局文件里面引入我们自定义的布局,这个Activity就拥有了滑动菜单的功能了。原理讲完了,是不是很简单?下面我们来动手实现吧。


    在Eclipse中新建一个Android项目,项目名就叫做RenRenSlidingLayout。


    新建一个类,名叫SlidingLayout,这个类是继承自LinearLayout的,并且实现了OnTouchListener接口,具体代码如下:

    public class SlidingLayout extends LinearLayout implements OnTouchListener {
    
    	/**
    	 * 滚动显示和隐藏左侧布局时,手指滑动需要达到的速度。
    	 */
    	public static final int SNAP_VELOCITY = 200;
    
    	/**
    	 * 屏幕宽度值。
    	 */
    	private int screenWidth;
    
    	/**
    	 * 左侧布局最多可以滑动到的左边缘。值由左侧布局的宽度来定,marginLeft到达此值之后,不能再减少。
    	 */
    	private int leftEdge;
    
    	/**
    	 * 左侧布局最多可以滑动到的右边缘。值恒为0,即marginLeft到达0之后,不能增加。
    	 */
    	private int rightEdge = 0;
    
    	/**
    	 * 左侧布局完全显示时,留给右侧布局的宽度值。
    	 */
    	private int leftLayoutPadding = 80;
    
    	/**
    	 * 记录手指按下时的横坐标。
    	 */
    	private float xDown;
    
    	/**
    	 * 记录手指移动时的横坐标。
    	 */
    	private float xMove;
    
    	/**
    	 * 记录手机抬起时的横坐标。
    	 */
    	private float xUp;
    
    	/**
    	 * 左侧布局当前是显示还是隐藏。只有完全显示或隐藏时才会更改此值,滑动过程中此值无效。
    	 */
    	private boolean isLeftLayoutVisible;
    
    	/**
    	 * 左侧布局对象。
    	 */
    	private View leftLayout;
    
    	/**
    	 * 右侧布局对象。
    	 */
    	private View rightLayout;
    
    	/**
    	 * 用于监听侧滑事件的View。
    	 */
    	private View mBindView;
    
    	/**
    	 * 左侧布局的参数,通过此参数来重新确定左侧布局的宽度,以及更改leftMargin的值。
    	 */
    	private MarginLayoutParams leftLayoutParams;
    
    	/**
    	 * 右侧布局的参数,通过此参数来重新确定右侧布局的宽度。
    	 */
    	private MarginLayoutParams rightLayoutParams;
    
    	/**
    	 * 用于计算手指滑动的速度。
    	 */
    	private VelocityTracker mVelocityTracker;
    
    	/**
    	 * 重写SlidingLayout的构造函数,其中获取了屏幕的宽度。
    	 * 
    	 * @param context
    	 * @param attrs
    	 */
    	public SlidingLayout(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    		screenWidth = wm.getDefaultDisplay().getWidth();
    	}
    
    	/**
    	 * 绑定监听侧滑事件的View,即在绑定的View进行滑动才可以显示和隐藏左侧布局。
    	 * 
    	 * @param bindView
    	 *            需要绑定的View对象。
    	 */
    	public void setScrollEvent(View bindView) {
    		mBindView = bindView;
    		mBindView.setOnTouchListener(this);
    	}
    
    	/**
    	 * 将屏幕滚动到左侧布局界面,滚动速度设定为30.
    	 */
    	public void scrollToLeftLayout() {
    		new ScrollTask().execute(30);
    	}
    
    	/**
    	 * 将屏幕滚动到右侧布局界面,滚动速度设定为-30.
    	 */
    	public void scrollToRightLayout() {
    		new ScrollTask().execute(-30);
    	}
    
    	/**
    	 * 左侧布局是否完全显示出来,或完全隐藏,滑动过程中此值无效。
    	 * 
    	 * @return 左侧布局完全显示返回true,完全隐藏返回false。
    	 */
    	public boolean isLeftLayoutVisible() {
    		return isLeftLayoutVisible;
    	}
    
    	/**
    	 * 在onLayout中重新设定左侧布局和右侧布局的参数。
    	 */
    	@Override
    	protected void onLayout(boolean changed, int l, int t, int r, int b) {
    		super.onLayout(changed, l, t, r, b);
    		if (changed) {
    			// 获取左侧布局对象
    			leftLayout = getChildAt(0);
    			leftLayoutParams = (MarginLayoutParams) leftLayout.getLayoutParams();
    			// 重置左侧布局对象的宽度为屏幕宽度减去leftLayoutPadding
    			leftLayoutParams.width = screenWidth - leftLayoutPadding;
    			// 设置最左边距为负的左侧布局的宽度
    			leftEdge = -leftLayoutParams.width;
    			leftLayoutParams.leftMargin = leftEdge;
    			leftLayout.setLayoutParams(leftLayoutParams);
    			// 获取右侧布局对象
    			rightLayout = getChildAt(1);
    			rightLayoutParams = (MarginLayoutParams) rightLayout.getLayoutParams();
    			rightLayoutParams.width = screenWidth;
    			rightLayout.setLayoutParams(rightLayoutParams);
    		}
    	}
    
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		createVelocityTracker(event);
    		switch (event.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			// 手指按下时,记录按下时的横坐标
    			xDown = event.getRawX();
    			break;
    		case MotionEvent.ACTION_MOVE:
    			// 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整左侧布局的leftMargin值,从而显示和隐藏左侧布局
    			xMove = event.getRawX();
    			int distanceX = (int) (xMove - xDown);
    			if (isLeftLayoutVisible) {
    				leftLayoutParams.leftMargin = distanceX;
    			} else {
    				leftLayoutParams.leftMargin = leftEdge + distanceX;
    			}
    			if (leftLayoutParams.leftMargin < leftEdge) {
    				leftLayoutParams.leftMargin = leftEdge;
    			} else if (leftLayoutParams.leftMargin > rightEdge) {
    				leftLayoutParams.leftMargin = rightEdge;
    			}
    			leftLayout.setLayoutParams(leftLayoutParams);
    			break;
    		case MotionEvent.ACTION_UP:
    			// 手指抬起时,进行判断当前手势的意图,从而决定是滚动到左侧布局,还是滚动到右侧布局
    			xUp = event.getRawX();
    			if (wantToShowLeftLayout()) {
    				if (shouldScrollToLeftLayout()) {
    					scrollToLeftLayout();
    				} else {
    					scrollToRightLayout();
    				}
    			} else if (wantToShowRightLayout()) {
    				if (shouldScrollToContent()) {
    					scrollToRightLayout();
    				} else {
    					scrollToLeftLayout();
    				}
    			}
    			recycleVelocityTracker();
    			break;
    		}
    		return isBindBasicLayout();
    	}
    
    	/**
    	 * 判断当前手势的意图是不是想显示右侧布局。如果手指移动的距离是负数,且当前左侧布局是可见的,则认为当前手势是想要显示右侧布局。
    	 * 
    	 * @return 当前手势想显示右侧布局返回true,否则返回false。
    	 */
    	private boolean wantToShowRightLayout() {
    		return xUp - xDown < 0 && isLeftLayoutVisible;
    	}
    
    	/**
    	 * 判断当前手势的意图是不是想显示左侧布局。如果手指移动的距离是正数,且当前左侧布局是不可见的,则认为当前手势是想要显示左侧布局。
    	 * 
    	 * @return 当前手势想显示左侧布局返回true,否则返回false。
    	 */
    	private boolean wantToShowLeftLayout() {
    		return xUp - xDown > 0 && !isLeftLayoutVisible;
    	}
    
    	/**
    	 * 判断是否应该滚动将左侧布局展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,
    	 * 就认为应该滚动将左侧布局展示出来。
    	 * 
    	 * @return 如果应该滚动将左侧布局展示出来返回true,否则返回false。
    	 */
    	private boolean shouldScrollToLeftLayout() {
    		return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY;
    	}
    
    	/**
    	 * 判断是否应该滚动将右侧布局展示出来。如果手指移动距离加上leftLayoutPadding大于屏幕的1/2,
    	 * 或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将右侧布局展示出来。
    	 * 
    	 * @return 如果应该滚动将右侧布局展示出来返回true,否则返回false。
    	 */
    	private boolean shouldScrollToContent() {
    		return xDown - xUp + leftLayoutPadding > screenWidth / 2
    				|| getScrollVelocity() > SNAP_VELOCITY;
    	}
    
    	/**
    	 * 判断绑定滑动事件的View是不是一个基础layout,不支持自定义layout,只支持四种基本layout,
    	 * AbsoluteLayout已被弃用。
    	 * 
    	 * @return 如果绑定滑动事件的View是LinearLayout,RelativeLayout,FrameLayout,
    	 *         TableLayout之一就返回true,否则返回false。
    	 */
    	private boolean isBindBasicLayout() {
    		if (mBindView == null) {
    			return false;
    		}
    		String viewName = mBindView.getClass().getName();
    		return viewName.equals(LinearLayout.class.getName())
    				|| viewName.equals(RelativeLayout.class.getName())
    				|| viewName.equals(FrameLayout.class.getName())
    				|| viewName.equals(TableLayout.class.getName());
    	}
    
    	/**
    	 * 创建VelocityTracker对象,并将触摸事件加入到VelocityTracker当中。
    	 * 
    	 * @param event
    	 *            右侧布局监听控件的滑动事件
    	 */
    	private void createVelocityTracker(MotionEvent event) {
    		if (mVelocityTracker == null) {
    			mVelocityTracker = VelocityTracker.obtain();
    		}
    		mVelocityTracker.addMovement(event);
    	}
    
    	/**
    	 * 获取手指在右侧布局的监听View上的滑动速度。
    	 * 
    	 * @return 滑动速度,以每秒钟移动了多少像素值为单位。
    	 */
    	private int getScrollVelocity() {
    		mVelocityTracker.computeCurrentVelocity(1000);
    		int velocity = (int) mVelocityTracker.getXVelocity();
    		return Math.abs(velocity);
    	}
    
    	/**
    	 * 回收VelocityTracker对象。
    	 */
    	private void recycleVelocityTracker() {
    		mVelocityTracker.recycle();
    		mVelocityTracker = null;
    	}
    
    	class ScrollTask extends AsyncTask<Integer, Integer, Integer> {
    
    		@Override
    		protected Integer doInBackground(Integer... speed) {
    			int leftMargin = leftLayoutParams.leftMargin;
    			// 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。
    			while (true) {
    				leftMargin = leftMargin + speed[0];
    				if (leftMargin > rightEdge) {
    					leftMargin = rightEdge;
    					break;
    				}
    				if (leftMargin < leftEdge) {
    					leftMargin = leftEdge;
    					break;
    				}
    				publishProgress(leftMargin);
    				// 为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动动画。
    				sleep(20);
    			}
    			if (speed[0] > 0) {
    				isLeftLayoutVisible = true;
    			} else {
    				isLeftLayoutVisible = false;
    			}
    			return leftMargin;
    		}
    
    		@Override
    		protected void onProgressUpdate(Integer... leftMargin) {
    			leftLayoutParams.leftMargin = leftMargin[0];
    			leftLayout.setLayoutParams(leftLayoutParams);
    		}
    
    		@Override
    		protected void onPostExecute(Integer leftMargin) {
    			leftLayoutParams.leftMargin = leftMargin;
    			leftLayout.setLayoutParams(leftLayoutParams);
    		}
    	}
    
    	/**
    	 * 使当前线程睡眠指定的毫秒数。
    	 * 
    	 * @param millis
    	 *            指定当前线程睡眠多久,以毫秒为单位
    	 */
    	private void sleep(long millis) {
    		try {
    			Thread.sleep(millis);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    }

    看到这里,我相信大家一定会觉得这些代码非常熟悉。没错,基本上这些代码和之前那篇文章的代码大同小异,只不过以前这些代码是写在Activity里的,而现在我们移动到了自定义的View当中。


    接着我来说明一下和以前不同的部分。我们可以看到,这里将onLayout方法进行了重写,使用getChildAt(0)获取到的布局作为左边布局,使用getChildAt(1)获取到的布局作为右边布局。并将左边布局的宽度重定义为屏幕宽度减去leftLayoutPadding,将右侧布局的宽度重定义为屏幕宽度。然后让左边布局偏移出屏幕,这样能看到的就只有右边布局了。因此在这里我们也可以看出,使用SlidingLayout这个布局的前提条件,必须为这个布局提供两个子元素,第一个元素会作为左边布局偏移出屏幕,第二个元素会作为右边布局显示在屏幕上。


    然后我们看一下setScrollEvent方法,这个方法接收一个View作为参数,然后为这个View绑定了一个touch事件。这是什么意思呢?让我们来想象一个场景,如果右侧布局是一个LinearLayout,我可以通过监听LinearLayout上的touch事件来控制左侧布局的显示和隐藏。但是如果右侧布局的LinearLayout里面加入了一个ListView,而这个ListView又充满了整个LinearLayout,这个时候LinearLayout将不可能再被touch到了,这个时候我们就需要将touch事件注册到ListView上。setScrollEvent方法也就是提供了一个注册接口,touch事件将会注册到传入的View上。


    最后还有一个陌生的方法,isBindBasicLayout。这个方法就是判断了一下注册touch事件的View是不是四个基本布局之一,如果是就返回true,否则返回false。这个方法在整个SlidingLayout中起着非常重要的作用,主要用于控制onTouch事件是返回true还是false,这将影响到布局当中的View的功能是否可用。由于里面牵扯到了Android的事件转发机制,内容比较多,就不在这里详细解释了,我会考虑以后专门写一篇文章来介绍Android的事件机制。这里就先简单记住如果是基本布局就返回true,否则就返回false。


    好了,我们的SlidingLayout写完了,接下来就是见证奇迹的时刻,让我们一起看看如何一分钟在Activity中引入滑动菜单功能。


    创建或打开layout目录下的activity_main.xml文件,加入如下代码:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal"
        tools:context=".MainActivity" >
    
        <!-- 使用自定义的侧滑布局,orientation必须为水平方向 -->
    
        <com.example.slide.SlidingLayout
            android:id="@+id/slidingLayout"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="horizontal" >
    
            <!--
    	        侧滑布局的根节点下,有且只能有两个子元素,这两个子元素必须是四种基本布局之一,
    	        即LinearLayout, RelativeLayout, FrameLayout或TableLayout。
    	        第一个子元素将做为左侧布局,初始化后被隐藏。第二个子元素将做为右侧布局,
    	        也就是当前Activity的主布局,将主要的数据放在里面。
            -->
    
            <RelativeLayout
                android:id="@+id/menu"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:background="#00ccff" >
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:text="This is menu"
                    android:textColor="#000000"
                    android:textSize="28sp" />
            </RelativeLayout>
    
            <LinearLayout
                android:id="@+id/content"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:orientation="vertical" >
    
                <Button
                    android:id="@+id/menuButton"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Menu" />
    
                <ListView
                    android:id="@+id/contentList"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent" >
                </ListView>
            </LinearLayout>
        </com.example.slide.SlidingLayout>
    
    </LinearLayout>

    我们可以看到,在根布局的下面,我们引入了自定义布局com.example.slide.SlidingLayout,然后在它里面加入了两个子元素,一个RelativeLayout和一个LinearLayout。RelativeLayout中比较简单,就加入了一个TextView。LinearLayout里面我们加入了一个按钮和一个ListView。


    然后创建或打开MainActivity作为程序的主Activity,加入代码:

    public class MainActivity extends Activity {
    
    	/**
    	 * 侧滑布局对象,用于通过手指滑动将左侧的菜单布局进行显示或隐藏。
    	 */
    	private SlidingLayout slidingLayout;
    
    	/**
    	 * menu按钮,点击按钮展示左侧布局,再点击一次隐藏左侧布局。
    	 */
    	private Button menuButton;
    
    	/**
    	 * 放在content布局中的ListView。
    	 */
    	private ListView contentListView;
    
    	/**
    	 * 作用于contentListView的适配器。
    	 */
    	private ArrayAdapter<String> contentListAdapter;
    
    	/**
    	 * 用于填充contentListAdapter的数据源。
    	 */
    	private String[] contentItems = { "Content Item 1", "Content Item 2", "Content Item 3",
    			"Content Item 4", "Content Item 5", "Content Item 6", "Content Item 7",
    			"Content Item 8", "Content Item 9", "Content Item 10", "Content Item 11",
    			"Content Item 12", "Content Item 13", "Content Item 14", "Content Item 15",
    			"Content Item 16" };
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		slidingLayout = (SlidingLayout) findViewById(R.id.slidingLayout);
    		menuButton = (Button) findViewById(R.id.menuButton);
    		contentListView = (ListView) findViewById(R.id.contentList);
    		contentListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
    				contentItems);
    		contentListView.setAdapter(contentListAdapter);
    		// 将监听滑动事件绑定在contentListView上
    		slidingLayout.setScrollEvent(contentListView);
    		menuButton.setOnClickListener(new OnClickListener() {
    			@Override
    			public void onClick(View v) {
    				// 实现点击一下menu展示左侧布局,再点击一下隐藏左侧布局的功能
    				if (slidingLayout.isLeftLayoutVisible()) {
    					slidingLayout.scrollToRightLayout();
    				} else {
    					slidingLayout.scrollToLeftLayout();
    				}
    			}
    		});
    	}
    
    }

    上述代码重点是调用SlidingLayout的setScrollEvent方法,为ListView注册touch事件。同时给按钮添加了一个点击事件,实现了点击一下显示左边布局,再点击一下隐藏左边布局的功能。


    最后还是老规矩,给出AndroidManifest.xml的代码:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.slide"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="8"
            android:targetSdkVersion="8" />
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.NoTitleBar" >
            <activity
                android:name="com.example.slide.MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    好了,现在让我们运行一下吧。首先是程序打开的时候,显示的是右边布局。用手指在界面上向右滑动,可以看到左边布局出现。


                    


    而当左边布局完全显示的时候,效果图如下:


                               


    除此之外,点击Menu按钮也可以控制左边布局的显示和隐藏,大家可以自己试一下。


    使用自定义布局的话,就可以用简单的方式在任意Activity中加入滑动菜单功能,即使你有再多的Activity也不用怕了,一分钟引入滑动菜单妥妥的。


    再总结一下吧,向Activity中加入滑动菜单功能只需要两步:

    1. 在Acitivty的layout中引入我们自定义的布局,并且给这个布局要加入两个直接子元素。

    2. 在Activity中通过setScrollEvent方法,给一个View注册touch事件。


    好了,今天的讲解到此结束,有疑问的朋友请在下面留言。


    源码下载,请点击这里


    补充:

    由于这段文章写的比较早了,那时写的滑动菜单还存在着不少问题,我之后又将上面的代码做了不少改动,编写了一个修正版的滑动菜单,这个版本主要修正了以下内容:


    1.将滑动方式改成了覆盖型。

    2.ListView上下滚动时不会轻易滑出菜单。

    3.正在滑动时屏蔽掉内容布局上的事件。

    4.当菜单布局展示时,点击一下右侧的内容布局,可以将菜单隐藏。

    5.修复刚打开程序时,菜单可能会短暂显示一下,然后瞬间消失的bug。


    修正版源码下载,请点击这里


    另外,有对双向滑动菜单感兴趣的朋友请转阅  Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效


    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • 弹性滑动菜单

    2021-06-04 05:37:58
    弹性滑动菜单
  • 滑动菜单javascript

    2013-03-16 21:56:04
    滑动菜单
  • 滑动和滑动菜单 与库一起使用的滑动菜单。 在线演示 访问。 也使用它! 非常酷! 与其他滑杆有什么区别? 该插件使用CSS3 transform属性移动导航。 这就是为什么它可以顺利运行的原因。 因为。 仅1个js文件(+ ...
  • CSS动态滑动菜单CSS动态滑动菜单CSS动态滑动菜单CSS动态滑动菜单CSS动态滑动菜单CSS动态滑动菜单CSS动态滑动菜单
  • 左侧滑动菜单

    2017-03-27 15:51:28
    左侧滑动菜单
  • 仿qq滑动菜单

    2017-09-06 15:06:37
    滑动菜单,Android 仿QQ滑动菜单栏仿照QQ完成的滑动菜单栏,Android 仿QQ滑动菜单栏仿照QQ完成的滑动菜单
  • android 滑动菜单SlidingMenu的实现

    万次阅读 多人点赞 2012-10-24 12:09:00
    * 滑动菜单 * * @author jjhappyforever... * */ public class MainActivity extends Activity implements OnTouchListener, GestureDetector.OnGestureListener { private boolean hasMeasured = false;/...

    首先我们看下面视图:

          

    这种效果大家都不陌生,网上好多都说是仿人人网的,估计人家牛逼出来的早吧,我也参考了一一些例子,实现起来有三种方法,我下面简单介绍下:

    方法一:其实就是对GestureDetector手势的应用及布局文件的设计.

    布局文件main.xml    采用RelativeLayout布局.

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
    
        <LinearLayout
            android:id="@+id/layout_right"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_marginLeft="50dp"
            android:orientation="vertical" >
    
            <AbsoluteLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:background="@color/grey21"
                android:padding="10dp" >
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="设置"
                    android:textColor="@android:color/background_light"
                    android:textSize="20sp" />
            </AbsoluteLayout>
    
            <ListView
                android:id="@+id/lv_set"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_weight="1" >
            </ListView>
        </LinearLayout>
    
        <LinearLayout
            android:id="@+id/layout_left"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@color/white"
            android:orientation="vertical" >
    
            <RelativeLayout
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/nav_bg" >
    
                <ImageView
                    android:id="@+id/iv_set"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:layout_alignParentTop="true"
                    android:src="@drawable/nav_setting" />
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:text="我"
                    android:textColor="@android:color/background_light"
                    android:textSize="20sp" />
            </RelativeLayout>
    
            <ImageView
                android:id="@+id/iv_set"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:scaleType="fitXY"
                android:src="@drawable/bg_guide_5" />
        </LinearLayout>
    
    </RelativeLayout>

    layout_right:这个大布局文件,layout_left:距离左边50dp像素.(我们要移动的是layout_left).

    看到这个图我想大家都很清晰了吧,其实:我们就是把layout_left这个布局控件整理向左移动,至于移动多少,就要看layout_right有多宽了。layout_left移动到距离左边的边距就是layout_right的宽及-MAX_WIDTH.相信大家都理解.

    布局文件就介绍到这里,下面看代码.

    /***
    	 * 初始化view
    	 */
    	void InitView() {
    		layout_left = (LinearLayout) findViewById(R.id.layout_left);
    		layout_right = (LinearLayout) findViewById(R.id.layout_right);
    		iv_set = (ImageView) findViewById(R.id.iv_set);
    		lv_set = (ListView) findViewById(R.id.lv_set);
    		lv_set.setAdapter(new ArrayAdapter<String>(this, R.layout.item,
    				R.id.tv_item, title));
    		lv_set.setOnItemClickListener(new OnItemClickListener() {
    
    			@Override
    			public void onItemClick(AdapterView<?> parent, View view,
    					int position, long id) {
    				Toast.makeText(MainActivity.this, title[position], 1).show();
    			}
    		});
    		layout_left.setOnTouchListener(this);
    		iv_set.setOnTouchListener(this);
    		mGestureDetector = new GestureDetector(this);
    		// 禁用长按监听
    		mGestureDetector.setIsLongpressEnabled(false);
    		getMAX_WIDTH();
    	}
    这里要对手势进行监听,我想大家都知道怎么做,在这里我要说明一个方法:

    /***
    	 * 获取移动距离 移动的距离其实就是layout_left的宽度
    	 */
    	void getMAX_WIDTH() {
    		ViewTreeObserver viewTreeObserver = layout_left.getViewTreeObserver();
    		// 获取控件宽度
    		viewTreeObserver.addOnPreDrawListener(new OnPreDrawListener() {
    			@Override
    			public boolean onPreDraw() {
    				if (!hasMeasured) {
    					window_width = getWindowManager().getDefaultDisplay()
    							.getWidth();
    					RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left
    							.getLayoutParams();
    					layoutParams.width = window_width;
    					layout_left.setLayoutParams(layoutParams);
    					MAX_WIDTH = layout_right.getWidth();
    					Log.v(TAG, "MAX_WIDTH=" + MAX_WIDTH + "width="
    							+ window_width);
    					hasMeasured = true;
    				}
    				return true;
    			}
    		});
    
    	}

    在这里我们要获取屏幕的宽度,并将屏幕宽度设置给layout_left这个控件,为什么要这么做呢因为如果不把该控件宽度写死的话,那么系统将认为layout_left会根据不同环境宽度自动适应,也就是说我们通过layout_left.getLayoutParams动态移动该控件的时候,该控件会伸缩而不是移动。描述的有点模糊,大家请看下面示意图就明白了.

    我们不为layout_left定义死宽度效果:

         

    getLayoutParams可以很清楚看到,layout_left被向左拉伸了,并不是我们要的效果.

    还有一种解决办法就是我们在配置文件中直接把layout_left宽度写死,不过这样不利于开发,因为分辨率的问题.因此就用ViewTreeObserver进行对layout_left设置宽度.

    ViewTreeObserver,这个类主要用于对布局文件的监听.强烈建议同学们参考这篇文章 android ViewTreeObserver详细讲解,相信让你对ViewTreeObserver有更一步的了解.

    其他的就是对GestureDetector手势的应用,下面我把代码贴出来:

    package com.jj.slidingmenu;
    
    import android.app.Activity;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewTreeObserver;
    import android.view.ViewTreeObserver.OnPreDrawListener;
    import android.view.Window;
    import android.view.View.OnTouchListener;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.ListView;
    import android.widget.RelativeLayout;
    import android.widget.Toast;
    import android.widget.LinearLayout.LayoutParams;
    
    /***
     * 滑动菜单
     * 
     * @author jjhappyforever...
     * 
     */
    public class MainActivity extends Activity implements OnTouchListener,
    		GestureDetector.OnGestureListener {
    	private boolean hasMeasured = false;// 是否Measured.
    	private LinearLayout layout_left;
    	private LinearLayout layout_right;
    	private ImageView iv_set;
    	private ListView lv_set;
    
    	/** 每次自动展开/收缩的范围 */
    	private int MAX_WIDTH = 0;
    	/** 每次自动展开/收缩的速度 */
    	private final static int SPEED = 30;
    
    	private GestureDetector mGestureDetector;// 手势
    	private boolean isScrolling = false;
    	private float mScrollX; // 滑块滑动距离
    	private int window_width;// 屏幕的宽度
    
    	private String TAG = "jj";
    
    	private String title[] = { "待发送队列", "同步分享设置", "编辑我的资料", "找朋友", "告诉朋友",
    			"节省流量", "推送设置", "版本更新", "意见反馈", "积分兑换", "精品应用", "常见问题", "退出当前帐号" };
    
    	/***
    	 * 初始化view
    	 */
    	void InitView() {
    		layout_left = (LinearLayout) findViewById(R.id.layout_left);
    		layout_right = (LinearLayout) findViewById(R.id.layout_right);
    		iv_set = (ImageView) findViewById(R.id.iv_set);
    		lv_set = (ListView) findViewById(R.id.lv_set);
    		lv_set.setAdapter(new ArrayAdapter<String>(this, R.layout.item,
    				R.id.tv_item, title));
    		lv_set.setOnItemClickListener(new OnItemClickListener() {
    
    			@Override
    			public void onItemClick(AdapterView<?> parent, View view,
    					int position, long id) {
    				Toast.makeText(MainActivity.this, title[position], 1).show();
    			}
    		});
    		layout_left.setOnTouchListener(this);
    		iv_set.setOnTouchListener(this);
    		mGestureDetector = new GestureDetector(this);
    		// 禁用长按监听
    		mGestureDetector.setIsLongpressEnabled(false);
    		getMAX_WIDTH();
    	}
    
    	/***
    	 * 获取移动距离 移动的距离其实就是layout_left的宽度
    	 */
    	void getMAX_WIDTH() {
    		ViewTreeObserver viewTreeObserver = layout_left.getViewTreeObserver();
    		// 获取控件宽度
    		viewTreeObserver.addOnPreDrawListener(new OnPreDrawListener() {
    			@Override
    			public boolean onPreDraw() {
    				if (!hasMeasured) {
    					window_width = getWindowManager().getDefaultDisplay()
    							.getWidth();
    					RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left
    							.getLayoutParams();
    					// layoutParams.width = window_width;
    					layout_left.setLayoutParams(layoutParams);
    					MAX_WIDTH = layout_right.getWidth();
    					Log.v(TAG, "MAX_WIDTH=" + MAX_WIDTH + "width="
    							+ window_width);
    					hasMeasured = true;
    				}
    				return true;
    			}
    		});
    
    	}
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		requestWindowFeature(Window.FEATURE_NO_TITLE);
    		setContentView(R.layout.main);
    		InitView();
    
    	}
    
    	// 返回键
    	@Override
    	public boolean onKeyDown(int keyCode, KeyEvent event) {
    		if (KeyEvent.KEYCODE_BACK == keyCode && event.getRepeatCount() == 0) {
    			RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left
    					.getLayoutParams();
    			if (layoutParams.leftMargin < 0) {
    				new AsynMove().execute(SPEED);
    				return false;
    			}
    		}
    
    		return super.onKeyDown(keyCode, event);
    	}
    
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		// 松开的时候要判断,如果不到半屏幕位子则缩回去,
    		if (MotionEvent.ACTION_UP == event.getAction() && isScrolling == true) {
    			RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left
    					.getLayoutParams();
    			// 缩回去
    			if (layoutParams.leftMargin < -window_width / 2) {
    				new AsynMove().execute(-SPEED);
    			} else {
    				new AsynMove().execute(SPEED);
    			}
    		}
    
    		return mGestureDetector.onTouchEvent(event);
    	}
    
    	@Override
    	public boolean onDown(MotionEvent e) {
    		mScrollX = 0;
    		isScrolling = false;
    		// 将之改为true,不然事件不会向下传递.
    		return true;
    	}
    
    	@Override
    	public void onShowPress(MotionEvent e) {
    
    	}
    
    	/***
    	 * 点击松开执行
    	 */
    	@Override
    	public boolean onSingleTapUp(MotionEvent e) {
    		RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left
    				.getLayoutParams();
    		// 左移动
    		if (layoutParams.leftMargin >= 0) {
    			new AsynMove().execute(-SPEED);
    		} else {
    			// 右移动
    			new AsynMove().execute(SPEED);
    		}
    
    		return true;
    	}
    
    	/***
    	 * e1 是起点,e2是终点,如果distanceX=e1.x-e2.x>0说明向左滑动。反之亦如此.
    	 */
    	@Override
    	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
    			float distanceY) {
    		isScrolling = true;
    		mScrollX += distanceX;// distanceX:向左为正,右为负
    		RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left
    				.getLayoutParams();
    		layoutParams.leftMargin -= mScrollX;
    		if (layoutParams.leftMargin >= 0) {
    			isScrolling = false;// 拖过头了不需要再执行AsynMove了
    			layoutParams.leftMargin = 0;
    
    		} else if (layoutParams.leftMargin <= -MAX_WIDTH) {
    			// 拖过头了不需要再执行AsynMove了
    			isScrolling = false;
    			layoutParams.leftMargin = -MAX_WIDTH;
    		}
    		layout_left.setLayoutParams(layoutParams);
    		return false;
    	}
    
    	@Override
    	public void onLongPress(MotionEvent e) {
    
    	}
    
    	@Override
    	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
    			float velocityY) {
    		return false;
    	}
    
    	class AsynMove extends AsyncTask<Integer, Integer, Void> {
    
    		@Override
    		protected Void doInBackground(Integer... params) {
    			int times = 0;
    			if (MAX_WIDTH % Math.abs(params[0]) == 0)// 整除
    				times = MAX_WIDTH / Math.abs(params[0]);
    			else
    				times = MAX_WIDTH / Math.abs(params[0]) + 1;// 有余数
    
    			for (int i = 0; i < times; i++) {
    				publishProgress(params[0]);
    				try {
    					Thread.sleep(Math.abs(params[0]));
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    
    			return null;
    		}
    
    		/**
    		 * update UI
    		 */
    		@Override
    		protected void onProgressUpdate(Integer... values) {
    			RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) layout_left
    					.getLayoutParams();
    			// 右移动
    			if (values[0] > 0) {
    				layoutParams.leftMargin = Math.min(layoutParams.leftMargin
    						+ values[0], 0);
    				Log.v(TAG, "移动右" + layoutParams.rightMargin);
    			} else {
    				// 左移动
    				layoutParams.leftMargin = Math.max(layoutParams.leftMargin
    						+ values[0], -MAX_WIDTH);
    				Log.v(TAG, "移动左" + layoutParams.rightMargin);
    			}
    			layout_left.setLayoutParams(layoutParams);
    
    		}
    
    	}
    
    }
    上面代码注释已经很明确,相信大家都看的明白,我就不过多解释了。

    效果图:截屏出来有点卡,不过在手机虚拟机上是不卡的.



    源码下载


    怎么样,看着还行吧,我们在看下面一个示例:



    简单说明一下,当你滑动的时候左边会跟着右边一起滑动,这个效果比上面那个酷吧,上面那个有点死板,其实实现起来也比较容易,只需要把我们上面那个稍微修改下,对layout_right也进行时时更新,这样就实现了这个效果了,如果上面那个理解了,这个很轻松就解决了,在这里我又遇到一个问题:此时的listview的item监听不到手势,意思就是我左右滑动listview他没有进行滑动。

    本人对touch众多事件监听拦截等熟悉度不够,因此这里我用到自己写的方法,也许比较麻烦,如果有更好的解决办法,请大家一定要分享哦,再次 thanks for you 了.

    具体解决办法:我们重写listview,对此listview进行手势监听,我们自定义一个接口来实现,具体代码如下:

    package com.jj.slidingmenu;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.GestureDetector.OnGestureListener;
    import android.view.View;
    import android.widget.ListView;
    import android.widget.Toast;
    
    public class MyListView extends ListView implements OnGestureListener {
    
    	private GestureDetector gd;
    	// 事件状态
    	public static final char FLING_CLICK = 0;
    	public static final char FLING_LEFT = 1;
    	public static final char FLING_RIGHT = 2;
    	public static char flingState = FLING_CLICK;
    
    	private float distanceX;// 水平滑动的距离
    
    	private MyListViewFling myListViewFling;
    
    	public static boolean isClick = false;// 是否可以点击
    
    	public void setMyListViewFling(MyListViewFling myListViewFling) {
    		this.myListViewFling = myListViewFling;
    	}
    
    	public float getDistanceX() {
    		return distanceX;
    	}
    
    	public char getFlingState() {
    		return flingState;
    	}
    
    	private Context context;
    
    	public MyListView(Context context) {
    		super(context);
    
    	}
    
    	public MyListView(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		this.context = context;
    		gd = new GestureDetector(this);
    	}
    
    	/**
    	 * 覆写此方法,以解决ListView滑动被屏蔽问题
    	 */
    	@Override
    	public boolean dispatchTouchEvent(MotionEvent event) {
    		myListViewFling.doFlingOver(event);// 回调执行完毕.
    		this.gd.onTouchEvent(event);
    
    		return super.dispatchTouchEvent(event);
    	}
    
    	@Override
    	public boolean onTouchEvent(MotionEvent ev) {
    		/***
    		 * 当移动的时候
    		 */
    		if (ev.getAction() == MotionEvent.ACTION_DOWN)
    			isClick = true;
    		if (ev.getAction() == MotionEvent.ACTION_MOVE)
    			isClick = false;
    		return super.onTouchEvent(ev);
    	}
    
    	@Override
    	public boolean onDown(MotionEvent e) {
    		int position = pointToPosition((int) e.getX(), (int) e.getY());
    		if (position != ListView.INVALID_POSITION) {
    			View child = getChildAt(position - getFirstVisiblePosition());
    		}
    		return true;
    	}
    
    	@Override
    	public void onShowPress(MotionEvent e) {
    
    	}
    
    	@Override
    	public boolean onSingleTapUp(MotionEvent e) {
    
    		return false;
    	}
    
    	@Override
    	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
    			float distanceY) {
    		// 左滑动
    		if (distanceX > 0) {
    			flingState = FLING_RIGHT;
    			Log.v("jj", "左distanceX=" + distanceX);
    			myListViewFling.doFlingLeft(distanceX);// 回调
    			// 右滑动.
    		} else if (distanceX < 0) {
    			flingState = FLING_LEFT;
    			Log.v("jj", "右distanceX=" + distanceX);
    			myListViewFling.doFlingRight(distanceX);// 回调
    		}
    
    		return false;
    	}
    
    	/***
    	 * 上下文菜单
    	 */
    	@Override
    	public void onLongPress(MotionEvent e) {
    		// System.out.println("Listview long press");
    		// int position = pointToPosition((int) e.getX(), (int) e.getY());
    		// if (position != ListView.INVALID_POSITION) {
    		// View child = getChildAt(position - getFirstVisiblePosition());
    		// if (child != null) {
    		// showContextMenuForChild(child);
    		// this.requestFocusFromTouch();
    		// }
    		//
    		// }
    	}
    
    	@Override
    	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
    			float velocityY) {
    
    		return false;
    	}
    
    	/***
    	 * 回调接口
    	 * 
    	 * @author jjhappyforever...
    	 * 
    	 */
    	interface MyListViewFling {
    		void doFlingLeft(float distanceX);// 左滑动执行
    
    		void doFlingRight(float distanceX);// 右滑动执行
    
    		void doFlingOver(MotionEvent event);// 拖拽松开时执行
    
    	}
    
    }
    

    而在MainActivity.java里面实现该接口,我这么一说,我想有的同学们都明白了,具体实现起来代码有点多,我把代码上传到网上,大家可以下载后用心看,我想大家都能够明白的.(在这里我鄙视一下自己,肯定通过对手势监听拦截实现对listview的左右滑动,但是自己学业不经,再次再说一下,如有好的解决方案,请一定要分享我一下哦.)

    另外有一个问题:当listivew超出一屏的时候,此时的listview滑动的时候可以上下左右一起滑动,在此没有解决这个问题,如有解决请分享我哦.

    效果图:



    源码下载


    补充说明上面这个例子有点小BUG,就是右边菜单过长的话,我不仅可以上下滑动,同时也可以左右滑动,这点肯定不是我们想要的效果,其实下面已经解决了这个问题,就是我们自定义一个布局文件,在布局文件中进行对Touch事件监听.效果比上面好的多,至于网上别的样式,我想大家都应该可以效仿实现,这里就不一一讲解了,关键:大家要明白原理,遇到问题知道怎么处理,话费时间长没关系,只要可以搞定.(网上有的朋友说这个有重影,有的布局会变形,其实和我们的布局有关,因为我们用的是AbsoluteLayout布局,但是只要你懂得怎么用,那些问题都不是问题.)


    更正后源码下载



    由于篇符较长,先说到这里,其实android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu也可以实现.具体参考下一篇文章:android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu


    /*********************************************************************************************/


    下面介绍下

    android 滑动菜单SlidingMenu之拓展(解决ListView滑动冲突)

                    

    百度新闻客户端可以手势左划右划,而操作的对象是一个ListView,大家都知道SlidingMenu里的ListView加手势GestureDetector就是蛋疼的操作,但是百度人家就这么搞,而且做的相当棒,其他的应用我很少见到如此的,不得不说,牛逼有牛逼的道理.

    网上我搜查了,没有找到类似的案例,只能自己琢磨了,功夫不负有心人啊,终于实现了,方法比较笨戳,下面我简单讲解下:

    实现原理:Touch事件的拦截与分发.

    在项目中,由于点击不同的菜单要显示不同的内容,所以右边最好弄成活动布局,就是添加一个Linerlayout,动态添加相应布局,这样扩展比较容易.但是这个Linerlayout我们要自己定义,因为我们要拦截一些Touch事件.(实现:当我们上下滑动,ListView上下滑动,当我们左右滑动ListView禁止上下滑动,进行左右滑动)

    package com.hytrip.ui.custom;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.GestureDetector;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.GestureDetector.SimpleOnGestureListener;
    import android.widget.LinearLayout;
    
    /***
     * 行程详情的自定义布局
     * 
     * @author zhangjia
     * 
     */
    public class JourneyLinearLayout extends LinearLayout {
    	private GestureDetector mGestureDetector;
    	View.OnTouchListener mGestureListener;
    
    	private boolean isLock = true;
    
    	private OnScrollListener onScrollListener;// 自定义接口
    
    	private boolean b;
    
    	public JourneyLinearLayout(Context context) {
    		super(context);
    	}
    
    	public void setOnScrollListener(OnScrollListener onScrollListener) {
    		this.onScrollListener = onScrollListener;
    	}
    
    	public JourneyLinearLayout(Context context, AttributeSet attrs) {
    		super(context, attrs);
    		mGestureDetector = new GestureDetector(new MySimpleGesture());
    	}
    
    	@Override
    	public boolean dispatchTouchEvent(MotionEvent ev) {
    		Log.e("jj", "dispatchTouchEvent...");
    		// 获取手势返回值
    		b = mGestureDetector.onTouchEvent(ev);
    		// 松开手要执行一些操作。(关闭 or 打开)
    		if (ev.getAction() == MotionEvent.ACTION_UP) {
    			onScrollListener.doLoosen();
    		}
    		return super.dispatchTouchEvent(ev);
    	}
    
    	@Override
    	public boolean onInterceptTouchEvent(MotionEvent ev) {
    		Log.e("jj", "onInterceptTouchEvent...");
    		super.onInterceptTouchEvent(ev);
    		return b;
    	}
        /***
         * 在这里我简单说明一下
         */
    	@Override
    	public boolean onTouchEvent(MotionEvent event) {
    		Log.e("jj", "onTouchEvent...");
    		isLock = false;
    		return super.onTouchEvent(event);
    	}
    
    	/***
    	 * 自定义手势执行
    	 * 
    	 * @author zhangjia
    	 * 
    	 */
    	class MySimpleGesture extends SimpleOnGestureListener {
    
    		@Override
    		public boolean onDown(MotionEvent e) {
    			Log.e("jj", "onDown...");
    			isLock = true;
    			return super.onDown(e);
    		}
    
    		@Override
    		public boolean onScroll(MotionEvent e1, MotionEvent e2,
    				float distanceX, float distanceY) {
    
    			if (!isLock)
    				onScrollListener.doScroll(distanceX);
    
    			// 垂直大于水平
    			if (Math.abs(distanceY) > Math.abs(distanceX)) {
    				// Log.e("jjj", "ll...垂直...");
    				return false;
    			} else {
    				// Log.e("jjj", "ll...水平...");
    				// Log.e("jj", "distanceX===" + distanceX);
    				return true;
    			}
    
    		}
    	}
    
    	/***
    	 * 自定义接口 实现滑动...
    	 * 
    	 * @author zhangjia
    	 * 
    	 */
    	public interface OnScrollListener {
    		void doScroll(float distanceX);// 滑动...
    
    		void doLoosen();// 手指松开后执行...
    	}
    
    }
    

    说明1:顺序:dispatchTouchEvent》GestureDetector》onInterceptTouchEvent》onTouchEvent.

    说明2:onInterceptTouchEvent 返回true,则拦截孩子touch事件,执行当前OnTouch事件,而返回false,则不执行OnTouch事件,事件传递给孩子执行。。。

    因为onInterceptTouchEvent 是用于拦截Touch的,不适用于执行一些操作,所以把注入手势操作方法分发事件dispatchTouchEvent中.

    下面是自定义的一个接口(方法1:滑动中。。。方法2:松开自动合拢。。。),用于实现手势移动操作,在SlidingMenuActivity.java中实现其接口.

    (写的比较凌乱,但是如果你仔细看的话一定会明白的,弄懂事件的传递对你自定义想实现一些牛叉View会有帮助的.鄙人正在研究中...)

        

     因为是模型,所以样子很丑,不过重要的是实现方法.

    弄懂上面那个,下面我们在深入看一下:最上面最后一张图片,我们在滑动中间图片的时候ListView肯定是不要进行左划或者右划,是不是有点头大了,其实分析好了,也不难,我们只要对上面那个自定义类稍微修调一下:我们在滑动左右滑动判断一下,如果是ListView的HeadView,那么我们就不进行手势操作,这样ViewPager就可以左右滑动,而ListView就不会左右滑动了,如果不是HeadView还照常就Ok了,简单吧。

    下面是示例图:

                         


       左右拖拽图片局域(ListView未受影响)                左右拖拽(非图片)局域


    就说到这里,如有疑问请留言。

    对你有帮助的话,记得赞一个,不要钱的.

    Thanks for you !



    展开全文
  • RecyclerView滑动菜单

    2016-05-26 14:22:30
    RecyclerView滑动菜单
  • android滑动菜单

    2016-05-09 11:36:33
    android滑动菜单
  • 平台滑动菜单是一款在标题打开左侧菜单中点击菜单按钮无穷的滑动菜单,点击好友按钮的标题,打开第二个菜单。
  • 滑动菜单 AMSlideMenu.zip

    2019-09-17 18:38:40
    滑动菜单 AMSlideMenu ,AMSlideMenu iOS 的滑动菜单(允许左右滑动的菜单)。
  • 滑动菜单特效

    2011-10-17 19:56:47
    JavaScript滑动菜单特效 JavaScript滑动菜单特效 JavaScript滑动菜单特效 JavaScript滑动菜单特效 JavaScript滑动菜单特效
  • 淘宝滑动菜单.html

    2021-03-31 23:52:11
    博主开发基于移动端的商城时,受手机淘宝首页的滑动菜单启发模仿的一个滑动菜单组件。
  • 响应式滑动菜单by Prashant Yadav 通过Prashant Yadav 如何创建响应式滑动菜单 (How to create a responsive sliding menu) I run a blog named learnersbucket.com where I write about ES6, Data structures, ...

    响应式滑动菜单

    by Prashant Yadav

    通过Prashant Yadav

    如何创建响应式滑动菜单 (How to create a responsive sliding menu)

    I run a blog named learnersbucket.com where I write about ES6, Data structures, and Algorithms to help others crack coding interviews. Follow me on Twitter for regular updates.

    我经营着一个名为learningersbucket.com的博客,其中写了有关ES6数据结构算法的文章,以帮助他人破解编码面试。 在Twitter上关注我以获取定期更新。

    When I was designing my blog with a mobile-first approach, I decided to keep my sidebar navigation menu separate at the bottom right. There is no need for a sticky header and the user can read everything in full height.

    在以移动优先方式设计博客时,我决定在右下角将侧边栏导航菜单分开。 不需要粘性标头,用户可以读取所有高度的内容。

    This is the simple version of how my mobile menu looks.

    这是我的移动菜单外观的简单版本。

    Here is how you can create your own responsive sidebar navigation menu.

    您可以按照以下方法创建自己的响应式侧边栏导航菜单。

    总览 (Overview)

    Before we move on to designing the menu, let us imagine what components we need.

    在继续设计菜单之前,让我们想象一下我们需要哪些组件。

    • A hamburger ? button which will show/hide the sliding menu.

      一个汉堡 ? 按钮,将S 如何/小时IDE滑动菜单。

    • Animation on the hamburger button to represent the current state of the menu.

      汉堡包按钮上的动画代表菜单的当前状态。
    • A side navigation menu.

      侧面导航菜单。

    As the side navigation menu will toggle on the click of the hamburger menu, we can keep them together in a single container.

    由于侧面导航菜单将在点击汉堡菜单上切换,因此我们可以将它们放在一个容器中。

    依存关系 (Dependencies)

    I like to use jQuery for DOM manipulation as it reduces the amount of code I need to write.

    我喜欢将jQuery用于DOM操作,因为它减少了我需要编写的代码量。

    汉堡按钮 (Hamburger button)

    HTML结构 (HTML structure)

    There is a simple trick to creating a hamburger menu.

    创建汉堡菜单有一个简单的技巧。

    We are going to use a <div> with a .hamburger class to create the hamburger button wrapper. Then we will place three <span>s to create the layers of the hamburger.

    我们将使用ith a .ham burger类的<d iv>来创建汉堡按钮包装。 然后,我们将放置three <span>来创建汉堡包的各层。

    设计汉堡包按钮 (Designing the hamburger button)

    Now that the HTML structure for our button is ready, we need to design it to make it look like a hamburger. While designing, we need to keep in mind that we have to provide the animation for open & close when the user clicks on it.

    现在我们的按钮HTML结构已经准备就绪,我们需要对其进行设计以使其看起来像一个汉堡包。 在设计时,我们需要记住,当用户单击动画时,必须提供打开和关闭动画。

    As we are creating a fixed dimension hamburger button, we are going to provide fixed dimensions to the wrapper.

    在创建固定尺寸的汉堡按钮时,我们将为包装纸提供固定尺寸。

    • We have created a fixed parent .hamburger{position:fixed} to place it wherever we want on the screen.

      我们已经创建了一个固定的父.hamburger{position:fixed}可以将其放置在屏幕上的.hamburger{position:fixed}

    • Then we have designed all the <span>s as small rectangular boxes with position:absolute.

      然后,我们将所有<sp an>设计为with position:ab solute的小矩形框。

    • As we need to show three different strips we have changed the top position of the 2nd span .hamburger > span:nth-child(2){ top: 16px; } & 3rd span .hamburger > span:nth-child(3){ top: 27px; }.

      因为我们需要显示三个不同的条,所以我们更改了第二个跨度的顶部位置。 .hamburger > span:nth-child(2){ top: 16px ; }&3rd sp an .hamburger > span:nth-child(3){ top: 2 7px; }。

    • We have also provided transition: all .25s ease-in-out; to all the spans so that the change of their properties should be smooth.

      我们还提供了transition: all .25s ease-in-out; 到所有跨度,以便其属性的变化应平滑。

    用jQuery打开和关闭汉堡包按钮 (Opening & Closing hamburger button with jQuery)

    Whenever the hamburger button is clicked it will toggle an open class to it. We can now use this class to add the opening & closing effect.

    只要点击“汉堡包”按钮,它就会切换一个open类。 现在,我们可以使用此类来添加打开和关闭效果。

    .hamburger.open > span:nth-child(2){ transform: translateX(-100%); opacity: 0;} will slide the middle strip of the hamburger to the left and make it transparent.

    .hamburger.open > span:nth-child(2){ transform: translateX(-100%); opacity: .hamburger.open > span:nth-child(2){ transform: translateX(-100%); opacity: 0;}会将汉堡包的中间条向左滑动并使其透明。

    .hamburger.open > span:nth-child(1){ transform: rotateZ(45deg); top:16px; } & .hamburger.open > span:nth-child(2){ transform: rotateZ(-45deg); top:16px; } will bring the first and last span to the same top position and rotate them to make an X.

    .hamburger.open > span:nth-child(1){ transform: rotateZ(45deg); top:16px .hamburger.open > span:nth-child(1){ transform: rotateZ(45deg); top:16px ; } & .hamburger.open > span:nth-child(2){ transform: rotateZ(-45deg); top:1 & .hamburger.open > span:nth-child(2){ transform: rotateZ(-45deg); top:1 6px; }将第一个和最后一个跨度移到相同的顶部位置,然后旋转它们以形成X。

    Kudos ? we have our hamburger ? button ready, so let us create the side navigation now.

    荣誉? 我们有我们的汉堡包吗? 按钮已准备就绪,现在让我们创建侧面导航。

    响应式侧面导航菜单 (Responsive side navigation menu)

    HTML结构 (HTML structure)

    We will create a simple navigation menu.

    我们将创建一个简单的导航菜单。

    We used a nav element for creating the navigation menu and placed the links in ul.

    我们使用nav元素创建导航菜单,并将链接放在ul

    设计导航菜单 (Designing the navigation menu)

    I have created a full-screen side menu, you can change the dimensions according to your need. We are using &gt; selector to avoid overwriting the style of other elements.

    我已经创建了一个全屏的侧面菜单,您可以根据需要更改尺寸。 我们使用& GT; 选择器以避免覆盖其他元素的样式。

    Now we have our navigation menu and hamburger button ready, so we can wrap them inside a wrapper to make them functional.

    现在,我们已经准备好导航菜单和汉堡包按钮,因此可以将它们包装在包装纸中以使其正常工作。

    滑动导航菜单 (Sliding navigation menu)

    HTML结构 (HTML structure)

    We have placed the hamburger button and navigation menu inside the .mobile-menu wrapper.

    我们将汉堡包按钮和导航菜单放在.mobile-menu包装器内。

    设计滑动导航菜单 (Designing the sliding navigation menu)

    We have updated the design a little by providing some property of the .hamburger to .mobile-menu making it fixed and made .hamburger relative to keep the <span> design the same.

    我们已经通过提供的某些属性更新设计有点.hamburger.mobile-menu使其固定并提出.hamburger相对保持<sp的>设计相同。

    As there can be multiple navs, we have updated all the selectors .mobile-menu > nav as well to make sure we are pointing to the required elements only.

    由于可以有多个nav ,因此我们也更新了所有选择器.mobile-menu > nav以确保我们仅指向必需的元素。

    使用jQuery使侧边栏菜单起作用 (Making sidebar menu functional with jQuery)

    We now add our .open class to the .mobile-menu so that we can handle both the hamburger button and the sliding menu with a single change.

    现在,我们将.open类添加到.mobile-menu以便我们只需一次更改即可处理汉堡包按钮和滑动菜单。

    Our CSS for the animation is also updated accordingly.

    我们的动画CSS也将相应更新。

    Well done ??? we covered everything.

    做得好 ??? 我们涵盖了一切。

    Check out the working demo here

    在这里查看工作演示

    结论 (Conclusion)

    This post was about a simple sliding menu. I tried to break it into different components so that you can use them independently.

    这篇文章是关于一个简单的滑动菜单的。 我试图将其分解为不同的组件,以便您可以独立使用它们。

    Thank you for having patience while reading this. If you learned something new today then give some ?. Also, share it with your friends so that they can learn something new too.

    感谢您耐心阅读。 如果您今天学到新知识,那就给些?。 另外,与您的朋友分享,以便他们也可以学习新知识。

    That’s it, follow me on Twitter for knowledge sharing. I write about ES6, Nodejs, Data structures & Algorithms and full stack web development with JavaScript.

    就是这样,在Twitter上关注我以进行知识共享。 我写了关于ES6 ,Nodejs, 数据结构算法以及使用JavaScript进行全栈Web开发的文章。

    Originally published at learnersbucket.com on April 14, 2019.

    最初于2019年4月14日发布在learningersbucket.com上。

    翻译自: https://www.freecodecamp.org/news/how-to-create-a-responsive-sliding-menu-97b90852a455/

    响应式滑动菜单

    展开全文
  • 手机端滑动菜单

    2018-04-02 14:22:51
    适用于手机端js显示滑动菜单。或者其他下拉选项, 适用于手机端显示滑动菜单。或者其他下拉选项。
  • demoSlidingMenu 滑动菜单
  • 双向滑动菜单

    2013-09-02 22:10:39
    双向滑动菜单

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,521
精华内容 3,008
关键字:

滑动菜单