Android自定义控件——侧滑菜单_ViewGroup

当我们打开某些应用的时候,总是会出现“侧滑菜单”这样的效果,至于这种侧滑菜单是谁首先创造出来的,已经不重要,但是侧滑菜单确实功能新颖,用户体验极好,以至于市面上很多很多的应用也纷纷加入侧滑菜单的效果,以下是我从应用市场上下载来的几个应用,随时截图发在这里,看看别人(大型互联网公司)都做这种效果,那么我们自己在没有很好的“创意”下,是不是可以选择“山寨”(自定义)一下呢?!

              


        怎么样?相信你们都在各大App中已经领教过其“风采”了,感觉这种效果确实不错!那么,我们怎样去“山寨”呢?别急,在Java或者Android这样的开源世界里,这种常用常见的事物,早就有“开源勇士”为我铺好了道路。老外的技术确实牛逼,早就已经写好了这些,而且功能强大,易于项目集成,没错,就是大名鼎鼎的SlidingMenu,在GitHub中可以找到,SlidingMenu的github链接在这里https://github.com/jfeinstein10/SlidingMenu 不过,我们今天不是要讨论这个开源的SlidingMenu的,毕竟这个开源的代码在使用起来也不一定就方便,因为首先项目中需要导包,如果项目要求严格的话,某些代码就需要修改了,就避免不了看“老外大神”的源码,这是一件比较痛苦的事情,而且SlidingMenu同时又引用了另外一个开源项目ActionBarSherlock,使得代码结构更加难以阅读,关于SlidingMenu,我会在下一篇博客中进行讲解。

    接下来,我们就自己动手来实现一个自定义的SlidingMenu,以下是我画的一份草图,能够大致分析出侧滑菜单的一些细节:


如果觉得感官不强烈,那废话不说,先上我做好后运行的效果,针对这个效果后,再慢慢讲解



        好,效果如上图所示的样子,可以看到,整个侧滑菜单其实还是使用了Android给我们提供的各种组件组合而成的,没有必须制造一个“从无到有”的过程,这里必须使用android已有控件的基础上构建一个自定控件,既然如此,整个的自定义控件就没必要继承View类了,而是选择继承ViewGroup类。为什么要继承ViewGroup呢?原因是,ViewGroup下提供了一个onLayout方法,这个方法是用来为自定义组件下各个子元素显示做排版的,当然ViewGroup下还有例如getChildXXX()的方法,用来获取子元素,单独对子元素进行控制,这些都是View做不到的。

       仔细看图会发现,其实该自定义的滑动菜单界面就是两个不同的布局,组合起来放在一个视图下,进行来回切换而已,那么关于这些布局的代码如下:

内容界面slidemenu_main.xml

[html]  view plain  copy
 print ?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <LinearLayout  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:background="@drawable/top_bar_bg"  
  11.         android:orientation="horizontal" >  
  12.   
  13.         <ImageButton  
  14.             android:id="@+id/ib_back"  
  15.             android:layout_width="wrap_content"  
  16.             android:layout_height="wrap_content"  
  17.             android:background="@drawable/main_back" />  
  18.   
  19.         <View  
  20.             android:layout_width="1dip"  
  21.             android:layout_height="fill_parent"  
  22.             android:layout_margin="5dip"  
  23.             android:background="@drawable/top_bar_divider" />  
  24.   
  25.         <TextView  
  26.             android:layout_width="wrap_content"  
  27.             android:layout_height="wrap_content"  
  28.             android:layout_gravity="center_vertical"  
  29.             android:layout_marginLeft="10dip"  
  30.             android:text="网易新闻"  
  31.             android:textColor="@android:color/white"  
  32.             android:textSize="28sp" />  
  33.     </LinearLayout>  
  34.   
  35.     <LinearLayout  
  36.         android:id="@+id/ll_content"  
  37.         android:layout_width="fill_parent"  
  38.         android:layout_height="fill_parent"  
  39.         android:orientation="vertical" >  
  40.     </LinearLayout>  
  41.   
  42. </LinearLayout>  

菜单界面slidemenu_menu.xml

