首页 > 代码库 > 【案例分享】仿QQ5.0侧滑菜单ResideMenu

【案例分享】仿QQ5.0侧滑菜单ResideMenu

本文由 孙国威 原创。如需转载,请注明出处!


为了后续对这个项目进行优化,比如透明度动画、背景图的位移动画,以及性能上的优化。

我把这个项目上传到github上面,请大家随时关注。

github地址 https://github.com/sunguowei


最近项目要做一个QQ5.0的侧滑菜单效果,和传统的侧滑菜单存在着一些差异。想必大家都已经见识过了。

为了不重复发明轮子,先去github上面搜索了一番。

发现了几个类似的,但是还是有一些不同。

下面是搜索到的类似的开源项目。

RESideMenu(ios项目)

https://github.com/romaonthego/RESideMenu

AndroidResideMenu

https://github.com/SpecialCyCi/AndroidResideMenu

ResideLayout

https://github.com/kyze8439690/ResideLayout


研究了一下这些开源项目的源代码。感觉并不是特别适用于我们自己的项目。所以,我自己又研究了一下。最后的效果如下。当然了,还有很多可以优化的地方,后续再慢慢优化。

备注:如果图片动画显示不出来,可以点击这个网址查看。

http://img.blog.csdn.net/20140902225149282?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFub2Vs/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast


我是基于SlidingMenu库进行的二次修改,增加了一些转场动画。

大家对这个库应该比较熟悉,下面是SlidingMenu的github地址。非常感谢Jeremy Feinstein提供的这个库,让广大Android Developers省去了非常多的麻烦。

https://github.com/jfeinstein10/SlidingMenu

备注:SlidingMenu使用了SherlockActionBar这个库,配置起来会比较麻烦,在文章的最后我会把demo上传,供大家下载,减去了大家自己配置项目的麻烦。

我主要修改了2个类,SlidingMenu.java和CustonViewAbove.java,只是增加了一些功能,并没有修改原本的功能。

做了修改的地方,我做了中文注释,其实实现很简单,几行代码而已。推荐大家下载Demo,然后自己调试一下。Demo的下载地址在文章的末尾。

