android自定义SlideMenu源码详解之最简单侧滑实现



实现原理:在一个Activity的布局中需要有两部分,一个是菜单(menu)的布局,一个是内容(content)的布局。两个布局横向排列,菜单布局在左,内容布局在右。初始化的时候将菜单布局向左偏移,以至于能够完全隐藏,这样内容布局就会完全显示在Activity中。然后通过监听手指滑动事件,来改变菜单布局的左偏移距离,从而控制菜单布局的显示和隐藏。

下来来实现这个效果:

1.打开layout下的activity_main.xml

<LinearLayout 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"

android:orientation="horizontal"

tools:context=".MainActivity" >

<LinearLayout

android:id="@+id/ll_menu"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical"

android:background="@drawable/menu"

></LinearLayout>

<LinearLayout

android:id="@+id/ll_content"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="@drawable/content"

android:orientation="vertical"

></LinearLayout>

</LinearLayout>

这个布局文件的最外层布局是一个LinearLayout,排列方向是水平方向排列。这个LinearLayout下面嵌套了两个子LinearLayout,分别就是菜单的布局和内容的布局。

2.打开MainActivity.java

public class MainActivity extends Activity implements OnTouchListener {

//滚动显示和隐藏Menu,手指滑动需要达到的速度

public static final int SNAP_VELOCITY=200;

//屏幕宽度

private int screenWidth;

//menu最多可以滑动的左边缘

private int leftEdge;

//menu最多可以滑动的右边缘

private int rightEdge=0;

//menu完全显示时,留给content的宽度值

private int menuPadding=80;

//主内容布局

private View content;

//menu布局

private View menu;

//menu布局的参数,通过这个参数来更改leftMargin的值

private LinearLayout.LayoutParams menuParams;

//记录手指按下的横坐标

private float xDown;

//记录手指移动的横坐标

private float xMove;

//记录手指抬起的横坐标

private float xUp;

//menu当前是显示还是隐藏

private boolean isMenuVisible;

//用于计算手指滑动的速度

private VelocityTracker mVelocityTracker;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

init();

content.setOnTouchListener(this);

}

private void init(){

WindowManager wm=getWindowManager();

screenWidth=wm.getDefaultDisplay().getWidth();

content=findViewById(R.id.ll_content);

menu=findViewById(R.id.ll_menu);

menuParams=(LayoutParams) menu.getLayoutParams();

//将menu的宽度设置为屏幕宽度减去menuPadding

menuParams.width=screenWidth-menuPadding;

leftEdge=-menuParams.width;

menuParams.leftMargin=leftEdge;

//将content的宽度设置为屏幕宽度

content.getLayoutParams().width=screenWidth;

}

@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:

xMove=event.getRawX();

int distanceX=(int)(xMove-xDown);// 移动的距离

if(isMenuVisible){//判断当前Menu是否已经显示,如果true,说明已经显示

menuParams.leftMargin=distanceX;

}

else{

menuParams.leftMargin=leftEdge+distanceX;

}

if(menuParams.leftMargin<leftEdge){

menuParams.leftMargin=leftEdge;

}

else if(menuParams.leftMargin>rightEdge){

menuParams.leftMargin=rightEdge;

}

menu.setLayoutParams(menuParams);

break;

case MotionEvent.ACTION_UP:

xUp=event.getRawX();

if(wantToShowMenu()){

if(shouldScrollToMenu()){

scrollToMenu();

}

else{

scrollToContent();

}

}

else if(wantToShowContent()){

if(shouldScrollToContent()){

scrollToContent();

}

else{

scrollToMenu();

}

}

recycleVelocityTracker();

break;

default:

break;

}

return true;

}

/**

* 判断当前手势的意图是不是想显示content。如果手指移动的距离是负数,且当前menu是可见的,则认为当前手势是想要显示content。

*

* @return 当前手势想显示content返回true,否则返回false。

*/

private boolean wantToShowContent() {

return xUp - xDown < 0 && isMenuVisible;

}

/**

* 判断当前手势的意图是不是想显示menu。如果手指移动的距离是正数,且当前menu是不可见的,则认为当前手势是想要显示menu。

*

* @return 当前手势想显示menu返回true,否则返回false。

*/