[html]  view plain  copy
 print ?
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="240dip"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <LinearLayout  
  7.         android:layout_width="240dip"  
  8.         android:layout_height="match_parent"  
  9.         android:background="@drawable/menu_bg"  
  10.         android:orientation="vertical" >  
  11.   
  12.         <TextView  
  13.             style="@style/tab_style"  
  14.             android:background="#33663300"  
  15.             android:drawableLeft="@drawable/tab_news"  
  16.             android:text="新闻" />  
  17.   
  18.         <TextView  
  19.             style="@style/tab_style"  
  20.             android:drawableLeft="@drawable/tab_read"  
  21.             android:text="订阅" />  
  22.   
  23.         <TextView  
  24.             style="@style/tab_style"  
  25.             android:drawableLeft="@drawable/tab_local"  
  26.             android:text="本地" />  
  27.   
  28.         <TextView  
  29.             style="@style/tab_style"  
  30.             android:drawableLeft="@drawable/tab_ties"  
  31.             android:text="跟帖" />  
  32.   
  33.         <TextView  
  34.             style="@style/tab_style"  
  35.             android:drawableLeft="@drawable/tab_pics"  
  36.             android:text="图片" />  
  37.   
  38.         <TextView  
  39.             style="@style/tab_style"  
  40.             android:drawableLeft="@drawable/tab_focus"  
  41.             android:text="话题" />  
  42.   
  43.         <TextView  
  44.             style="@style/tab_style"  
  45.             android:drawableLeft="@drawable/tab_vote"  
  46.             android:text="投票" />  
  47.   
  48.         <TextView  
  49.             style="@style/tab_style"  
  50.             android:drawableLeft="@drawable/tab_ugc"  
  51.             android:text="聚合阅读" />  
  52.     </LinearLayout>  
  53.   
  54. </ScrollView>  
需要用到的Style:

[html]  view plain  copy
 print ?
  1. <style name="tab_style">  
  2.         <item name="android:layout_width">fill_parent</item>  
  3.         <item name="android:layout_height">wrap_content</item>  
  4.         <item name="android:background">@drawable/tab_background</item>  
  5.         <item name="android:drawablePadding">20dip</item>  
  6.         <item name="android:gravity">center_vertical</item>  
  7.         <item name="android:padding">20dip</item>  
  8.         <item name="android:textColor">@android:color/white</item>  
  9.         <item name="android:textSize">25sp</item>  
  10.         <item name="android:onClick">tabClick</item>  
  11.         <item name="android:clickable">true</item>  
  12.     </style>  

主界面activity_main.xml

[html]  view plain  copy
 print ?
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     tools:context=".MainActivity" >  
  6.   
  7.     <com.example.slidingmenu.view.SlideMenu  
  8.         android:id="@+id/slidemenu"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="fill_parent" >  
  11.   
  12.         <include layout="@layout/slidemenu_menu" />  
  13.   
  14.         <include layout="@layout/slidemenu_main" />  
  15.     </com.example.slidingmenu.view.SlideMenu>  
  16.   
  17. </RelativeLayout>  

以下是侧滑菜单实现的主要两个步骤:

SlideMenu extends ViewGroup

1. 测量宽和高(测量菜单和主界面的宽和高)
2. 布置viewgroup中子控件的位置

3. 触摸事件的处理:
    按下: x轴的最后一次偏移量
    移动: 当前最新的x轴偏移量, x轴的最后一次偏移量
             1. 计算增量值
                 增量值 = x轴的最后一次偏移量 -  当前最新的x轴偏移量;
              2. 根据增量值, 更新屏幕显示的位置
                  scrollBy(增量值, 0);
              3. x轴的最后一次偏移量 = 当前最新的x轴偏移量;
    抬起: 获取当前移动到的X轴的位置,与菜单的中心点坐标进行对比
               当前移动的X轴位置 < 菜单中心点,切换到菜单
               当前移动的X轴位置 > 菜单中心点,切换到主界面

4,当手指抬起的时候,判断中心线坐标实现左右滑动切换的时候,明显切换速度过快了,没有一个渐变的过程,这样给用户体验就差了。那么,我们该怎么实现这个渐渐切换菜单和主界面的效果呢?实现的方式肯定不是唯一的,但是在这里,我推荐使用Android为我们提供好的一个Helper类——Scroller。
        Android里Scroller类是为了实现View平滑滚动的一个Helper类。通常在自定义的View时使用,在View中定义一个私有成员mScroller = new Scroller(context)。设置mScroller滚动的位置时,并不会导致View的滚动,通常是用mScroller记录/计算View滚动的位置,再重写View的computeScroll(),完成实际的滚动。 

ViewGroup.java