废话不多说,直接上代码,略微有点长。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class SlidingMenu extends RelativeLayout {  
  2.   
  3.     private static final String TAG = SlidingMenu.class.getSimpleName();  
  4.   
  5.     public static final int SLIDING_WINDOW = 0;  
  6.     public static final int SLIDING_CONTENT = 1;  
  7.     private boolean mActionbarOverlay = false;  
  8.   
  9.     /** 
  10.      * Constant value for use with setTouchModeAbove(). Allows the SlidingMenu 
  11.      * to be opened with a swipe gesture on the screen‘s margin 
  12.      */  
  13.     public static final int TOUCHMODE_MARGIN = 0;  
  14.   
  15.     /** 
  16.      * Constant value for use with setTouchModeAbove(). Allows the SlidingMenu 
  17.      * to be opened with a swipe gesture anywhere on the screen 
  18.      */  
  19.     public static final int TOUCHMODE_FULLSCREEN = 1;  
  20.   
  21.     /** 
  22.      * Constant value for use with setTouchModeAbove(). Denies the SlidingMenu 
  23.      * to be opened with a swipe gesture 
  24.      */  
  25.     public static final int TOUCHMODE_NONE = 2;  
  26.   
  27.     /** 
  28.      * Constant value for use with setMode(). Puts the menu to the left of the 
  29.      * content. 
  30.      */  
  31.     public static final int LEFT = 0;  
  32.   
  33.     /** 
  34.      * Constant value for use with setMode(). Puts the menu to the right of the 
  35.      * content. 
  36.      */  
  37.     public static final int RIGHT = 1;  
  38.   
  39.     /** 
  40.      * Constant value for use with setMode(). Puts menus to the left and right 
  41.      * of the content. 
  42.      */  
  43.     public static final int LEFT_RIGHT = 2;  
  44.   
  45.     private CustomViewAbove mViewAbove;  
  46.   
  47.     private CustomViewBehind mViewBehind;  
  48.   
  49.         /** 整体的背景,用一个ImageView代替 */  
  50.     private ImageView mViewBackground;  
  51.   
  52.     private OnOpenListener mOpenListener;  
  53.   
  54.     private OnOpenListener mSecondaryOpenListner;  
  55.   
  56.     private OnCloseListener mCloseListener;  
  57.   
  58.     /** 
  59.      * The listener interface for receiving onOpen events. The class that is 
  60.      * interested in processing a onOpen event implements this interface, and 
  61.      * the object created with that class is registered with a component using 
  62.      * the component‘s <code>addOnOpenListener<code> method. When 
  63.      * the onOpen event occurs, that object‘s appropriate 
  64.      * method is invoked 
  65.      */  
  66.     public interface OnOpenListener {  
  67.   
  68.         /** 
  69.          * On open. 
  70.          */  
  71.         public void onOpen();  
  72.     }  
  73.   
  74.     /** 
  75.      * The listener interface for receiving onOpened events. The class that is 
  76.      * interested in processing a onOpened event implements this interface, and 
  77.      * the object created with that class is registered with a component using 
  78.      * the component‘s <code>addOnOpenedListener<code> method. When 
  79.      * the onOpened event occurs, that object‘s appropriate 
  80.      * method is invoked. 
  81.      *  
  82.      * @see OnOpenedEvent 
  83.      */  
  84.     public interface OnOpenedListener {  
  85.   
  86.         /** 
  87.          * On opened. 
  88.          */  
  89.         public void onOpened();  
  90.     }  
  91.   
  92.     /** 
  93.      * The listener interface for receiving onClose events. The class that is 
  94.      * interested in processing a onClose event implements this interface, and 
  95.      * the object created with that class is registered with a component using 
  96.      * the component‘s <code>addOnCloseListener<code> method. When 
  97.      * the onClose event occurs, that object‘s appropriate 
  98.      * method is invoked. 
  99.      *  
  100.      * @see OnCloseEvent 
  101.      */  
  102.     public interface OnCloseListener {  
  103.   
  104.         /** 
  105.          * On close. 
  106.          */  
  107.         public void onClose();  
  108.     }  
  109.   
  110.     /** 
  111.      * The listener interface for receiving onClosed events. The class that is 
  112.      * interested in processing a onClosed event implements this interface, and 
  113.      * the object created with that class is registered with a component using 
  114.      * the component‘s <code>addOnClosedListener<code> method. When 
  115.      * the onClosed event occurs, that object‘s appropriate 
  116.      * method is invoked. 
  117.      *  
  118.      * @see OnClosedEvent 
  119.      */  
  120.     public interface OnClosedListener {  
  121.   
  122.         /** 
  123.          * On closed. 
  124.          */  
  125.         public void onClosed();  
  126.     }  
  127.   
  128.     /** 
  129.      * The Interface CanvasTransformer. 
  130.      */  
  131.     public interface CanvasTransformer {  
  132.   
  133.         /** 
  134.          * Transform canvas. 
  135.          *  
  136.          * @param canvas 
  137.          *            the canvas 
  138.          * @param percentOpen 
  139.          *            the percent open 
  140.          */  
  141.         public void transformCanvas(Canvas canvas, float percentOpen);  
  142.     }  
  143.   
  144.     /** 
  145.      * Instantiates a new SlidingMenu. 
  146.      *  
  147.      * @param context 
  148.      *            the associated Context 
  149.      */  
  150.     public SlidingMenu(Context context) {  
  151.         this(context, null);  
  152.     }  
  153.   
  154.     /** 
  155.      * Instantiates a new SlidingMenu and attach to Activity. 
  156.      *  
  157.      * @param activity 
  158.      *            the activity to attach slidingmenu 
  159.      * @param slideStyle 
  160.      *            the slidingmenu style 
  161.      */  
  162.     public SlidingMenu(Activity activity, int slideStyle) {  
  163.         this(activity, null);  
  164.         this.attachToActivity(activity, slideStyle);  
  165.     }  
  166.   
  167.     /** 
  168.      * Instantiates a new SlidingMenu. 
  169.      *  
  170.      * @param context 
  171.      *            the associated Context 
  172.      * @param attrs 
  173.      *            the attrs 
  174.      */  
  175.     public SlidingMenu(Context context, AttributeSet attrs) {  
  176.         this(context, attrs, 0);  
  177.     }  
  178.   
  179.     /** 
  180.      * Instantiates a new SlidingMenu. 
  181.      *  
  182.      * @param context 
  183.      *            the associated Context 
  184.      * @param attrs 
  185.      *            the attrs 
  186.      * @param defStyle 
  187.      *            the def style 
  188.      */  
  189.     public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {  
  190.         super(context, attrs, defStyle);  
  191.                 /** SlidingMenu是一个RelativeLayout,这里把背景图ImageView添加到RelativeLayout的最底层。*/  
  192.         LayoutParams backgroundParams = new LayoutParams(  
  193.                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);  
  194.         mViewBackground = new ImageView(context);  
  195.         mViewBackground.setScaleType(ImageView.ScaleType.CENTER_CROP);  
  196.         addView(mViewBackground, backgroundParams);  
  197.           
  198.                 LayoutParams behindParams = new LayoutParams(LayoutParams.MATCH_PARENT,  
  199.                 LayoutParams.MATCH_PARENT);  
  200.         mViewBehind = new CustomViewBehind(context);  
  201.         addView(mViewBehind, behindParams);  
  202.         LayoutParams aboveParams = new LayoutParams(LayoutParams.MATCH_PARENT,  
  203.                 LayoutParams.MATCH_PARENT);  
  204.         mViewAbove = new CustomViewAbove(context);  
  205.         addView(mViewAbove, aboveParams);  
  206.         // register the CustomViewBehind with the CustomViewAbove  
  207.         mViewAbove.setCustomViewBehind(mViewBehind);  
  208.         mViewBehind.setCustomViewAbove(mViewAbove);  
  209.         mViewAbove.setOnPageChangeListener(new OnPageChangeListener() {  
  210.             public static final int POSITION_OPEN = 0;  
  211.             public static final int POSITION_CLOSE = 1;  
  212.             public static final int POSITION_SECONDARY_OPEN = 2;  
  213.   
  214.             public void onPageScrolled(int position, float positionOffset,  
  215.                     int positionOffsetPixels) {  
  216.             }  
  217.   
  218.             public void onPageSelected(int position) {  
  219.                 if (position == POSITION_OPEN && mOpenListener != null) {  
  220.                     mOpenListener.onOpen();  
  221.                 } else if (position == POSITION_CLOSE && mCloseListener != null) {  
  222.                     mCloseListener.onClose();  
  223.                 } else if (position == POSITION_SECONDARY_OPEN  
  224.                         && mSecondaryOpenListner != null) {  
  225.                     mSecondaryOpenListner.onOpen();  
  226.                 }  
  227.             }  
  228.         });  
  229.   
  230.         // now style everything!  
  231.         TypedArray ta = context.obtainStyledAttributes(attrs,  
  232.                 R.styleable.SlidingMenu);  
  233.         // set the above and behind views if defined in xml  
  234.         int mode = ta.getInt(R.styleable.SlidingMenu_mode, LEFT);  
  235.         setMode(mode);  
  236.         int viewAbove = ta.getResourceId(R.styleable.SlidingMenu_viewAbove, -1);  
  237.         if (viewAbove != -1) {  
  238.             setContent(viewAbove);  
  239.         } else {  
  240.             setContent(new FrameLayout(context));  
  241.         }  
  242.         int viewBehind = ta.getResourceId(R.styleable.SlidingMenu_viewBehind,  
  243.                 -1);  
  244.         if (viewBehind != -1) {  
  245.             setMenu(viewBehind);  
  246.         } else {  
  247.             setMenu(new FrameLayout(context));  
  248.         }  
  249.         int touchModeAbove = ta.getInt(R.styleable.SlidingMenu_touchModeAbove,  
  250.                 TOUCHMODE_MARGIN);  
  251.         setTouchModeAbove(touchModeAbove);  
  252.         int touchModeBehind = ta.getInt(  
  253.                 R.styleable.SlidingMenu_touchModeBehind, TOUCHMODE_MARGIN);  
  254.         setTouchModeBehind(touchModeBehind);  
  255.   
  256.         int offsetBehind = (int) ta.getDimension(  
  257.                 R.styleable.SlidingMenu_behindOffset, -1);  
  258.         int widthBehind = (int) ta.getDimension(  
  259.                 R.styleable.SlidingMenu_behindWidth, -1);  
  260.         if (offsetBehind != -1 && widthBehind != -1)  
  261.             throw new IllegalStateException(  
  262.                     "Cannot set both behindOffset and behindWidth for a SlidingMenu");  
  263.         else if (offsetBehind != -1)  
  264.             setBehindOffset(offsetBehind);  
  265.         else if (widthBehind != -1)  
  266.             setBehindWidth(widthBehind);  
  267.         else  
  268.             setBehindOffset(0);  
  269.         float scrollOffsetBehind = ta.getFloat(  
  270.                 R.styleable.SlidingMenu_behindScrollScale, 0.33f);  
  271.         setBehindScrollScale(scrollOffsetBehind);  
  272.         int shadowRes = ta.getResourceId(  
  273.                 R.styleable.SlidingMenu_shadowDrawable, -1);  
  274.         if (shadowRes != -1) {  
  275.             setShadowDrawable(shadowRes);  
  276.         }  
  277.         int shadowWidth = (int) ta.getDimension(  
  278.                 R.styleable.SlidingMenu_shadowWidth, 0);  
  279.         setShadowWidth(shadowWidth);  
  280.         boolean fadeEnabled = ta.getBoolean(  
  281.                 R.styleable.SlidingMenu_fadeEnabled, true);  
  282.         setFadeEnabled(fadeEnabled);  
  283.         float fadeDeg = ta.getFloat(R.styleable.SlidingMenu_fadeDegree, 0.33f);  
  284.         setFadeDegree(fadeDeg);  
  285.         boolean selectorEnabled = ta.getBoolean(  
  286.                 R.styleable.SlidingMenu_selectorEnabled, false);  
  287.         setSelectorEnabled(selectorEnabled);  
  288.         int selectorRes = ta.getResourceId(  
  289.                 R.styleable.SlidingMenu_selectorDrawable, -1);  
  290.         if (selectorRes != -1)  
  291.             setSelectorDrawable(selectorRes);  
  292.         ta.recycle();  
  293.     }  
  294.   
  295.     /** 
  296.      * Attaches the SlidingMenu to an entire Activity 
  297.      *  
  298.      * @param activity 
  299.      *            the Activity 
  300.      * @param slideStyle 
  301.      *            either SLIDING_CONTENT or SLIDING_WINDOW 
  302.      */  
  303.     public void attachToActivity(Activity activity, int slideStyle) {  
  304.         attachToActivity(activity, slideStyle, false);  
  305.     }  
  306.   
  307.     /** 
  308.      * Attaches the SlidingMenu to an entire Activity 
  309.      *  
  310.      * @param activity 
  311.      *            the Activity 
  312.      * @param slideStyle 
  313.      *            either SLIDING_CONTENT or SLIDING_WINDOW 
  314.      * @param actionbarOverlay 
  315.      *            whether or not the ActionBar is overlaid 
  316.      */  
  317.     public void attachToActivity(Activity activity, int slideStyle,  
  318.             boolean actionbarOverlay) {  
  319.         if (slideStyle != SLIDING_WINDOW && slideStyle != SLIDING_CONTENT)  
  320.             throw new IllegalArgumentException(  
  321.                     "slideStyle must be either SLIDING_WINDOW or SLIDING_CONTENT");  
  322.   
  323.         if (getParent() != null)  
  324.             throw new IllegalStateException(  
  325.                     "This SlidingMenu appears to already be attached");  
  326.   
  327.         // get the window background  
  328.         TypedArray a = activity.getTheme().obtainStyledAttributes(  
  329.                 new int[] { android.R.attr.windowBackground });  
  330.         int background = a.getResourceId(00);  
  331.         a.recycle();  
  332.   
  333.         switch (slideStyle) {  
  334.         case SLIDING_WINDOW:  
  335.             mActionbarOverlay = false;  
  336.             ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();  
  337.             ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);  
  338.             // save ActionBar themes that have transparent assets  
  339.             decorChild.setBackgroundResource(background);  
  340.             decor.removeView(decorChild);  
  341.             decor.addView(this);  
  342.             setContent(decorChild);  
  343.             break;  
  344.         case SLIDING_CONTENT:  
  345.             mActionbarOverlay = actionbarOverlay;  
  346.             // take the above view out of  
  347.             ViewGroup contentParent = (ViewGroup) activity  
  348.                     .findViewById(android.R.id.content);  
  349.             View content = contentParent.getChildAt(0);  
  350.             contentParent.removeView(content);  
  351.             contentParent.addView(this);  
  352.             setContent(content);  
  353.             // save people from having transparent backgrounds  
  354.             if (content.getBackground() == null)  
  355.                 content.setBackgroundResource(background);  
  356.             break;  
  357.         }  
  358.     }  
  359.   
  360.     /** 
  361.      * Set the above view content from a layout resource. The resource will be 
  362.      * inflated, adding all top-level views to the above view. 
  363.      *  
  364.      * @param res 
  365.      *            the new content 
  366.      */  
  367.     public void setContent(int res) {  
  368.         setContent(LayoutInflater.from(getContext()).inflate(res, null));  
  369.     }  
  370.   
  371.     /** 
  372.      * Set the above view content to the given View. 
  373.      *  
  374.      * @param view 
  375.      *            The desired content to display. 
  376.      */  
  377.     public void setContent(View view) {  
  378.         mViewAbove.setContent(view);  
  379.         showContent();  
  380.     }  
  381.   
  382.     /** 
  383.      * 设置背景图片 
  384.      *  
  385.      * @param resid 
  386.      */  
  387.     public void setBackgroundImage(int resid) {  
  388.         mViewBackground.setBackgroundResource(resid);  
  389.     }  
  390.   
  391.     /** 
  392.      * Retrieves the current content. 
  393.      *  
  394.      * @return the current content 
  395.      */  
  396.     public View getContent() {  
  397.         return mViewAbove.getContent();  
  398.     }  
  399.   
  400.     /** 
  401.      * Set the behind view (menu) content from a layout resource. The resource 
  402.      * will be inflated, adding all top-level views to the behind view. 
  403.      *  
  404.      * @param res 
  405.      *            the new content 
  406.      */  
  407.     public void setMenu(int res) {  
  408.         setMenu(LayoutInflater.from(getContext()).inflate(res, null));  
  409.     }  
  410.   
  411.     /** 
  412.      * Set the behind view (menu) content to the given View. 
  413.      *  
  414.      * @param view 
  415.      *            The desired content to display. 
  416.      */  
  417.     public void setMenu(View v) {  
  418.         mViewBehind.setContent(v);  
  419.     }  
  420.   
  421.     /** 
  422.      * Retrieves the main menu. 
  423.      *  
  424.      * @return the main menu 
  425.      */  
  426.     public View getMenu() {  
  427.         return mViewBehind.getContent();  
  428.     }  
  429.   
  430.     /** 
  431.      * Set the secondary behind view (right menu) content from a layout 
  432.      * resource. The resource will be inflated, adding all top-level views to 
  433.      * the behind view. 
  434.      *  
  435.      * @param res 
  436.      *            the new content 
  437.      */  
  438.     public void setSecondaryMenu(int res) {  
  439.         setSecondaryMenu(LayoutInflater.from(getContext()).inflate(res, null));  
  440.     }  
  441.   
  442.     /** 
  443.      * Set the secondary behind view (right menu) content to the given View. 
  444.      *  
  445.      * @param view 
  446.      *            The desired content to display. 
  447.      */  
  448.     public void setSecondaryMenu(View v) {  
  449.         mViewBehind.setSecondaryContent(v);  
  450.         // mViewBehind.invalidate();  
  451.     }  
  452.   
  453.     /** 
  454.      * Retrieves the current secondary menu (right). 
  455.      *  
  456.      * @return the current menu 
  457.      */  
  458.     public View getSecondaryMenu() {  
  459.         return mViewBehind.getSecondaryContent();  
  460.     }  
  461.   
  462.     /** 
  463.      * Sets the sliding enabled. 
  464.      *  
  465.      * @param b 
  466.      *            true to enable sliding, false to disable it. 
  467.      */  
  468.     public void setSlidingEnabled(boolean b) {  
  469.         mViewAbove.setSlidingEnabled(b);  
  470.     }  
  471.   
  472.     /** 
  473.      * Checks if is sliding enabled. 
  474.      *  
  475.      * @return true, if is sliding enabled 
  476.      */  
  477.     public boolean isSlidingEnabled() {  
  478.         return mViewAbove.isSlidingEnabled();  
  479.     }  
  480.   
  481.     /** 
  482.      * Sets which side the SlidingMenu should appear on. 
  483.      *  
  484.      * @param mode 
  485.      *            must be either SlidingMenu.LEFT or SlidingMenu.RIGHT 
  486.      */  
  487.     public void setMode(int mode) {  
  488.         if (mode != LEFT && mode != RIGHT && mode != LEFT_RIGHT) {  
  489.             throw new IllegalStateException(  
  490.                     "SlidingMenu mode must be LEFT, RIGHT, or LEFT_RIGHT");  
  491.         }  
  492.         mViewBehind.setMode(mode);  
  493.     }  
  494.   
  495.     /** 
  496.      * Returns the current side that the SlidingMenu is on. 
  497.      *  
  498.      * @return the current mode, either SlidingMenu.LEFT or SlidingMenu.RIGHT 
  499.      */  
  500.     public int getMode() {  
  501.         return mViewBehind.getMode();  
  502.     }  
  503.   
  504.     /** 
  505.      * Sets whether or not the SlidingMenu is in static mode (i.e. nothing is 
  506.      * moving and everything is showing) 
  507.      *  
  508.      * @param b 
  509.      *            true to set static mode, false to disable static mode. 
  510.      */  
  511.     public void setStatic(boolean b) {  
  512.         if (b) {  
  513.             setSlidingEnabled(false);  
  514.             mViewAbove.setCustomViewBehind(null);  
  515.             mViewAbove.setCurrentItem(1);  
  516.             // mViewBehind.setCurrentItem(0);  
  517.         } else {  
  518.             mViewAbove.setCurrentItem(1);  
  519.             // mViewBehind.setCurrentItem(1);  
  520.             mViewAbove.setCustomViewBehind(mViewBehind);  
  521.             setSlidingEnabled(true);  
  522.         }  
  523.     }  
  524.   
  525.     /** 
  526.      * Opens the menu and shows the menu view. 
  527.      */  
  528.     public void showMenu() {  
  529.         showMenu(true);  
  530.     }  
  531.   
  532.     /** 
  533.      * Opens the menu and shows the menu view. 
  534.      *  
  535.      * @param animate 
  536.      *            true to animate the transition, false to ignore animation 
  537.      */  
  538.     public void showMenu(boolean animate) {  
  539.         mViewAbove.setCurrentItem(0, animate);  
  540.     }  
  541.   
  542.     /** 
  543.      * Opens the menu and shows the secondary menu view. Will default to the 
  544.      * regular menu if there is only one. 
  545.      */  
  546.     public void showSecondaryMenu() {  
  547.         showSecondaryMenu(true);  
  548.     }  
  549.   
  550.     /** 
  551.      * Opens the menu and shows the secondary (right) menu view. Will default to 
  552.      * the regular menu if there is only one. 
  553.      *  
  554.      * @param animate 
  555.      *            true to animate the transition, false to ignore animation 
  556.      */  
  557.     public void showSecondaryMenu(boolean animate) {  
  558.         mViewAbove.setCurrentItem(2, animate);  
  559.     }  
  560.   
  561.     /** 
  562.      * Closes the menu and shows the above view. 
  563.      */  
  564.     public void showContent() {  
  565.         showContent(true);  
  566.     }  
  567.   
  568.     /** 
  569.      * Closes the menu and shows the above view. 
  570.      *  
  571.      * @param animate 
  572.      *            true to animate the transition, false to ignore animation 
  573.      */  
  574.     public void showContent(boolean animate) {  
  575.         mViewAbove.setCurrentItem(1, animate);  
  576.     }  
  577.   
  578.     /** 
  579.      * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa. 
  580.      */  
  581.     public void toggle() {  
  582.         toggle(true);  
  583.     }  
  584.   
  585.     /** 
  586.      * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa. 
  587.      *  
  588.      * @param animate 
  589.      *            true to animate the transition, false to ignore animation 
  590.      */  
  591.     public void toggle(boolean animate) {  
  592.         if (isMenuShowing()) {  
  593.             showContent(animate);  
  594.         } else {  
  595.             showMenu(animate);  
  596.         }  
  597.     }  
  598.   
  599.     /** 
  600.      * Checks if is the behind view showing. 
  601.      *  
  602.      * @return Whether or not the behind view is showing 
  603.      */  
  604.     public boolean isMenuShowing() {  
  605.         return mViewAbove.getCurrentItem() == 0  
  606.                 || mViewAbove.getCurrentItem() == 2;  
  607.     }  
  608.   
  609.     /** 
  610.      * Checks if is the behind view showing. 
  611.      *  
  612.      * @return Whether or not the behind view is showing 
  613.      */  
  614.     public boolean isSecondaryMenuShowing() {  
  615.         return mViewAbove.getCurrentItem() == 2;  
  616.     }  
  617.   
  618.     /** 
  619.      * Gets the behind offset. 
  620.      *  
  621.      * @return The margin on the right of the screen that the behind view 
  622.      *         scrolls to 
  623.      */  
  624.     public int getBehindOffset() {  
  625.         return ((RelativeLayout.LayoutParams) mViewBehind.getLayoutParams()).rightMargin;  
  626.     }  
  627.   
  628.     /** 
  629.      * Sets the behind offset. 
  630.      *  
  631.      * @param i 
  632.      *            The margin, in pixels, on the right of the screen that the 
  633.      *            behind view scrolls to. 
  634.      */  
  635.     public void setBehindOffset(int i) {  
  636.         // RelativeLayout.LayoutParams params =  
  637.         // ((RelativeLayout.LayoutParams)mViewBehind.getLayoutParams());  
  638.         // int bottom = params.bottomMargin;  
  639.         // int top = params.topMargin;  
  640.         // int left = params.leftMargin;  
  641.         // params.setMargins(left, top, i, bottom);  
  642.         mViewBehind.setWidthOffset(i);  
  643.     }  
  644.   
  645.     /** 
  646.      * Sets the behind offset. 
  647.      *  
  648.      * @param resID 
  649.      *            The dimension resource id to be set as the behind offset. The 
  650.      *            menu, when open, will leave this width margin on the right of 
  651.      *            the screen. 
  652.      */  
  653.     public void setBehindOffsetRes(int resID) {  
  654.         int i = (int) getContext().getResources().getDimension(resID);  
  655.         setBehindOffset(i);  
  656.     }  
  657.   
  658.     /** 
  659.      * Sets the above offset. 
  660.      *  
  661.      * @param i 
  662.      *            the new above offset, in pixels 
  663.      */  
  664.     public void setAboveOffset(int i) {  
  665.         mViewAbove.setAboveOffset(i);  
  666.     }  
  667.   
  668.     /** 
  669.      * Sets the above offset. 
  670.      *  
  671.      * @param resID 
  672.      *            The dimension resource id to be set as the above offset. 
  673.      */  
  674.     public void setAboveOffsetRes(int resID) {  
  675.         int i = (int) getContext().getResources().getDimension(resID);  
  676.         setAboveOffset(i);  
  677.     }  
  678.   
  679.     /** 
  680.      * Sets the behind width. 
  681.      *  
  682.      * @param i 
  683.      *            The width the Sliding Menu will open to, in pixels 
  684.      */  
  685.     @SuppressWarnings("deprecation")  
  686.     public void setBehindWidth(int i) {  
  687.         int width;  
  688.         Display display = ((WindowManager) getContext().getSystemService(  
  689.                 Context.WINDOW_SERVICE)).getDefaultDisplay();  
  690.         try {  
  691.             Class<?> cls = Display.class;  
  692.             Class<?>[] parameterTypes = { Point.class };  
  693.             Point parameter = new Point();  
  694.             Method method = cls.getMethod("getSize", parameterTypes);  
  695.             method.invoke(display, parameter);  
  696.             width = parameter.x;  
  697.         } catch (Exception e) {  
  698.             width = display.getWidth();  
  699.         }  
  700.         setBehindOffset(width - i);  
  701.     }  
  702.   
  703.     /** 
  704.      * Sets the behind width. 
  705.      *  
  706.      * @param res 
  707.      *            The dimension resource id to be set as the behind width 
  708.      *            offset. The menu, when open, will open this wide. 
  709.      */  
  710.     public void setBehindWidthRes(int res) {  
  711.         int i = (int) getContext().getResources().getDimension(res);  
  712.         setBehindWidth(i);  
  713.     }  
  714.   
  715.     /** 
  716.      * Gets the behind scroll scale. 
  717.      *  
  718.      * @return The scale of the parallax scroll 
  719.      */  
  720.     public float getBehindScrollScale() {  
  721.         return mViewBehind.getScrollScale();  
  722.     }  
  723.   
  724.     /** 
  725.      * Gets the touch mode margin threshold 
  726.      *  
  727.      * @return the touch mode margin threshold 
  728.      */  
  729.     public int getTouchmodeMarginThreshold() {  
  730.         return mViewBehind.getMarginThreshold();  
  731.     }  
  732.   
  733.     /** 
  734.      * Set the touch mode margin threshold 
  735.      *  
  736.      * @param touchmodeMarginThreshold 
  737.      */  
  738.     public void setTouchmodeMarginThreshold(int touchmodeMarginThreshold) {  
  739.         mViewBehind.setMarginThreshold(touchmodeMarginThreshold);  
  740.     }  
  741.   
  742.     /** 
  743.      * Sets the behind scroll scale. 
  744.      *  
  745.      * @param f 
  746.      *            The scale of the parallax scroll (i.e. 1.0f scrolls 1 pixel 
  747.      *            for every 1 pixel that the above view scrolls and 0.0f scrolls 
  748.      *            0 pixels) 
  749.      */  
  750.     public void setBehindScrollScale(float f) {  
  751.         if (f < 0 && f > 1)  
  752.             throw new IllegalStateException(  
  753.                     "ScrollScale must be between 0 and 1");  
  754.         mViewBehind.setScrollScale(f);  
  755.     }  
  756.   
  757.     /** 
  758.      * Sets the behind canvas transformer. 
  759.      *  
  760.      * @param t 
  761.      *            the new behind canvas transformer 
  762.      */  
  763.     public void setBehindCanvasTransformer(CanvasTransformer t) {  
  764.         mViewBehind.setCanvasTransformer(t);  
  765.     }  
  766.   
  767.     /** 
  768.      * 设置右侧视图的转场动画 
  769.      *  
  770.      * @param t 
  771.      *            the new above canvas transformer 
  772.      */  
  773.     public void setAboveCanvasTransformer(CanvasTransformer t) {  
  774.         mViewAbove.setCanvasTransformer(t);  
  775.     }  
  776.   
  777.     /** 
  778.      * Gets the touch mode above. 
  779.      *  
  780.      * @return the touch mode above 
  781.      */  
  782.     public int getTouchModeAbove() {  
  783.         return mViewAbove.getTouchMode();  
  784.     }  
  785.   
  786.     /** 
  787.      * Controls whether the SlidingMenu can be opened with a swipe gesture. 
  788.      * Options are {@link #TOUCHMODE_MARGIN TOUCHMODE_MARGIN}, 
  789.      * {@link #TOUCHMODE_FULLSCREEN TOUCHMODE_FULLSCREEN}, or 
  790.      * {@link #TOUCHMODE_NONE TOUCHMODE_NONE} 
  791.      *  
  792.      * @param i 
  793.      *            the new touch mode 
  794.      */  
  795.     public void setTouchModeAbove(int i) {  
  796.         if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN  
  797.                 && i != TOUCHMODE_NONE) {  
  798.             throw new IllegalStateException(  
  799.                     "TouchMode must be set to either"  
  800.                             + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE.");  
  801.         }  
  802.         mViewAbove.setTouchMode(i);  
  803.     }  
  804.   
  805.     /** 
  806.      * Controls whether the SlidingMenu can be opened with a swipe gesture. 
  807.      * Options are {@link #TOUCHMODE_MARGIN TOUCHMODE_MARGIN}, 
  808.      * {@link #TOUCHMODE_FULLSCREEN TOUCHMODE_FULLSCREEN}, or 
  809.      * {@link #TOUCHMODE_NONE TOUCHMODE_NONE} 
  810.      *  
  811.      * @param i 
  812.      *            the new touch mode 
  813.      */  
  814.     public void setTouchModeBehind(int i) {  
  815.         if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN  
  816.                 && i != TOUCHMODE_NONE) {  
  817.             throw new IllegalStateException(  
  818.                     "TouchMode must be set to either"  
  819.                             + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE.");  
  820.         }  
  821.         mViewBehind.setTouchMode(i);  
  822.     }  
  823.   
  824.     /** 
  825.      * Sets the shadow drawable. 
  826.      *  
  827.      * @param resId 
  828.      *            the resource ID of the new shadow drawable 
  829.      */  
  830.     public void setShadowDrawable(int resId) {  
  831.         setShadowDrawable(getContext().getResources().getDrawable(resId));  
  832.     }  
  833.   
  834.     /** 
  835.      * Sets the shadow drawable. 
  836.      *  
  837.      * @param d 
  838.      *            the new shadow drawable 
  839.      */  
  840.     public void setShadowDrawable(Drawable d) {  
  841.         mViewBehind.setShadowDrawable(d);  
  842.     }  
  843.   
  844.     /** 
  845.      * Sets the secondary (right) shadow drawable. 
  846.      *  
  847.      * @param resId 
  848.      *            the resource ID of the new shadow drawable 
  849.      */  
  850.     public void setSecondaryShadowDrawable(int resId) {  
  851.         setSecondaryShadowDrawable(getContext().getResources().getDrawable(  
  852.                 resId));  
  853.     }  
  854.   
  855.     /** 
  856.      * Sets the secondary (right) shadow drawable. 
  857.      *  
  858.      * @param d 
  859.      *            the new shadow drawable 
  860.      */  
  861.     public void setSecondaryShadowDrawable(Drawable d) {  
  862.         mViewBehind.setSecondaryShadowDrawable(d);  
  863.     }  
  864.   
  865.     /** 
  866.      * Sets the shadow width. 
  867.      *  
  868.      * @param resId 
  869.      *            The dimension resource id to be set as the shadow width. 
  870.      */  
  871.     public void setShadowWidthRes(int resId) {  
  872.         setShadowWidth((int) getResources().getDimension(resId));  
  873.     }  
  874.   
  875.     /** 
  876.      * Sets the shadow width. 
  877.      *  
  878.      * @param pixels 
  879.      *            the new shadow width, in pixels 
  880.      */  
  881.     public void setShadowWidth(int pixels) {  
  882.         mViewBehind.setShadowWidth(pixels);  
  883.     }  
  884.   
  885.     /** 
  886.      * Enables or disables the SlidingMenu‘s fade in and out 
  887.      *  
  888.      * @param b 
  889.      *            true to enable fade, false to disable it 
  890.      */  
  891.     public void setFadeEnabled(boolean b) {  
  892.         mViewBehind.setFadeEnabled(b);  
  893.     }  
  894.   
  895.     /** 
  896.      * Sets how much the SlidingMenu fades in and out. Fade must be enabled, see 
  897.      * {@link #setFadeEnabled(boolean) setFadeEnabled(boolean)} 
  898.      *  
  899.      * @param f 
  900.      *            the new fade degree, between 0.0f and 1.0f 
  901.      */  
  902.     public void setFadeDegree(float f) {  
  903.         mViewBehind.setFadeDegree(f);  
  904.     }  
  905.   
  906.     /** 
  907.      * Enables or disables whether the selector is drawn 
  908.      *  
  909.      * @param b 
  910.      *            true to draw the selector, false to not draw the selector 
  911.      */  
  912.     public void setSelectorEnabled(boolean b) {  
  913.         mViewBehind.setSelectorEnabled(true);  
  914.     }  
  915.   
  916.     /** 
  917.      * Sets the selected view. The selector will be drawn here 
  918.      *  
  919.      * @param v 
  920.      *            the new selected view 
  921.      */  
  922.     public void setSelectedView(View v) {  
  923.         mViewBehind.setSelectedView(v);  
  924.     }  
  925.   
  926.     /** 
  927.      * Sets the selector drawable. 
  928.      *  
  929.      * @param res 
  930.      *            a resource ID for the selector drawable 
  931.      */  
  932.     public void setSelectorDrawable(int res) {  
  933.         mViewBehind.setSelectorBitmap(BitmapFactory.decodeResource(  
  934.                 getResources(), res));  
  935.     }  
  936.   
  937.     /** 
  938.      * Sets the selector drawable. 
  939.      *  
  940.      * @param b 
  941.      *            the new selector bitmap 
  942.      */  
  943.     public void setSelectorBitmap(Bitmap b) {  
  944.         mViewBehind.setSelectorBitmap(b);  
  945.     }  
  946.   
  947.     /** 
  948.      * Add a View ignored by the Touch Down event when mode is Fullscreen 
  949.      *  
  950.      * @param v 
  951.      *            a view to be ignored 
  952.      */  
  953.     public void addIgnoredView(View v) {  
  954.         mViewAbove.addIgnoredView(v);  
  955.     }  
  956.   
  957.     /** 
  958.      * Remove a View ignored by the Touch Down event when mode is Fullscreen 
  959.      *  
  960.      * @param v 
  961.      *            a view not wanted to be ignored anymore 
  962.      */  
  963.     public void removeIgnoredView(View v) {  
  964.         mViewAbove.removeIgnoredView(v);  
  965.     }  
  966.   
  967.     /** 
  968.      * Clear the list of Views ignored by the Touch Down event when mode is 
  969.      * Fullscreen 
  970.      */  
  971.     public void clearIgnoredViews() {  
  972.         mViewAbove.clearIgnoredViews();  
  973.     }  
  974.   
  975.     /** 
  976.      * Sets the OnOpenListener. {@link OnOpenListener#onOpen() 
  977.      * OnOpenListener.onOpen()} will be called when the SlidingMenu is opened 
  978.      *  
  979.      * @param listener 
  980.      *            the new OnOpenListener 
  981.      */  
  982.     public void setOnOpenListener(OnOpenListener listener) {  
  983.         // mViewAbove.setOnOpenListener(listener);  
  984.         mOpenListener = listener;  
  985.     }  
  986.   
  987.     /** 
  988.      * Sets the OnOpenListner for secondary menu {@link OnOpenListener#onOpen() 
  989.      * OnOpenListener.onOpen()} will be called when the secondary SlidingMenu is 
  990.      * opened 
  991.      *  
  992.      * @param listener 
  993.      *            the new OnOpenListener 
  994.      */  
  995.   
  996.     public void setSecondaryOnOpenListner(OnOpenListener listener) {  
  997.         mSecondaryOpenListner = listener;  
  998.     }  
  999.   
  1000.     /** 
  1001.      * Sets the OnCloseListener. {@link OnCloseListener#onClose() 
  1002.      * OnCloseListener.onClose()} will be called when any one of the SlidingMenu 
  1003.      * is closed 
  1004.      *  
  1005.      * @param listener 
  1006.      *            the new setOnCloseListener 
  1007.      */  
  1008.     public void setOnCloseListener(OnCloseListener listener) {  
  1009.         // mViewAbove.setOnCloseListener(listener);  
  1010.         mCloseListener = listener;  
  1011.     }  
  1012.   
  1013.     /** 
  1014.      * Sets the OnOpenedListener. {@link OnOpenedListener#onOpened() 
  1015.      * OnOpenedListener.onOpened()} will be called after the SlidingMenu is 
  1016.      * opened 
  1017.      *  
  1018.      * @param listener 
  1019.      *            the new OnOpenedListener 
  1020.      */  
  1021.     public void setOnOpenedListener(OnOpenedListener listener) {  
  1022.         mViewAbove.setOnOpenedListener(listener);  
  1023.     }  
  1024.   
  1025.     /** 
  1026.      * Sets the OnClosedListener. {@link OnClosedListener#onClosed() 
  1027.      * OnClosedListener.onClosed()} will be called after the SlidingMenu is 
  1028.      * closed 
  1029.      *  
  1030.      * @param listener 
  1031.      *            the new OnClosedListener 
  1032.      */  
  1033.     public void setOnClosedListener(OnClosedListener listener) {  
  1034.         mViewAbove.setOnClosedListener(listener);  
  1035.     }  
  1036.   
  1037.     public static class SavedState extends BaseSavedState {  
  1038.   
  1039.         private final int mItem;  
  1040.   
  1041.         public SavedState(Parcelable superState, int item) {  
  1042.             super(superState);  
  1043.             mItem = item;  
  1044.         }  
  1045.   
  1046.         private SavedState(Parcel in) {  
  1047.             super(in);  
  1048.             mItem = in.readInt();  
  1049.         }  
  1050.   
  1051.         public int getItem() {  
  1052.             return mItem;  
  1053.         }  
  1054.   
  1055.         /* 
  1056.          * (non-Javadoc) 
  1057.          *  
  1058.          * @see android.view.AbsSavedState#writeToParcel(android.os.Parcel, int) 
  1059.          */  
  1060.         public void writeToParcel(Parcel out, int flags) {  
  1061.             super.writeToParcel(out, flags);  
  1062.             out.writeInt(mItem);  
  1063.         }  
  1064.   
  1065.         public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {  
  1066.             public SavedState createFromParcel(Parcel in) {  
  1067.                 return new SavedState(in);  
  1068.             }  
  1069.   
  1070.             public SavedState[] newArray(int size) {  
  1071.                 return new SavedState[size];  
  1072.             }  
  1073.         };  
  1074.   
  1075.     }  
  1076.   
  1077.     /* 
  1078.      * (non-Javadoc) 
  1079.      *  
  1080.      * @see android.view.View#onSaveInstanceState() 
  1081.      */  
  1082.     @Override  
  1083.     protected Parcelable onSaveInstanceState() {  
  1084.         Parcelable superState = super.onSaveInstanceState();  
  1085.         SavedState ss = new SavedState(superState, mViewAbove.getCurrentItem());  
  1086.         return ss;  
  1087.     }  
  1088.   
  1089.     /* 
  1090.      * (non-Javadoc) 
  1091.      *  
  1092.      * @see android.view.View#onRestoreInstanceState(android.os.Parcelable) 
  1093.      */  
  1094.     @Override  
  1095.     protected void onRestoreInstanceState(Parcelable state) {  
  1096.         SavedState ss = (SavedState) state;  
  1097.         super.onRestoreInstanceState(ss.getSuperState());  
  1098.         mViewAbove.setCurrentItem(ss.getItem());  
  1099.     }  
  1100.   
  1101.     /* 
  1102.      * (non-Javadoc) 
  1103.      *  
  1104.      * @see android.view.ViewGroup#fitSystemWindows(android.graphics.Rect) 
  1105.      */  
  1106.     @SuppressLint("NewApi")  
  1107.     @Override  
  1108.     protected boolean fitSystemWindows(Rect insets) {  
  1109.         int leftPadding = insets.left;  
  1110.         int rightPadding = insets.right;  
  1111.         int topPadding = insets.top;  
  1112.         int bottomPadding = insets.bottom;  
  1113.         if (!mActionbarOverlay) {  
  1114.             Log.v(TAG, "setting padding!");  
  1115.             setPadding(leftPadding, topPadding, rightPadding, bottomPadding);  
  1116.         }  
  1117.         return true;  
  1118.     }  
  1119.   
  1120.     @TargetApi(Build.VERSION_CODES.HONEYCOMB)  
  1121.     public void manageLayers(float percentOpen) {  
  1122.         if (Build.VERSION.SDK_INT < 11)  
  1123.             return;  
  1124.   
  1125.         boolean layer = percentOpen > 0.0f && percentOpen < 1.0f;  
  1126.         final int layerType = layer ? View.LAYER_TYPE_HARDWARE  
  1127.                 : View.LAYER_TYPE_NONE;  
  1128.   
  1129.         if (layerType != getContent().getLayerType()) {  
  1130.             getHandler().post(new Runnable() {  
  1131.                 public void run() {  
  1132.                     Log.v(TAG, "changing layerType. hardware? "  
  1133.                             + (layerType == View.LAYER_TYPE_HARDWARE));  
  1134.                     getContent().setLayerType(layerType, null);  
  1135.                     getMenu().setLayerType(layerType, null);  
  1136.                     if (getSecondaryMenu() != null) {  
  1137.                         getSecondaryMenu().setLayerType(layerType, null);  
  1138.                     }  
  1139.                 }  
  1140.             });  
  1141.         }  
  1142.     }  
  1143.   
  1144. }  
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class CustomViewAbove extends ViewGroup {  
  2.   
  3.     private static final String TAG = "CustomViewAbove";  
  4.     private static final boolean DEBUG = false;  
  5.   
  6.     private static final boolean USE_CACHE = false;  
  7.   
  8.     private static final int MAX_SETTLE_DURATION = 600// ms  
  9.     private static final int MIN_DISTANCE_FOR_FLING = 25// dips  
  10.   
  11.     private static final Interpolator sInterpolator = new Interpolator() {  
  12.         public float getInterpolation(float t) {  
  13.             t -= 1.0f;  
  14.             return t * t * t * t * t + 1.0f;  
  15.         }  
  16.     };  
  17.   
  18.     private View mContent;  
  19.   
  20.     private int mCurItem;  
  21.     private Scroller mScroller;  
  22.   
  23.     private boolean mScrollingCacheEnabled;  
  24.   
  25.     private boolean mScrolling;  
  26.   
  27.     private boolean mIsBeingDragged;  
  28.     private boolean mIsUnableToDrag;  
  29.     private int mTouchSlop;  
  30.     private float mInitialMotionX;  
  31.     /** 
  32.      * Position of the last motion event. 
  33.      */  
  34.     private float mLastMotionX;  
  35.     private float mLastMotionY;  
  36.     /** 
  37.      * ID of the active pointer. This is used to retain consistency during 
  38.      * drags/flings if multiple pointers are used. 
  39.      */  
  40.     protected int mActivePointerId = INVALID_POINTER;  
  41.     /** 
  42.      * Sentinel value for no current active pointer. 
  43.      * Used by {@link #mActivePointerId}. 
  44.      */  
  45.     private static final int INVALID_POINTER = -1;  
  46.         /** 保存转场动画的变量*/  
  47.     private CanvasTransformer mTransformer;  
  48.       
  49.     /** 
  50.      * Determines speed during touch scrolling 
  51.      */  
  52.     protected VelocityTracker mVelocityTracker;  
  53.     private int mMinimumVelocity;  
  54.     protected int mMaximumVelocity;  
  55.     private int mFlingDistance;  
  56.   
  57.     private CustomViewBehind mViewBehind;  
  58.     //  private int mMode;  
  59.     private boolean mEnabled = true;  
  60.   
  61.     private OnPageChangeListener mOnPageChangeListener;  
  62.     private OnPageChangeListener mInternalPageChangeListener;  
  63.   
  64.     //  private OnCloseListener mCloseListener;  
  65.     //  private OnOpenListener mOpenListener;  
  66.     private OnClosedListener mClosedListener;  
  67.     private OnOpenedListener mOpenedListener;  
  68.   
  69.     private List<View> mIgnoredViews = new ArrayList<View>();  
  70.   
  71.     //  private int mScrollState = SCROLL_STATE_IDLE;  
  72.   
  73.     /** 
  74.      * Callback interface for responding to changing state of the selected page. 
  75.      */  
  76.     public interface OnPageChangeListener {  
  77.   
  78.         /** 
  79.          * This method will be invoked when the current page is scrolled, either as part 
  80.          * of a programmatically initiated smooth scroll or a user initiated touch scroll. 
  81.          * 
  82.          * @param position Position index of the first page currently being displayed. 
  83.          *                 Page position+1 will be visible if positionOffset is nonzero. 
  84.          * @param positionOffset Value from [0, 1) indicating the offset from the page at position. 
  85.          * @param positionOffsetPixels Value in pixels indicating the offset from position. 
  86.          */  
  87.         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);  
  88.   
  89.         /** 
  90.          * This method will be invoked when a new page becomes selected. Animation is not 
  91.          * necessarily complete. 
  92.          * 
  93.          * @param position Position index of the new selected page. 
  94.          */  
  95.         public void onPageSelected(int position);  
  96.   
  97.     }  
  98.   
  99.     /** 
  100.      * Simple implementation of the {@link OnPageChangeListener} interface with stub 
  101.      * implementations of each method. Extend this if you do not intend to override 
  102.      * every method of {@link OnPageChangeListener}. 
  103.      */  
  104.     public static class SimpleOnPageChangeListener implements OnPageChangeListener {  
  105.   
  106.         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
  107.             // This space for rent  
  108.         }  
  109.   
  110.         public void onPageSelected(int position) {  
  111.             // This space for rent  
  112.         }  
  113.   
  114.         public void onPageScrollStateChanged(int state) {  
  115.             // This space for rent  
  116.         }  
  117.   
  118.     }  
  119.   
  120.     public CustomViewAbove(Context context) {  
  121.         this(context, null);  
  122.     }  
  123.   
  124.     public CustomViewAbove(Context context, AttributeSet attrs) {  
  125.         super(context, attrs);  
  126.         initCustomViewAbove();  
  127.     }  
  128.   
  129.     void initCustomViewAbove() {  
  130.         setWillNotDraw(false);  
  131.         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);  
  132.         setFocusable(true);  
  133.         final Context context = getContext();  
  134.         mScroller = new Scroller(context, sInterpolator);  
  135.         final ViewConfiguration configuration = ViewConfiguration.get(context);  
  136.         mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);  
  137.         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();  
  138.         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();  
  139.         setInternalPageChangeListener(new SimpleOnPageChangeListener() {  
  140.             public void onPageSelected(int position) {  
  141.                 if (mViewBehind != null) {  
  142.                     switch (position) {  
  143.                     case 0:  
  144.                     case 2:  
  145.                         mViewBehind.setChildrenEnabled(true);  
  146.                         break;  
  147.                     case 1:  
  148.                         mViewBehind.setChildrenEnabled(false);  
  149.                         break;  
  150.                     }  
  151.                 }  
  152.             }  
  153.         });  
  154.   
  155.         final float density = context.getResources().getDisplayMetrics().density;  
  156.         mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);  
  157.     }  
  158.   
  159.     /** 
  160.      * Set the currently selected page. If the CustomViewPager has already been through its first 
  161.      * layout there will be a smooth animated transition between the current item and the 
  162.      * specified item. 
  163.      * 
  164.      * @param item Item index to select 
  165.      */  
  166.     public void setCurrentItem(int item) {  
  167.         setCurrentItemInternal(item, truefalse);  
  168.     }  
  169.   
  170.     /** 
  171.      * Set the currently selected page. 
  172.      * 
  173.      * @param item Item index to select 
  174.      * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately 
  175.      */  
  176.     public void setCurrentItem(int item, boolean smoothScroll) {  
  177.         setCurrentItemInternal(item, smoothScroll, false);  
  178.     }  
  179.   
  180.     public int getCurrentItem() {  
  181.         return mCurItem;  
  182.     }  
  183.   
  184.     void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {  
  185.         setCurrentItemInternal(item, smoothScroll, always, 0);  
  186.     }  
  187.   
  188.     void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {  
  189.         if (!always && mCurItem == item) {  
  190.             setScrollingCacheEnabled(false);  
  191.             return;  
  192.         }  
  193.   
  194.         item = mViewBehind.getMenuPage(item);  
  195.   
  196.         final boolean dispatchSelected = mCurItem != item;  
  197.         mCurItem = item;  
  198.         final int destX = getDestScrollX(mCurItem);  
  199.         if (dispatchSelected && mOnPageChangeListener != null) {  
  200.             mOnPageChangeListener.onPageSelected(item);  
  201.         }  
  202.         if (dispatchSelected && mInternalPageChangeListener != null) {  
  203.             mInternalPageChangeListener.onPageSelected(item);  
  204.         }  
  205.         if (smoothScroll) {  
  206.             smoothScrollTo(destX, 0, velocity);  
  207.         } else {  
  208.             completeScroll();  
  209.             scrollTo(destX, 0);  
  210.         }  
  211.     }  
  212.   
  213.     /** 
  214.      * Set a listener that will be invoked whenever the page changes or is incrementally 
  215.      * scrolled. See {@link OnPageChangeListener}. 
  216.      * 
  217.      * @param listener Listener to set 
  218.      */  
  219.     public void setOnPageChangeListener(OnPageChangeListener listener) {  
  220.         mOnPageChangeListener = listener;  
  221.     }  
  222.     /* 
  223.     public void setOnOpenListener(OnOpenListener l) { 
  224.         mOpenListener = l; 
  225.     } 
  226.  
  227.     public void setOnCloseListener(OnCloseListener l) { 
  228.         mCloseListener = l; 
  229.     } 
  230.      */  
  231.     public void setOnOpenedListener(OnOpenedListener l) {  
  232.         mOpenedListener = l;  
  233.     }  
  234.   
  235.     public void setOnClosedListener(OnClosedListener l) {  
  236.         mClosedListener = l;  
  237.     }  
  238.   
  239.     /** 
  240.      * Set a separate OnPageChangeListener for internal use by the support library. 
  241.      * 
  242.      * @param listener Listener to set 
  243.      * @return The old listener that was set, if any. 
  244.      */  
  245.     OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {  
  246.         OnPageChangeListener oldListener = mInternalPageChangeListener;  
  247.         mInternalPageChangeListener = listener;  
  248.         return oldListener;  
  249.     }  
  250.   
  251.     public void addIgnoredView(View v) {  
  252.         if (!mIgnoredViews.contains(v)) {  
  253.             mIgnoredViews.add(v);  
  254.         }  
  255.     }  
  256.   
  257.     public void removeIgnoredView(View v) {  
  258.         mIgnoredViews.remove(v);  
  259.     }  
  260.   
  261.     public void clearIgnoredViews() {  
  262.         mIgnoredViews.clear();  
  263.     }  
  264.   
  265.     // We want the duration of the page snap animation to be influenced by the distance that  
  266.     // the screen has to travel, however, we don‘t want this duration to be effected in a  
  267.     // purely linear fashion. Instead, we use this method to moderate the effect that the distance  
  268.     // of travel has on the overall snap duration.  
  269.     float distanceInfluenceForSnapDuration(float f) {  
  270.         f -= 0.5f; // center the values about 0.  
  271.         f *= 0.3f * Math.PI / 2.0f;  
  272.         return (float) FloatMath.sin(f);  
  273.     }  
  274.   
  275.     public int getDestScrollX(int page) {  
  276.         switch (page) {  
  277.         case 0:  
  278.         case 2:  
  279.             return mViewBehind.getMenuLeft(mContent, page);  
  280.         case 1:  
  281.             return mContent.getLeft();  
  282.         }  
  283.         return 0;  
  284.     }  
  285.   
  286.     private int getLeftBound() {  
  287.         return mViewBehind.getAbsLeftBound(mContent);  
  288.     }  
  289.   
  290.     private int getRightBound() {  
  291.         return mViewBehind.getAbsRightBound(mContent);  
  292.     }  
  293.   
  294.     public int getContentLeft() {  
  295.         return mContent.getLeft() + mContent.getPaddingLeft();  
  296.     }  
  297.   
  298.     public boolean isMenuOpen() {  
  299.         return mCurItem == 0 || mCurItem == 2;  
  300.     }  
  301.   
  302.     private boolean isInIgnoredView(MotionEvent ev) {  
  303.         Rect rect = new Rect();  
  304.         for (View v : mIgnoredViews) {  
  305.             v.getHitRect(rect);  
  306.             if (rect.contains((int)ev.getX(), (int)ev.getY())) return true;  
  307.         }  
  308.         return false;  
  309.     }  
  310.   
  311.     public int getBehindWidth() {  
  312.         if (mViewBehind == null) {  
  313.             return 0;  
  314.         } else {  
  315.             return mViewBehind.getBehindWidth();  
  316.         }  
  317.     }  
  318.   
  319.     public int getChildWidth(int i) {  
  320.         switch (i) {  
  321.         case 0:  
  322.             return getBehindWidth();  
  323.         case 1:  
  324.             return mContent.getWidth();  
  325.         default:  
  326.             return 0;  
  327.         }  
  328.     }  
  329.   
  330.     public boolean isSlidingEnabled() {  
  331.         return mEnabled;  
  332.     }  
  333.   
  334.     public void setSlidingEnabled(boolean b) {  
  335.         mEnabled = b;  
  336.     }  
  337.   
  338.     /** 
  339.      * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 
  340.      * 
  341.      * @param x the number of pixels to scroll by on the X axis 
  342.      * @param y the number of pixels to scroll by on the Y axis 
  343.      */  
  344.     void smoothScrollTo(int x, int y) {  
  345.         smoothScrollTo(x, y, 0);  
  346.     }  
  347.   
  348.     /** 
  349.      * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 
  350.      * 
  351.      * @param x the number of pixels to scroll by on the X axis 
  352.      * @param y the number of pixels to scroll by on the Y axis 
  353.      * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) 
  354.      */  
  355.     void smoothScrollTo(int x, int y, int velocity) {  
  356.         if (getChildCount() == 0) {  
  357.             // Nothing to do.  
  358.             setScrollingCacheEnabled(false);  
  359.             return;  
  360.         }  
  361.         int sx = getScrollX();  
  362.         int sy = getScrollY();  
  363.         int dx = x - sx;  
  364.         int dy = y - sy;  
  365.         if (dx == 0 && dy == 0) {  
  366.             completeScroll();  
  367.             if (isMenuOpen()) {  
  368.                 if (mOpenedListener != null)  
  369.                     mOpenedListener.onOpened();  
  370.             } else {  
  371.                 if (mClosedListener != null)  
  372.                     mClosedListener.onClosed();  
  373.             }  
  374.             return;  
  375.         }  
  376.   
  377.         setScrollingCacheEnabled(true);  
  378.         mScrolling = true;  
  379.   
  380.         final int width = getBehindWidth();  
  381.         final int halfWidth = width / 2;  
  382.         final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);  
  383.         final float distance = halfWidth + halfWidth *  
  384.                 distanceInfluenceForSnapDuration(distanceRatio);  
  385.   
  386.         int duration = 0;  
  387.         velocity = Math.abs(velocity);  
  388.         if (velocity > 0) {  
  389.             duration = 4 * Math.round(1000 * Math.abs(distance / velocity));  
  390.         } else {  
  391.             final float pageDelta = (float) Math.abs(dx) / width;  
  392.             duration = (int) ((pageDelta + 1) * 100);  
  393.             duration = MAX_SETTLE_DURATION;  
  394.         }  
  395.         duration = Math.min(duration, MAX_SETTLE_DURATION);  
  396.   
  397.         mScroller.startScroll(sx, sy, dx, dy, duration);  
  398.         invalidate();  
  399.     }  
  400.   
  401.     public void setContent(View v) {  
  402.         if (mContent != null)   
  403.             this.removeView(mContent);  
  404.         mContent = v;  
  405.         addView(mContent);  
  406.     }  
  407.   
  408.     public View getContent() {  
  409.         return mContent;  
  410.     }  
  411.   
  412.     public void setCustomViewBehind(CustomViewBehind cvb) {  
  413.         mViewBehind = cvb;  
  414.     }  
  415.   
  416.     @Override  
  417.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  418.   
  419.         int width = getDefaultSize(0, widthMeasureSpec);  
  420.         int height = getDefaultSize(0, heightMeasureSpec);  
  421.         setMeasuredDimension(width, height);  
  422.   
  423.         final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);  
  424.         final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);  
  425.         mContent.measure(contentWidth, contentHeight);  
  426.     }  
  427.   
  428.     @Override  
  429.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  430.         super.onSizeChanged(w, h, oldw, oldh);  
  431.         // Make sure scroll position is set correctly.  
  432.         if (w != oldw) {  
  433.             // [ChrisJ] - This fixes the onConfiguration change for orientation issue..  
  434.             // maybe worth having a look why the recomputeScroll pos is screwing  
  435.             // up?  
  436.             completeScroll();  
  437.             scrollTo(getDestScrollX(mCurItem), getScrollY());  
  438.         }  
  439.     }  
  440.   
  441.     @Override  
  442.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  443.         final int width = r - l;  
  444.         final int height = b - t;  
  445.         mContent.layout(00, width, height);  
  446.     }  
  447.   
  448.     public void setAboveOffset(int i) {  
  449.         //      RelativeLayout.LayoutParams params = ((RelativeLayout.LayoutParams)mContent.getLayoutParams());  
  450.         //      params.setMargins(i, params.topMargin, params.rightMargin, params.bottomMargin);  
  451.         mContent.setPadding(i, mContent.getPaddingTop(),   
  452.                 mContent.getPaddingRight(), mContent.getPaddingBottom());  
  453.     }  
  454.   
  455.   
  456.     @Override  
  457.     public void computeScroll() {  
  458.         if (!mScroller.isFinished()) {  
  459.             if (mScroller.computeScrollOffset()) {  
  460.                 int oldX = getScrollX();  
  461.                 int oldY = getScrollY();  
  462.                 int x = mScroller.getCurrX();  
  463.                 int y = mScroller.getCurrY();  
  464.   
  465.                 if (oldX != x || oldY != y) {  
  466.                     scrollTo(x, y);  
  467.                     pageScrolled(x);  
  468.                 }  
  469.   
  470.                 // Keep on drawing until the animation has finished.  
  471.                 invalidate();  
  472.                 return;  
  473.             }  
  474.         }  
  475.   
  476.         // Done with scroll, clean up state.  
  477.         completeScroll();  
  478.     }  
  479.   
  480.     private void pageScrolled(int xpos) {  
  481.         final int widthWithMargin = getWidth();  
  482.         final int position = xpos / widthWithMargin;  
  483.         final int offsetPixels = xpos % widthWithMargin;  
  484.         final float offset = (float) offsetPixels / widthWithMargin;  
  485.   
  486.         onPageScrolled(position, offset, offsetPixels);  
  487.     }  
  488.   
  489.     /** 
  490.      * This method will be invoked when the current page is scrolled, either as part 
  491.      * of a programmatically initiated smooth scroll or a user initiated touch scroll. 
  492.      * If you override this method you must call through to the superclass implementation 
  493.      * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled 
  494.      * returns. 
  495.      * 
  496.      * @param position Position index of the first page currently being displayed. 
  497.      *                 Page position+1 will be visible if positionOffset is nonzero. 
  498.      * @param offset Value from [0, 1) indicating the offset from the page at position. 
  499.      * @param offsetPixels Value in pixels indicating the offset from position. 
  500.      */  
  501.     protected void onPageScrolled(int position, float offset, int offsetPixels) {  
  502.         if (mOnPageChangeListener != null) {  
  503.             mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);  
  504.         }  
  505.         if (mInternalPageChangeListener != null) {  
  506.             mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);  
  507.         }  
  508.     }  
  509.   
  510.     private void completeScroll() {  
  511.         boolean needPopulate = mScrolling;  
  512.         if (needPopulate) {  
  513.             // Done with scroll, no longer want to cache view drawing.  
  514.             setScrollingCacheEnabled(false);  
  515.             mScroller.abortAnimation();  
  516.             int oldX = getScrollX();  
  517.             int oldY = getScrollY();  
  518.             int x = mScroller.getCurrX();  
  519.             int y = mScroller.getCurrY();  
  520.             if (oldX != x || oldY != y) {  
  521.                 scrollTo(x, y);  
  522.             }  
  523.             if (isMenuOpen()) {  
  524.                 if (mOpenedListener != null)  
  525.                     mOpenedListener.onOpened();  
  526.             } else {  
  527.                 if (mClosedListener != null)  
  528.                     mClosedListener.onClosed();  
  529.             }  
  530.         }  
  531.         mScrolling = false;  
  532.     }  
  533.   
  534.     protected int mTouchMode = SlidingMenu.TOUCHMODE_MARGIN;  
  535.   
  536.     public void setTouchMode(int i) {  
  537.         mTouchMode = i;  
  538.     }  
  539.   
  540.     public int getTouchMode() {  
  541.         return mTouchMode;  
  542.     }  
  543.   
  544.     private boolean thisTouchAllowed(MotionEvent ev) {  
  545.         int x = (int) (ev.getX() + mScrollX);  
  546.         if (isMenuOpen()) {  
  547.             return mViewBehind.menuOpenTouchAllowed(mContent, mCurItem, x);  
  548.         } else {  
  549.             switch (mTouchMode) {  
  550.             case SlidingMenu.TOUCHMODE_FULLSCREEN:  
  551.                 return !isInIgnoredView(ev);  
  552.             case SlidingMenu.TOUCHMODE_NONE:  
  553.                 return false;  
  554.             case SlidingMenu.TOUCHMODE_MARGIN:  
  555.                 return mViewBehind.marginTouchAllowed(mContent, x);  
  556.             }  
  557.         }  
  558.         return false;  
  559.     }  
  560.   
  561.     private boolean thisSlideAllowed(float dx) {  
  562.         boolean allowed = false;  
  563.         if (isMenuOpen()) {  
  564.             allowed = mViewBehind.menuOpenSlideAllowed(dx);  
  565.         } else {  
  566.             allowed = mViewBehind.menuClosedSlideAllowed(dx);  
  567.         }  
  568.         if (DEBUG)  
  569.             Log.v(TAG, "this slide allowed " + allowed + " dx: " + dx);  
  570.         return allowed;  
  571.     }  
  572.   
  573.     private int getPointerIndex(MotionEvent ev, int id) {  
  574.         int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);  
  575.         if (activePointerIndex == -1)  
  576.             mActivePointerId = INVALID_POINTER;  
  577.         return activePointerIndex;  
  578.     }  
  579.   
  580.     private boolean mQuickReturn = false;  
  581.   
  582.     @Override  
  583.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  584.   
  585.         if (!mEnabled)  
  586.             return false;  
  587.   
  588.         final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;  
  589.   
  590.         if (DEBUG)  
  591.             if (action == MotionEvent.ACTION_DOWN)  
  592.                 Log.v(TAG, "Received ACTION_DOWN");  
  593.   
  594.         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP  
  595.                 || (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) {  
  596.             endDrag();  
  597.             return false;  
  598.         }  
  599.   
  600.         switch (action) {  
  601.         case MotionEvent.ACTION_MOVE:  
  602.             determineDrag(ev);  
  603.             break;  
  604.         case MotionEvent.ACTION_DOWN:  
  605.             int index = MotionEventCompat.getActionIndex(ev);  
  606.             mActivePointerId = MotionEventCompat.getPointerId(ev, index);  
  607.             if (mActivePointerId == INVALID_POINTER)  
  608.                 break;  
  609.             mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);  
  610.             mLastMotionY = MotionEventCompat.getY(ev, index);  
  611.             if (thisTouchAllowed(ev)) {  
  612.                 mIsBeingDragged = false;  
  613.                 mIsUnableToDrag = false;  
  614.                 if (isMenuOpen() && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {  
  615.                     mQuickReturn = true;  
  616.                 }  
  617.             } else {  
  618.                 mIsUnableToDrag = true;  
  619.             }  
  620.             break;  
  621.         case MotionEventCompat.ACTION_POINTER_UP:  
  622.             onSecondaryPointerUp(ev);  
  623.             break;  
  624.         }  
  625.   
  626.         if (!mIsBeingDragged) {  
  627.             if (mVelocityTracker == null) {  
  628.                 mVelocityTracker = VelocityTracker.obtain();  
  629.             }  
  630.             mVelocityTracker.addMovement(ev);  
  631.         }  
  632.         return mIsBeingDragged || mQuickReturn;  
  633.     }  
  634.   
  635.   
  636.     @Override  
  637.     public boolean onTouchEvent(MotionEvent ev) {  
  638.   
  639.         if (!mEnabled)  
  640.             return false;  
  641.   
  642.         if (!mIsBeingDragged && !thisTouchAllowed(ev))  
  643.             return false;  
  644.   
  645.         //      if (!mIsBeingDragged && !mQuickReturn)  
  646.         //          return false;  
  647.   
  648.         final int action = ev.getAction();  
  649.   
  650.         if (mVelocityTracker == null) {  
  651.             mVelocityTracker = VelocityTracker.obtain();  
  652.         }  
  653.         mVelocityTracker.addMovement(ev);  
  654.   
  655.         switch (action & MotionEventCompat.ACTION_MASK) {  
  656.         case MotionEvent.ACTION_DOWN:  
  657.             /* 
  658.              * If being flinged and user touches, stop the fling. isFinished 
  659.              * will be false if being flinged. 
  660.              */  
  661.             completeScroll();  
  662.   
  663.             // Remember where the motion event started  
  664.             int index = MotionEventCompat.getActionIndex(ev);  
  665.             mActivePointerId = MotionEventCompat.getPointerId(ev, index);  
  666.             mLastMotionX = mInitialMotionX = ev.getX();  
  667.             break;  
  668.         case MotionEvent.ACTION_MOVE:  
  669.             if (!mIsBeingDragged) {   
  670.                 determineDrag(ev);  
  671.                 if (mIsUnableToDrag)  
  672.                     return false;  
  673.             }  
  674.             if (mIsBeingDragged) {  
  675.                 // Scroll to follow the motion event  
  676.                 final int activePointerIndex = getPointerIndex(ev, mActivePointerId);  
  677.                 if (mActivePointerId == INVALID_POINTER)  
  678.                     break;  
  679.                 final float x = MotionEventCompat.getX(ev, activePointerIndex);  
  680.                 final float deltaX = mLastMotionX - x;  
  681.                 mLastMotionX = x;  
  682.                 float oldScrollX = getScrollX();  
  683.                 float scrollX = oldScrollX + deltaX;  
  684.                 final float leftBound = getLeftBound();  
  685.                 final float rightBound = getRightBound();  
  686.                 if (scrollX < leftBound) {  
  687.                     scrollX = leftBound;  
  688.                 } else if (scrollX > rightBound) {  
  689.                     scrollX = rightBound;  
  690.                 }  
  691.                 // Don‘t lose the rounded component  
  692.                 mLastMotionX += scrollX - (int) scrollX;  
  693.                 scrollTo((int) scrollX, getScrollY());  
  694.                 pageScrolled((int) scrollX);  
  695.             }  
  696.             break;  
  697.         case MotionEvent.ACTION_UP:  
  698.             if (mIsBeingDragged) {  
  699.                 final VelocityTracker velocityTracker = mVelocityTracker;  
  700.                 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
  701.                 int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(  
  702.                         velocityTracker, mActivePointerId);  
  703.                 final int scrollX = getScrollX();  
  704.                 final float pageOffset = (float) (scrollX - getDestScrollX(mCurItem)) / getBehindWidth();  
  705.                 final int activePointerIndex = getPointerIndex(ev, mActivePointerId);  
  706.                 if (mActivePointerId != INVALID_POINTER) {  
  707.                     final float x = MotionEventCompat.getX(ev, activePointerIndex);  
  708.                     final int totalDelta = (int) (x - mInitialMotionX);  
  709.                     int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);  
  710.                     setCurrentItemInternal(nextPage, truetrue, initialVelocity);  
  711.                 } else {      
  712.                     setCurrentItemInternal(mCurItem, truetrue, initialVelocity);  
  713.                 }  
  714.                 mActivePointerId = INVALID_POINTER;  
  715.                 endDrag();  
  716.             } else if (mQuickReturn && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {  
  717.                 // close the menu  
  718.                 setCurrentItem(1);  
  719.                 endDrag();  
  720.             }  
  721.             break;  
  722.         case MotionEvent.ACTION_CANCEL:  
  723.             if (mIsBeingDragged) {  
  724.                 setCurrentItemInternal(mCurItem, truetrue);  
  725.                 mActivePointerId = INVALID_POINTER;  
  726.                 endDrag();  
  727.             }  
  728.             break;  
  729.         case MotionEventCompat.ACTION_POINTER_DOWN: {  
  730.             final int indexx = MotionEventCompat.getActionIndex(ev);  
  731.             mLastMotionX = MotionEventCompat.getX(ev, indexx);  
  732.             mActivePointerId = MotionEventCompat.getPointerId(ev, indexx);  
  733.             break;  
  734.         }  
  735.         case MotionEventCompat.ACTION_POINTER_UP:  
  736.             onSecondaryPointerUp(ev);  
  737.             int pointerIndex = getPointerIndex(ev, mActivePointerId);  
  738.             if (mActivePointerId == INVALID_POINTER)  
  739.                 break;  
  740.             mLastMotionX = MotionEventCompat.getX(ev, pointerIndex);  
  741.             break;  
  742.         }  
  743.         return true;  
  744.     }  
  745.       
  746.     private void determineDrag(MotionEvent ev) {  
  747.         final int activePointerId = mActivePointerId;  
  748.         final int pointerIndex = getPointerIndex(ev, activePointerId);  
  749.         if (activePointerId == INVALID_POINTER || pointerIndex == INVALID_POINTER)  
  750.             return;  
  751.         final float x = MotionEventCompat.getX(ev, pointerIndex);  
  752.         final float dx = x - mLastMotionX;  
  753.         final float xDiff = Math.abs(dx);  
  754.         final float y = MotionEventCompat.getY(ev, pointerIndex);  
  755.         final float dy = y - mLastMotionY;  
  756.         final float yDiff = Math.abs(dy);  
  757.         if (xDiff > (isMenuOpen()?mTouchSlop/2:mTouchSlop) && xDiff > yDiff && thisSlideAllowed(dx)) {          
  758.             startDrag();  
  759.             mLastMotionX = x;  
  760.             mLastMotionY = y;  
  761.             setScrollingCacheEnabled(true);  
  762.             // TODO add back in touch slop check  
  763.         } else if (xDiff > mTouchSlop) {  
  764.             mIsUnableToDrag = true;  
  765.         }  
  766.     }  
  767.   
  768.     @Override  
  769.     public void scrollTo(int x, int y) {  
  770.         super.scrollTo(x, y);  
  771.         mScrollX = x;  
  772.         mViewBehind.scrollBehindTo(mContent, x, y);   
  773.        ((SlidingMenu)getParent()).manageLayers(getPercentOpen());  
  774.           
  775.         if (mTransformer != null) {  
  776.             invalidate();  
  777.         }  
  778.     }  
  779.   
  780.     private int determineTargetPage(float pageOffset, int velocity, int deltaX) {  
  781.         int targetPage = mCurItem;  
  782.         if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {  
  783.             if (velocity > 0 && deltaX > 0) {  
  784.                 targetPage -= 1;  
  785.             } else if (velocity < 0 && deltaX < 0){  
  786.                 targetPage += 1;  
  787.             }  
  788.         } else {  
  789.             targetPage = (int) Math.round(mCurItem + pageOffset);  
  790.         }  
  791.         return targetPage;  
  792.     }  
  793.   
  794.     protected float getPercentOpen() {  
  795.         return Math.abs(mScrollX-mContent.getLeft()) / getBehindWidth();  
  796.     }  
  797.   
  798.     @Override  
  799.     protected void dispatchDraw(Canvas canvas) {  
  800.         // 这句要注释掉,否则会出现2个右侧的视图,一个有转场动画,一个没有转场动画  
  801.                 // super.dispatchDraw(canvas);  
  802.         // Draw the margin drawable if needed.  
  803.         mViewBehind.drawShadow(mContent, canvas);  
  804.         mViewBehind.drawFade(mContent, canvas, getPercentOpen());  
  805.         mViewBehind.drawSelector(mContent, canvas, getPercentOpen());  
  806.         // 设置右侧视图的转场效果,主要是修改Canvas。  
  807.         if (mTransformer != null) {  
  808.             canvas.save();  
  809.             mTransformer.transformCanvas(canvas, getPercentOpen());  
  810.             super.dispatchDraw(canvas);  
  811.             canvas.restore();  
  812.         } else {  
  813.             super.dispatchDraw(canvas);  
  814.         }  
  815.     }  
  816.   
  817.     // variables for drawing  
  818.     private float mScrollX = 0.0f;  
  819.   
  820.     private void onSecondaryPointerUp(MotionEvent ev) {  
  821.         if (DEBUG) Log.v(TAG, "onSecondaryPointerUp called");  
  822.         final int pointerIndex = MotionEventCompat.getActionIndex(ev);  
  823.         final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);  
  824.         if (pointerId == mActivePointerId) {  
  825.             // This was our active pointer going up. Choose a new  
  826.             // active pointer and adjust accordingly.  
  827.             final int newPointerIndex = pointerIndex == 0 ? 1 : 0;  
  828.             mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);  
  829.             mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);  
  830.             if (mVelocityTracker != null) {  
  831.                 mVelocityTracker.clear();  
  832.             }  
  833.         }  
  834.     }  
  835.   
  836.     private void startDrag() {  
  837.         mIsBeingDragged = true;  
  838.         mQuickReturn = false;  
  839.     }  
  840.   
  841.     private void endDrag() {  
  842.         mQuickReturn = false;  
  843.         mIsBeingDragged = false;  
  844.         mIsUnableToDrag = false;  
  845.         mActivePointerId = INVALID_POINTER;  
  846.   
  847.         if (mVelocityTracker != null) {  
  848.             mVelocityTracker.recycle();  
  849.             mVelocityTracker = null;  
  850.         }  
  851.     }  
  852.   
  853.     private void setScrollingCacheEnabled(boolean enabled) {  
  854.         if (mScrollingCacheEnabled != enabled) {  
  855.             mScrollingCacheEnabled = enabled;  
  856.             if (USE_CACHE) {  
  857.                 final int size = getChildCount();  
  858.                 for (int i = 0; i < size; ++i) {  
  859.                     final View child = getChildAt(i);  
  860.                     if (child.getVisibility() != GONE) {  
  861.                         child.setDrawingCacheEnabled(enabled);  
  862.                     }  
  863.                 }  
  864.             }  
  865.         }  
  866.     }  
  867.   
  868.     /** 
  869.      * Tests scrollability within child views of v given a delta of dx. 
  870.      * 
  871.      * @param v View to test for horizontal scrollability 
  872.      * @param checkV Whether the view v passed should itself be checked for scrollability (true), 
  873.      *               or just its children (false). 
  874.      * @param dx Delta scrolled in pixels 
  875.      * @param x X coordinate of the active touch point 
  876.      * @param y Y coordinate of the active touch point 
  877.      * @return true if child views of v can be scrolled by delta of dx. 
  878.      */  
  879.     protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {  
  880.         if (v instanceof ViewGroup) {  
  881.             final ViewGroup group = (ViewGroup) v;  
  882.             final int scrollX = v.getScrollX();  
  883.             final int scrollY = v.getScrollY();  
  884.             final int count = group.getChildCount();  
  885.             // Count backwards - let topmost views consume scroll distance first.  
  886.             for (int i = count - 1; i >= 0; i--) {  
  887.                 final View child = group.getChildAt(i);  
  888.                 if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&  
  889.                         y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&  
  890.                         canScroll(child, true, dx, x + scrollX - child.getLeft(),  
  891.                                 y + scrollY - child.getTop())) {  
  892.                     return true;  
  893.                 }  
  894.             }  
  895.         }  
  896.   
  897.         return checkV && ViewCompat.canScrollHorizontally(v, -dx);  
  898.     }  
  899.   
  900.   
  901.     @Override  
  902.     public boolean dispatchKeyEvent(KeyEvent event) {  
  903.         // Let the focused view and/or our descendants get the key first  
  904.         return super.dispatchKeyEvent(event) || executeKeyEvent(event);  
  905.     }  
  906.   
  907.     /** 
  908.      * You can call this function yourself to have the scroll view perform 
  909.      * scrolling from a key event, just as if the event had been dispatched to 
  910.      * it by the view hierarchy. 
  911.      * 
  912.      * @param event The key event to execute. 
  913.      * @return Return true if the event was handled, else false. 
  914.      */  
  915.     public boolean executeKeyEvent(KeyEvent event) {  
  916.         boolean handled = false;  
  917.         if (event.getAction() == KeyEvent.ACTION_DOWN) {  
  918.             switch (event.getKeyCode()) {  
  919.             case KeyEvent.KEYCODE_DPAD_LEFT:  
  920.                 handled = arrowScroll(FOCUS_LEFT);  
  921.                 break;  
  922.             case KeyEvent.KEYCODE_DPAD_RIGHT:  
  923.                 handled = arrowScroll(FOCUS_RIGHT);  
  924.                 break;  
  925.             case KeyEvent.KEYCODE_TAB:  
  926.                 if (Build.VERSION.SDK_INT >= 11) {  
  927.                     // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD  
  928.                     // before Android 3.0. Ignore the tab key on those devices.  
  929.                     if (KeyEventCompat.hasNoModifiers(event)) {  
  930.                         handled = arrowScroll(FOCUS_FORWARD);  
  931.                     } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {  
  932.                         handled = arrowScroll(FOCUS_BACKWARD);  
  933.                     }  
  934.                 }  
  935.                 break;  
  936.             }  
  937.         }  
  938.         return handled;  
  939.     }  
  940.   
  941.     public boolean arrowScroll(int direction) {  
  942.         View currentFocused = findFocus();  
  943.         if (currentFocused == this) currentFocused = null;  
  944.   
  945.         boolean handled = false;  
  946.   
  947.         View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,  
  948.                 direction);  
  949.         if (nextFocused != null && nextFocused != currentFocused) {  
  950.             if (direction == View.FOCUS_LEFT) {  
  951.                 handled = nextFocused.requestFocus();  
  952.             } else if (direction == View.FOCUS_RIGHT) {  
  953.                 // If there is nothing to the right, or this is causing us to  
  954.                 // jump to the left, then what we really want to do is page right.  
  955.                 if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) {  
  956.                     handled = pageRight();  
  957.                 } else {  
  958.                     handled = nextFocused.requestFocus();  
  959.                 }  
  960.             }  
  961.         } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {  
  962.             // Trying to move left and nothing there; try to page.  
  963.             handled = pageLeft();  
  964.         } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {  
  965.             // Trying to move right and nothing there; try to page.  
  966.             handled = pageRight();  
  967.         }  
  968.         if (handled) {  
  969.             playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));  
  970.         }  
  971.         return handled;  
  972.     }  
  973.   
  974.     boolean pageLeft() {  
  975.         if (mCurItem > 0) {  
  976.             setCurrentItem(mCurItem-1true);  
  977.             return true;  
  978.         }  
  979.         return false;  
  980.     }  
  981.   
  982.     boolean pageRight() {  
  983.         if (mCurItem < 1) {  
  984.             setCurrentItem(mCurItem+1true);  
  985.             return true;  
  986.         }  
  987.         return false;  
  988.     }  
  989.       
  990.     public void setCanvasTransformer(CanvasTransformer t) {  
  991.         mTransformer = t;  
  992.     }  
  993.       
  994. }  