private boolean wantToShowMenu(){

return xUp-xDown>0&&!isMenuVisible;

}

/**

* 判断是否应该滚动将menu展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY,

* 就认为应该滚动将menu展示出来。

*

* @return 如果应该滚动将menu展示出来返回true,否则返回false。

*/

private boolean shouldScrollToMenu(){

return xUp-xDown>screenWidth/2||getScrollVelocity()>SNAP_VELOCITY;

}

/**

* 判断是否应该滚动将content展示出来。如果手指移动距离加上menuPadding大于屏幕的1/2,

* 或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将content展示出来。

*

* @return 如果应该滚动将content展示出来返回true,否则返回false。

*/

private boolean shouldScrollToContent(){

return xDown-xUp+menuPadding>screenWidth/2||getScrollVelocity()>SNAP_VELOCITY;

}

//将屏幕滚动到menu界面,滚动速度设为30

private void scrollToMenu(){

new ScrollTask().execute(30);

}

//将屏幕滚到到content界面,滚动速度为-30

private void scrollToContent(){

new ScrollTask().execute(-30);

}

//创建velocityTracker对象,并将触摸content界面的滑动事件加入velocityTracker当中

private void createVelocityTracker(MotionEvent event){

if(mVelocityTracker==null){

mVelocityTracker=VelocityTracker.obtain();

}

mVelocityTracker.addMovement(event);

}

//获取手指在Content界面滑动的速度

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=menuParams.leftMargin;

//根据传入速度来滚动界面,当滚动达到左边界或右边界,跳出循环

while(true){

leftMargin=leftMargin+speed[0];

if(leftMargin>rightEdge){

leftMargin=rightEdge;

break;

}

if(leftMargin<leftEdge){

leftMargin=leftEdge;

break;

}

publishProgress(leftMargin);

sleep(10);

}

if(speed[0]>0){

isMenuVisible=true;

}

else{

isMenuVisible=false;

}

return leftMargin;

}

@Override

protected void onProgressUpdate(Integer... values) {

menuParams.leftMargin=values[0];

menu.setLayoutParams(menuParams);

}

@Override

protected void onPostExecute(Integer result) {

menuParams.leftMargin=result;

menu.setLayoutParams(menuParams);

}