[java]  view plain  copy
 print ?
  1. protected boolean drawChild(Canvas canvas, View child, long drawingTime) {  
  2.        return child.draw(canvas, this, drawingTime);  
  3.    }  
接着触发View下的相关方法:

[java]  view plain  copy
 print ?
  1. boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {  
  2.     ...  
  3.     ...  
  4.     computeScroll();  
  5.     ...  
  6. }  
看看computeScroll();这个方法:
[java]  view plain  copy
 print ?
  1. public void computeScroll() {  
  2.     }  

 通过翻阅Android的相关源码会发现,在自定义控件里调用了父类的invalidate();方法刷新界面时,会触发ViewGroup下drawChild方法,而这个方法会触发View下的draw方法,在draw方法又触发了computeScroll()方法,再继续看这个computeScroll()方法,是个空方法,需要我们在子类中去实现这个方法,提供给刷新界面时被调用相关的逻辑,在这里当然是怎么实现渐变过程的逻辑了。以下是Scroller类的相关方法:  

mScroller.getCurrX()    //获取mScroller当前水平滚动的位置  
mScroller.getCurrY()    //获取mScroller当前竖直滚动的位置  
mScroller.getFinalX()   //获取mScroller最终停止的水平位置  
mScroller.getFinalY()     //获取mScroller最终停止的竖直位置  
mScroller.setFinalX(int newX)    //设置mScroller最终停留的水平位置,没有动画效果,直接跳到目标位置  
mScroller.setFinalY(int newY)    //设置mScroller最终停留的竖直位置,没有动画效果,直接跳到目标位置  
mScroller.startScroll(int startX, int startY, int dx, int dy)   //滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量  
mScroller.startScroll(int startX, int startY, int dx, int dy, int duration)    //滚动,startX, startY为开始滚动的位置,dx,dy为滚动的偏移量, duration为完成滚动的时间
mScroller.computeScrollOffset()   //返回值为boolean,true说明滚动尚未完成,false说明滚动已经完成。这是一个很重要的方法,通常放在View.computeScroll()中,用来判断是否滚动是否结束。  

主要代码如下,SlideMenu.java