如果想要使用这个侧滑菜单的动画效果,直接替换这两个类即可。同时,并不会影响SlidingMenu的固有功能。

下面看看如何配置SlidingMenu实例。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. SlidingMenu sm = getSlidingMenu();  
  2. sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);  
  3. sm.setFadeEnabled(false);  
  4. sm.setBehindScrollScale(0.25f);  
  5. sm.setFadeDegree(0.25f);  
  6.   
  7. // 配置背景图片  
  8. sm.setBackgroundImage(R.drawable.img_frame_background);  
  9. // 设置专场动画效果  
  10. sm.setBehindCanvasTransformer(new SlidingMenu.CanvasTransformer() {  
  11.     @Override  
  12.     public void transformCanvas(Canvas canvas, float percentOpen) {  
  13.         float scale = (float) (percentOpen * 0.25 + 0.75);  
  14.         canvas.scale(scale, scale, -canvas.getWidth() / 2,  
  15.                 canvas.getHeight() / 2);  
  16.     }  
  17. });  
  18.   
  19. sm.setAboveCanvasTransformer(new SlidingMenu.CanvasTransformer() {  
  20.     @Override  
  21.     public void transformCanvas(Canvas canvas, float percentOpen) {  
  22.         float scale = (float) (1 - percentOpen * 0.25);  
  23.         canvas.scale(scale, scale, 0, canvas.getHeight() / 2);  
  24.     }  
  25. });  

大功告成!

最后,附上Demo的下载地址。

GitHub https://github.com/sunguowei/Android-ResideMenu

CSDN资源   http://download.csdn.net/detail/manoel/7857771

百度网盘    http://pan.baidu.com/s/1jGrASui


关于这个框架后期的优化,请关注我的github。

https://github.com/sunguowei

Okay,要说的就这么多,希望能给大家带来一些帮助。

【案例分享】仿QQ5.0侧滑菜单ResideMenu