private void sleep(long mills){

try {

Thread.sleep(mills);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

初始化的时候调用initValues方法,在这里面将内容布局的宽度设定为屏幕的宽度,菜单布局的宽度设定为屏幕的宽度减去menuPadding值,这样可以保证在菜单布局展示的时候,仍有一部分内容布局可以看到。如果不在初始化的时候重定义两个布局宽度,就会按照layout文件里面声明的一样,两个布局都是fill_parent,这样就无法实现滑动菜单的效果了。然后将菜单布局的左偏移量设置为负的菜单布局的宽度,这样菜单布局就会被完全隐藏,只有内容布局会显示在界面上。

之后给内容布局注册监听事件,这样当手指在内容布局上滑动的时候就会触发onTouch事件。在onTouch事件里面,根据手指滑动的距离会改变菜单布局的左偏移量,从而控制菜单布局的显示和隐藏。当手指离开屏幕的时候,会判断应该滑动到菜单布局还是内容布局,判断依据是根据手指滑动的距离或者滑动的速度.

当前Demo只适用于单个Activity.

来自为知笔记(Wiz)

时间: 2024-10-06 07:47:52

android自定义SlideMenu源码详解之最简单侧滑实现的相关文章

Android View 事件分发机制源码详解(View篇)

前言 在Android View 事件分发机制源码详解(ViewGroup篇)一文中,主要对ViewGroup#dispatchTouchEvent的源码做了相应的解析,其中说到在ViewGroup把事件传递给子View的时候,会调用子View的dispatchTouchEvent,这时分两种情况,如果子View也是一个ViewGroup那么再执行同样的流程继续把事件分发下去,即调用ViewGroup#dispatchTouchEvent:如果子View只是单纯的一个View,那么调用的是Vie

Android编程之Fragment动画加载方法源码详解

上次谈到了Fragment动画加载的异常问题,今天再聊聊它的动画加载loadAnimation的实现源代码: Animation loadAnimation(Fragment fragment, int transit, boolean enter, int transitionStyle) { 接下来具体看一下里面的源码部分,我将一部分一部分的讲解,首先是: Animation animObj = fragment.onCreateAnimation(transit, enter, fragm

Android ArrayMap源码详解

尊重原创,转载请标明出处    http://blog.csdn.net/abcdef314159 分析源码之前先来介绍一下ArrayMap的存储结构,ArrayMap数据的存储不同于HashMap和SparseArray,在上一篇<Android SparseArray源码详解>中我们讲到SparseArray是以纯数组的形式存储的,一个数组存储的是key值一个数组存储的是value值,今天我们分析的ArrayMap和SparseArray有点类似,他也是以纯数组的形式存储,不过不同的是他的

IntentService源码详解

IntentService可以做什么: 如果你有一个任务,分成n个子任务,需要它们按照顺序完成.如果需要放到一个服务中完成,那么IntentService就会使最好的选择. IntentService是什么: IntentService是一个Service(看起来像废话,但是我第一眼看到这个名字,首先注意的是Intent啊.),所以如果自定义一个IntentService的话,一定要在AndroidManifest.xml里面声明. 从上面的"可以做什么"我们大概可以猜测一下Inten

butterknife源码详解

butterknife源码详解 作为Android开发者,大家肯定都知道大名鼎鼎的butterknife.它大大的提高了开发效率,虽然在很早之前就开始使用它了,但是只知道是通过注解的方式实现的,却一直没有仔细的学习下大牛的代码.最近在学习运行时注解,决定今天来系统的分析下butterknife的实现原理. 如果你之前不了解Annotation,那强烈建议你先看注解使用. 废多看图: 从图中可以很直观的看出它的module结构,以及使用示例代码. 它的目录和我们在注解使用这篇文章中介绍的一样,大体

深入Java基础(四)--哈希表(1)HashMap应用及源码详解

继续深入Java基础系列.今天是研究下哈希表,毕竟我们很多应用层的查找存储框架都是哈希作为它的根数据结构进行封装的嘛. 本系列: (1)深入Java基础(一)--基本数据类型及其包装类 (2)深入Java基础(二)--字符串家族 (3)深入Java基础(三)–集合(1)集合父类以及父接口源码及理解 (4)深入Java基础(三)–集合(2)ArrayList和其继承树源码解析以及其注意事项 文章结构:(1)哈希概述及HashMap应用:(2)HashMap源码分析:(3)再次总结关键点 一.哈希概

Spring IOC源码详解之容器初始化

Spring IOC源码详解之容器初始化 上篇介绍了Spring IOC的大致体系类图,先来看一段简短的代码,使用IOC比较典型的代码 ClassPathResource res = new ClassPathResource("beans.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDe

Guava Cache源码详解

目录 一.引子 二.使用方法 2.1 CacheBuilder有3种失效重载模式 2.2 测试验证 三.源码剖析 3.1 简介 3.2 源码剖析 四.总结 优点: 缺点: 正文 回到顶部 一.引子 缓存有很多种解决方案,常见的是: 1.存储在内存中 : 内存缓存顾名思义直接存储在JVM内存中,JVM宕机那么内存丢失,读写速度快,但受内存大小的限制,且有丢失数据风险. 2.存储在磁盘中: 即从内存落地并序列化写入磁盘的缓存,持久化在磁盘,读写需要IO效率低,但是安全. 3.内存+磁盘组合方式:这种

Java concurrent AQS 源码详解

一.引言 AQS(同步阻塞队列)是concurrent包下锁机制实现的基础,相信大家在读完本篇博客后会对AQS框架有一个较为清晰的认识 这篇博客主要针对AbstractQueuedSynchronizer的源码进行分析,大致分为三个部分: 静态内部类Node的解析 重要常量以及字段的解析 重要方法的源码详解. 所有的分析仅基于个人的理解,若有不正之处,请谅解和批评指正,不胜感激!!! 二.Node解析 AQS在内部维护了一个同步阻塞队列,下面简称sync queue,该队列的元素即静态内部类No