[java]  view plain  copy
 print ?
  1. package com.example.slidingmenu.view;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.view.MotionEvent;  
  6. import android.view.View;  
  7. import android.view.ViewConfiguration;  
  8. import android.view.ViewGroup;  
  9. import android.widget.Scroller;  
  10.   
  11. public class SlideMenu extends ViewGroup {  
  12.   
  13.     /** 记录上一次移动的位置 */  
  14.     private int mMostRecentX;  
  15.     /** 标记菜单界面 */  
  16.     private final int MENU_VIEW = 1;  
  17.     /** 标记主界面 */  
  18.     private final int MAIN_VIEW = 2;  
  19.     /** 标记当前屏幕显示的界面 */  
  20.     private int currentView = MAIN_VIEW;  
  21.     /** 模拟数据 */  
  22.     private Scroller scroller;  
  23.     /** 向左滑动菜单隐藏的一定距离 */  
  24.     private int mTouchSlop;  
  25.   
  26.     public SlideMenu(Context context, AttributeSet attrs) {  
  27.         super(context, attrs);  
  28.         scroller = new Scroller(context);  
  29.         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();  
  30.     }  
  31.   
  32.     @Override  
  33.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  34.         // TODO Auto-generated method stub  
  35.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  36.   
  37.         // 初始化菜单和主界面的宽和高  
  38.         initView(widthMeasureSpec, heightMeasureSpec);  
  39.     }  
  40.   
  41.     /** 
  42.      * 测量菜单和主界面的宽和高 
  43.      *  
  44.      * @param widthMeasureSpec 
  45.      * @param heightMeasureSpec 
  46.      */  
  47.     private void initView(int widthMeasureSpec, int heightMeasureSpec) {  
  48.         // 获取菜单并且测量宽高  
  49.         View menuView = this.getChildAt(0);  
  50.         // menuView.getLayoutParams().width拿到布局参数 heightMeasureSpec屏幕高度  
  51.         menuView.measure(menuView.getLayoutParams().width, heightMeasureSpec);  
  52.   
  53.         // 获取主界面并且测量宽高  
  54.         View mainView = this.getChildAt(1);  
  55.         mainView.measure(widthMeasureSpec, heightMeasureSpec);  
  56.     }  
  57.   
  58.     @Override  
  59.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  60.         // 菜单  
  61.         View menuView = this.getChildAt(0);  
  62.         menuView.layout(-menuView.getMeasuredWidth(), 00, b);  
  63.         // 主界面  
  64.         View mainView = this.getChildAt(1);  
  65.         mainView.layout(l, t, r, b);  
  66.     }  
  67.   
  68.     @Override  
  69.     public boolean onTouchEvent(MotionEvent event) {  
  70.         // TODO Auto-generated method stub  
  71.         switch (event.getAction()) {  
  72.         case MotionEvent.ACTION_DOWN: // 手指按下  
  73.             mMostRecentX = (int) event.getX();  
  74.             break;  
  75.         case MotionEvent.ACTION_MOVE: // 手指移动  
  76.             // 1. 计算增量值 增量值 = x轴的最后一次偏移量 - 当前最新的x轴偏移量;  
  77.             int currentX = (int) event.getX();  
  78.             int delta = mMostRecentX - currentX;  
  79.   
  80.             // 2. 根据增量值, 更新屏幕显示的位置 scrollBy(增量值, 0);  
  81.             // 判断是否超出左右边界  
  82.             int scrollX = getScrollX() + delta; // 移动后的x轴偏移量  
  83.             if (scrollX < -this.getChildAt(0).getWidth()) {  
  84.                 // 超出左边界  
  85.                 scrollTo(-this.getChildAt(0).getWidth(), 0);  
  86.             } else if (scrollX > 0) {  
  87.                 // 超出右边界  
  88.                 scrollTo(00);  
  89.             } else {  
  90.                 scrollBy(delta, 0);  
  91.             }  
  92.   
  93.             // 3. x轴的最后一次偏移量 = 当前最新的x轴偏移量;  
  94.             mMostRecentX = currentX;  
  95.   
  96.             break;  
  97.         case MotionEvent.ACTION_UP: // 手指抬起  
  98.             // 菜单的中心点  
  99.             int menuCenter = -this.getChildAt(0).getWidth() / 2;  
  100.             // 当前移动到的X轴坐标  
  101.             int _x = getScrollX();  
  102.             if (_x < menuCenter) {  
  103.                 // 切换到菜单界面  
  104.                 // scrollTo(-this.getChildAt(0).getWidth(), 0);  
  105.                 currentView = MENU_VIEW;  
  106.             } else {  
  107.                 // 切换到主界面  
  108.                 // scrollTo(0, 0);  
  109.                 currentView = MAIN_VIEW;  
  110.             }  
  111.             switchView();  
  112.             break;  
  113.         default:  
  114.             break;  
  115.         }  
  116.   
  117.         return true;  
  118.     }  
  119.   
  120.     @Override  
  121.     public void computeScroll() {  
  122.         // 更新当前的X轴偏移量  
  123.         if (scroller.computeScrollOffset()) { // 返回true代表正在模拟数据,false 已经停止模拟数据  
  124.             scrollTo(scroller.getCurrX(), 0); // 更新X轴的偏移量  
  125.   
  126.             invalidate();  
  127.         }  
  128.     }  
  129.   
  130.     /** 
  131.      * 菜单和主界面切换 
  132.      */  
  133.     private void switchView() {  
  134.         int startX = getScrollX();  
  135.         int dx = 0;  
  136.         if (currentView == MAIN_VIEW) {  
  137.             dx = 0 - startX;  
  138.         } else if (currentView == MENU_VIEW) {  
  139.             dx = -getChildAt(0).getWidth() - startX;  
  140.         }  
  141.   
  142.         // 开始模拟数据  
  143.         scroller.startScroll(startX, 0, dx, 0, Math.abs(dx) * 5);  
  144.         invalidate(); // 刷新界面,会触发ViewGroup下drawChild-->child.draw-->computeScroll  
  145.     }  
  146.   
  147.     /** 
  148.      * 事件分发机制,处理菜单向左滑动时,时间消费事件 
  149.      */  
  150.     @Override  
  151.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  152.         switch (ev.getAction()) {  
  153.         case MotionEvent.ACTION_DOWN:  
  154.             mMostRecentX = (int) ev.getX();  
  155.             break;  
  156.         case MotionEvent.ACTION_MOVE:  
  157.             int moveX = (int) ev.getX();  
  158.             int diff = moveX - mMostRecentX;  
  159.             if (Math.abs(diff) > mTouchSlop)  
  160.                 return true// 认为是横向移动,消耗掉此事件  
  161.             break;  
  162.         case MotionEvent.ACTION_UP:  
  163.   
  164.             break;  
  165.         default:  
  166.             break;  
  167.         }  
  168.         return super.onInterceptTouchEvent(ev);  
  169.     }  
  170.   
  171.     /** 
  172.      * 判断菜单是否显示 
  173.      *  
  174.      * @return 
  175.      */  
  176.     public boolean isShowMenu() {  
  177.         return currentView == MENU_VIEW;  
  178.     }  
  179.   
  180.     /** 
  181.      * 隐藏菜单 
  182.      */  
  183.     public void hideMenu() {  
  184.         currentView = MAIN_VIEW;  
  185.         switchView();  
  186.     }  
  187.   
  188.     /** 
  189.      * 显示菜单 
  190.      */  
  191.     public void showMenu() {  
  192.         currentView = MENU_VIEW;  
  193.         switchView();  
  194.     }  
  195.   
  196. }  
       再看菜单的布局文件,细心会发现菜单布局中含有ScrollView,该组件上下滑动,会阻碍菜单键的左右滑动,描述的不太好,即ScrollView会消耗掉在菜单视图上的滑动事件,ScrollView的抢占焦点能力较强,为了解决这个问题,我们必须得了解一下Android下的事件分发机制,Android下的事件分发机制并不是一句话两句话就可以讲清楚的,所以我暂时画了一个草图,简单的研究一下事件分发机制



如上图,右边显示的是某一个布局,该布局分为三层,外ViewGroup,内ViewGroup和View,那么想象一下,当用户手指按下屏幕的时候,Android对于这样一个事件是怎样处理的呢?首先请参考左边的示意图。

    1,当事件被触发时,首先接收到事件的是外ViewGroup,事件传入时,外ViewGroup会调用自身的onDispatchTouchEvent方法用来处理事件分发,接着调用onInterceptTouchEvent方法,这个方法会判断事件是否由调用者(外ViewGroup)消耗掉?若返回true,需要消耗,则接下来调用onTouchEvent方法处理事件;若返回false,不需要消耗这个事件,则该事件会向下继续传递给内ViewGroup。

    2,内ViewGroup得到此次事件,同样效仿外ViewGroup,会先调用onDispatchTouchEvent方法处理事件分发,接着调用onInterceptTouchEvent判断事件消耗,若返回true,需要消耗,接下来执行onTouchEvent方法处理这个事件;若返回false,不需要消耗这个事件,则该事件继续向下传递,传递给了View。

    3,当View接收到了这个事件的时候,注意,因为View是最后一级组件,或许是某一具体的组件如TextView,在View里是没有onInterceptTouchEvent方法的。所以,当View接收到这个事件时,先会onDispatchTouchEvent分发这个事件,接下来就是onTouchEvent出来这个事件。返回值为boolean类型。若View消耗此事件,返回true,若不处理,就返回false或者调用父类返回值,不处理的这事件。

    4,若View不消耗此事件,Android事件分发机制会采取事件回传,即此事件再次被传给内ViewGroup,依次类推,内ViewGroup会向上传给外ViewGroup,最后直接传给屏幕,事件消失。

    不知道这样粗略的描述,是不是能理解呢?!Android下的事件分发机制比较复杂,想要认真学习这个知识点的话,我在这里提供两篇网上一些“大牛”写过的一些博客,写的很详尽,大家可以做一个深入的了解。

   Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

   Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

   Android编程下Touch事件的分发和消费机制

   以下是简单引用该SlidingMenu的方法,看MainActivity.java

[java]  view plain  copy
 print ?
  1. package com.example.slidingmenu;  
  2.   
  3. import com.example.slidingmenu.view.SlideMenu;  
  4.   
  5. import android.os.Bundle;  
  6. import android.view.LayoutInflater;  
  7. import android.view.View;  
  8. import android.view.View.OnClickListener;  
  9. import android.widget.ImageButton;  
  10. import android.widget.LinearLayout;  
  11. import android.app.Activity;  
  12.   
  13. public class MainActivity extends Activity implements OnClickListener {  
  14.   
  15.     private ImageButton backBtn;  
  16.     private SlideMenu slideMenu;  
  17.     private LinearLayout ll_content;  
  18.     private View view;  
  19.     private LayoutInflater inflater;  
  20.   
  21.     @Override  
  22.     protected void onCreate(Bundle savedInstanceState) {  
  23.         super.onCreate(savedInstanceState);  
  24.         setContentView(R.layout.activity_main);  
  25.         slideMenu = (SlideMenu) findViewById(R.id.slidemenu);  
  26.         // 初始化首页  
  27.         ll_content = (LinearLayout) findViewById(R.id.ll_content);  
  28.         inflater = LayoutInflater.from(this);  
  29.         view = inflater.inflate(R.layout.frag_news, null);  
  30.         ll_content.removeAllViews();  
  31.         ll_content.addView(view);  
  32.   
  33.         backBtn = (ImageButton) findViewById(R.id.ib_back);  
  34.         backBtn.setOnClickListener(this);  
  35.   
  36.         findViewById(R.id.tab_news).setOnClickListener(this);  
  37.         findViewById(R.id.tab_read).setOnClickListener(this);  
  38.         findViewById(R.id.tab_local).setOnClickListener(this);  
  39.         findViewById(R.id.tab_ties).setOnClickListener(this);  
  40.         findViewById(R.id.tab_pics).setOnClickListener(this);  
  41.         findViewById(R.id.tab_focus).setOnClickListener(this);  
  42.         findViewById(R.id.tab_vote).setOnClickListener(this);  
  43.         findViewById(R.id.tab_ugc).setOnClickListener(this);  
  44.     }  
  45.   
  46.     @Override  
  47.     public void onClick(View v) {  
  48.         switch (v.getId()) {  
  49.         case R.id.ib_back:  
  50.             if (slideMenu.isShowMenu()) {  
  51.                 slideMenu.hideMenu();  
  52.             } else {  
  53.                 slideMenu.showMenu();  
  54.             }  
  55.             break;  
  56.         case R.id.tab_news: // 新闻  
  57.             view = inflater.inflate(R.layout.frag_news, null);  
  58.             switchView();  
  59.             break;  
  60.         case R.id.tab_read: // 订阅  
  61.             view = inflater.inflate(R.layout.frag_read, null);  
  62.             switchView();  
  63.             break;  
  64.         case R.id.tab_local: // 本地  
  65.             view = inflater.inflate(R.layout.frag_local, null);  
  66.             switchView();  
  67.             break;  
  68.         case R.id.tab_ties: // 跟帖  
  69.             view = inflater.inflate(R.layout.frag_ties, null);  
  70.             switchView();  
  71.             break;  
  72.         case R.id.tab_pics: // 图片  
  73.             view = inflater.inflate(R.layout.frag_pics, null);  
  74.             switchView();  
  75.             break;  
  76.         case R.id.tab_focus: // 话题  
  77.             view = inflater.inflate(R.layout.frag_focus, null);  
  78.             switchView();  
  79.             break;  
  80.         case R.id.tab_vote: // 投票  
  81.             view = inflater.inflate(R.layout.frag_vote, null);  
  82.             switchView();  
  83.             break;  
  84.         case R.id.tab_ugc: // 聚合阅读  
  85.             view = inflater.inflate(R.layout.frag_ugc, null);  
  86.             switchView();  
  87.             break;  
  88.         }  
  89.     }  
  90.   
  91.     private void switchView() {  
  92.         if (view != null) {  
  93.             ll_content.removeAllViews();  
  94.             ll_content.addView(view);  
  95.             if (slideMenu.isShowMenu()) {  
  96.                 slideMenu.hideMenu();  
  97.             } else {  
  98.                 slideMenu.showMenu();  
  99.             }  
  100.         }  
  101.     }  
  102. }  
    引用这个自定义SlidMenu组件的时候,需要注意的是怎么在内容主界面添加我们需要的界面展示。大家回头看看,我在slidemenu_main.xml这个内容布局的下方放置了一个LinearLayout线性布局,为什么呢?大家看看LinearLayout或者RelativeLayout的继承结构,诚然它们都继承自ViewGroup,大家都知道ViewGroup里面提供了很多用操作子元素的方法,于是,我们可以找到这样的一些方法,addView(View child)和removeAllViews()。我们可以非常轻松的将某个布局文件渲染成视图View后,通过这两个方法来替换主界面的视图,这样就达到了菜单和主界面交互的效果。

   以上,由于篇幅有限,涉及到的知识比较多比较深的地方做了些简洁描述,时间仓促,难免有疏忽的地方,敬请指正。


源码请在这里下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值