前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >仿qq最新侧滑菜单

仿qq最新侧滑菜单

作者头像
xiangzhihong
发布2018-01-30 10:33:15
3K0
发布2018-01-30 10:33:15
举报
文章被收录于专栏:向治洪

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

我把这个项目上传到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
复制
public class SlidingMenu extends RelativeLayout {  
 
 private static final String TAG = SlidingMenu.class.getSimpleName();  
 
 public static final int SLIDING_WINDOW = 0;  
 public static final int SLIDING_CONTENT = 1;  
 private boolean mActionbarOverlay = false;  
 
 /** 
     * Constant value for use with setTouchModeAbove(). Allows the SlidingMenu 
     * to be opened with a swipe gesture on the screen's margin 
     */ 
 public static final int TOUCHMODE_MARGIN = 0;  
 
 /** 
     * Constant value for use with setTouchModeAbove(). Allows the SlidingMenu 
     * to be opened with a swipe gesture anywhere on the screen 
     */ 
 public static final int TOUCHMODE_FULLSCREEN = 1;  
 
 /** 
     * Constant value for use with setTouchModeAbove(). Denies the SlidingMenu 
     * to be opened with a swipe gesture 
     */ 
 public static final int TOUCHMODE_NONE = 2;  
 
 /** 
     * Constant value for use with setMode(). Puts the menu to the left of the 
     * content. 
     */ 
 public static final int LEFT = 0;  
 
 /** 
     * Constant value for use with setMode(). Puts the menu to the right of the 
     * content. 
     */ 
 public static final int RIGHT = 1;  
 
 /** 
     * Constant value for use with setMode(). Puts menus to the left and right 
     * of the content. 
     */ 
 public static final int LEFT_RIGHT = 2;  
 
 private CustomViewAbove mViewAbove;  
 
 private CustomViewBehind mViewBehind;  
 
 /** 整体的背景,用一个ImageView代替 */ 
 private ImageView mViewBackground;  
 
 private OnOpenListener mOpenListener;  
 
 private OnOpenListener mSecondaryOpenListner;  
 
 private OnCloseListener mCloseListener;  
 
 /** 
     * The listener interface for receiving onOpen events. The class that is 
     * interested in processing a onOpen event implements this interface, and 
     * the object created with that class is registered with a component using 
     * the component's <code>addOnOpenListener<code> method. When 
     * the onOpen event occurs, that object's appropriate 
     * method is invoked 
     */ 
 public interface OnOpenListener {  
 
 /** 
         * On open. 
         */ 
 public void onOpen();  
    }  
 
 /** 
     * The listener interface for receiving onOpened events. The class that is 
     * interested in processing a onOpened event implements this interface, and 
     * the object created with that class is registered with a component using 
     * the component's <code>addOnOpenedListener<code> method. When 
     * the onOpened event occurs, that object's appropriate 
     * method is invoked. 
     *  
     * @see OnOpenedEvent 
     */ 
 public interface OnOpenedListener {  
 
 /** 
         * On opened. 
         */ 
 public void onOpened();  
    }  
 
 /** 
     * The listener interface for receiving onClose events. The class that is 
     * interested in processing a onClose event implements this interface, and 
     * the object created with that class is registered with a component using 
     * the component's <code>addOnCloseListener<code> method. When 
     * the onClose event occurs, that object's appropriate 
     * method is invoked. 
     *  
     * @see OnCloseEvent 
     */ 
 public interface OnCloseListener {  
 
 /** 
         * On close. 
         */ 
 public void onClose();  
    }  
 
 /** 
     * The listener interface for receiving onClosed events. The class that is 
     * interested in processing a onClosed event implements this interface, and 
     * the object created with that class is registered with a component using 
     * the component's <code>addOnClosedListener<code> method. When 
     * the onClosed event occurs, that object's appropriate 
     * method is invoked. 
     *  
     * @see OnClosedEvent 
     */ 
 public interface OnClosedListener {  
 
 /** 
         * On closed. 
         */ 
 public void onClosed();  
    }  
 
 /** 
     * The Interface CanvasTransformer. 
     */ 
 public interface CanvasTransformer {  
 
 /** 
         * Transform canvas. 
         *  
         * @param canvas 
         *            the canvas 
         * @param percentOpen 
         *            the percent open 
         */ 
 public void transformCanvas(Canvas canvas, float percentOpen);  
    }  
 
 /** 
     * Instantiates a new SlidingMenu. 
     *  
     * @param context 
     *            the associated Context 
     */ 
 public SlidingMenu(Context context) {  
 this(context, null);  
    }  
 
 /** 
     * Instantiates a new SlidingMenu and attach to Activity. 
     *  
     * @param activity 
     *            the activity to attach slidingmenu 
     * @param slideStyle 
     *            the slidingmenu style 
     */ 
 public SlidingMenu(Activity activity, int slideStyle) {  
 this(activity, null);  
 this.attachToActivity(activity, slideStyle);  
    }  
 
 /** 
     * Instantiates a new SlidingMenu. 
     *  
     * @param context 
     *            the associated Context 
     * @param attrs 
     *            the attrs 
     */ 
 public SlidingMenu(Context context, AttributeSet attrs) {  
 this(context, attrs, 0);  
    }  
 
 /** 
     * Instantiates a new SlidingMenu. 
     *  
     * @param context 
     *            the associated Context 
     * @param attrs 
     *            the attrs 
     * @param defStyle 
     *            the def style 
     */ 
 public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {  
 super(context, attrs, defStyle);  
 /** SlidingMenu是一个RelativeLayout,这里把背景图ImageView添加到RelativeLayout的最底层。*/ 
        LayoutParams backgroundParams = new LayoutParams(  
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);  
        mViewBackground = new ImageView(context);  
        mViewBackground.setScaleType(ImageView.ScaleType.CENTER_CROP);  
        addView(mViewBackground, backgroundParams);  
 
                LayoutParams behindParams = new LayoutParams(LayoutParams.MATCH_PARENT,  
                LayoutParams.MATCH_PARENT);  
        mViewBehind = new CustomViewBehind(context);  
        addView(mViewBehind, behindParams);  
        LayoutParams aboveParams = new LayoutParams(LayoutParams.MATCH_PARENT,  
                LayoutParams.MATCH_PARENT);  
        mViewAbove = new CustomViewAbove(context);  
        addView(mViewAbove, aboveParams);  
 // register the CustomViewBehind with the CustomViewAbove 
        mViewAbove.setCustomViewBehind(mViewBehind);  
        mViewBehind.setCustomViewAbove(mViewAbove);  
        mViewAbove.setOnPageChangeListener(new OnPageChangeListener() {  
 public static final int POSITION_OPEN = 0;  
 public static final int POSITION_CLOSE = 1;  
 public static final int POSITION_SECONDARY_OPEN = 2;  
 
 public void onPageScrolled(int position, float positionOffset,  
 int positionOffsetPixels) {  
            }  
 
 public void onPageSelected(int position) {  
 if (position == POSITION_OPEN && mOpenListener != null) {  
                    mOpenListener.onOpen();  
                } else if (position == POSITION_CLOSE && mCloseListener != null) {  
                    mCloseListener.onClose();  
                } else if (position == POSITION_SECONDARY_OPEN  
                        && mSecondaryOpenListner != null) {  
                    mSecondaryOpenListner.onOpen();  
                }  
            }  
        });  
 
 // now style everything! 
        TypedArray ta = context.obtainStyledAttributes(attrs,  
                R.styleable.SlidingMenu);  
 // set the above and behind views if defined in xml 
 int mode = ta.getInt(R.styleable.SlidingMenu_mode, LEFT);  
        setMode(mode);  
 int viewAbove = ta.getResourceId(R.styleable.SlidingMenu_viewAbove, -1);  
 if (viewAbove != -1) {  
            setContent(viewAbove);  
        } else {  
            setContent(new FrameLayout(context));  
        }  
 int viewBehind = ta.getResourceId(R.styleable.SlidingMenu_viewBehind,  
                -1);  
 if (viewBehind != -1) {  
            setMenu(viewBehind);  
        } else {  
            setMenu(new FrameLayout(context));  
        }  
 int touchModeAbove = ta.getInt(R.styleable.SlidingMenu_touchModeAbove,  
                TOUCHMODE_MARGIN);  
        setTouchModeAbove(touchModeAbove);  
 int touchModeBehind = ta.getInt(  
                R.styleable.SlidingMenu_touchModeBehind, TOUCHMODE_MARGIN);  
        setTouchModeBehind(touchModeBehind);  
 
 int offsetBehind = (int) ta.getDimension(  
                R.styleable.SlidingMenu_behindOffset, -1);  
 int widthBehind = (int) ta.getDimension(  
                R.styleable.SlidingMenu_behindWidth, -1);  
 if (offsetBehind != -1 && widthBehind != -1)  
 throw new IllegalStateException(  
 "Cannot set both behindOffset and behindWidth for a SlidingMenu");  
 else if (offsetBehind != -1)  
            setBehindOffset(offsetBehind);  
 else if (widthBehind != -1)  
            setBehindWidth(widthBehind);  
 else 
            setBehindOffset(0);  
 float scrollOffsetBehind = ta.getFloat(  
                R.styleable.SlidingMenu_behindScrollScale, 0.33f);  
        setBehindScrollScale(scrollOffsetBehind);  
 int shadowRes = ta.getResourceId(  
                R.styleable.SlidingMenu_shadowDrawable, -1);  
 if (shadowRes != -1) {  
            setShadowDrawable(shadowRes);  
        }  
 int shadowWidth = (int) ta.getDimension(  
                R.styleable.SlidingMenu_shadowWidth, 0);  
        setShadowWidth(shadowWidth);  
 boolean fadeEnabled = ta.getBoolean(  
                R.styleable.SlidingMenu_fadeEnabled, true);  
        setFadeEnabled(fadeEnabled);  
 float fadeDeg = ta.getFloat(R.styleable.SlidingMenu_fadeDegree, 0.33f);  
        setFadeDegree(fadeDeg);  
 boolean selectorEnabled = ta.getBoolean(  
                R.styleable.SlidingMenu_selectorEnabled, false);  
        setSelectorEnabled(selectorEnabled);  
 int selectorRes = ta.getResourceId(  
                R.styleable.SlidingMenu_selectorDrawable, -1);  
 if (selectorRes != -1)  
            setSelectorDrawable(selectorRes);  
        ta.recycle();  
    }  
 
 /** 
     * Attaches the SlidingMenu to an entire Activity 
     *  
     * @param activity 
     *            the Activity 
     * @param slideStyle 
     *            either SLIDING_CONTENT or SLIDING_WINDOW 
     */ 
 public void attachToActivity(Activity activity, int slideStyle) {  
        attachToActivity(activity, slideStyle, false);  
    }  
 
 /** 
     * Attaches the SlidingMenu to an entire Activity 
     *  
     * @param activity 
     *            the Activity 
     * @param slideStyle 
     *            either SLIDING_CONTENT or SLIDING_WINDOW 
     * @param actionbarOverlay 
     *            whether or not the ActionBar is overlaid 
     */ 
 public void attachToActivity(Activity activity, int slideStyle,  
 boolean actionbarOverlay) {  
 if (slideStyle != SLIDING_WINDOW && slideStyle != SLIDING_CONTENT)  
 throw new IllegalArgumentException(  
 "slideStyle must be either SLIDING_WINDOW or SLIDING_CONTENT");  
 
 if (getParent() != null)  
 throw new IllegalStateException(  
 "This SlidingMenu appears to already be attached");  
 
 // get the window background 
        TypedArray a = activity.getTheme().obtainStyledAttributes(  
 new int[] { android.R.attr.windowBackground });  
 int background = a.getResourceId(0, 0);  
        a.recycle();  
 
 switch (slideStyle) {  
 case SLIDING_WINDOW:  
            mActionbarOverlay = false;  
            ViewGroup decor = (ViewGroup) activity.getWindow().getDecorView();  
            ViewGroup decorChild = (ViewGroup) decor.getChildAt(0);  
 // save ActionBar themes that have transparent assets 
            decorChild.setBackgroundResource(background);  
            decor.removeView(decorChild);  
            decor.addView(this);  
            setContent(decorChild);  
 break;  
 case SLIDING_CONTENT:  
            mActionbarOverlay = actionbarOverlay;  
 // take the above view out of 
            ViewGroup contentParent = (ViewGroup) activity  
                    .findViewById(android.R.id.content);  
            View content = contentParent.getChildAt(0);  
            contentParent.removeView(content);  
            contentParent.addView(this);  
            setContent(content);  
 // save people from having transparent backgrounds 
 if (content.getBackground() == null)  
                content.setBackgroundResource(background);  
 break;  
        }  
    }  
 
 /** 
     * Set the above view content from a layout resource. The resource will be 
     * inflated, adding all top-level views to the above view. 
     *  
     * @param res 
     *            the new content 
     */ 
 public void setContent(int res) {  
        setContent(LayoutInflater.from(getContext()).inflate(res, null));  
    }  
 
 /** 
     * Set the above view content to the given View. 
     *  
     * @param view 
     *            The desired content to display. 
     */ 
 public void setContent(View view) {  
        mViewAbove.setContent(view);  
        showContent();  
    }  
 
 /** 
     * 设置背景图片 
     *  
     * @param resid 
     */ 
 public void setBackgroundImage(int resid) {  
        mViewBackground.setBackgroundResource(resid);  
    }  
 
 /** 
     * Retrieves the current content. 
     *  
     * @return the current content 
     */ 
 public View getContent() {  
 return mViewAbove.getContent();  
    }  
 
 /** 
     * Set the behind view (menu) content from a layout resource. The resource 
     * will be inflated, adding all top-level views to the behind view. 
     *  
     * @param res 
     *            the new content 
     */ 
 public void setMenu(int res) {  
        setMenu(LayoutInflater.from(getContext()).inflate(res, null));  
    }  
 
 /** 
     * Set the behind view (menu) content to the given View. 
     *  
     * @param view 
     *            The desired content to display. 
     */ 
 public void setMenu(View v) {  
        mViewBehind.setContent(v);  
    }  
 
 /** 
     * Retrieves the main menu. 
     *  
     * @return the main menu 
     */ 
 public View getMenu() {  
 return mViewBehind.getContent();  
    }  
 
 /** 
     * Set the secondary behind view (right menu) content from a layout 
     * resource. The resource will be inflated, adding all top-level views to 
     * the behind view. 
     *  
     * @param res 
     *            the new content 
     */ 
 public void setSecondaryMenu(int res) {  
        setSecondaryMenu(LayoutInflater.from(getContext()).inflate(res, null));  
    }  
 
 /** 
     * Set the secondary behind view (right menu) content to the given View. 
     *  
     * @param view 
     *            The desired content to display. 
     */ 
 public void setSecondaryMenu(View v) {  
        mViewBehind.setSecondaryContent(v);  
 // mViewBehind.invalidate(); 
    }  
 
 /** 
     * Retrieves the current secondary menu (right). 
     *  
     * @return the current menu 
     */ 
 public View getSecondaryMenu() {  
 return mViewBehind.getSecondaryContent();  
    }  
 
 /** 
     * Sets the sliding enabled. 
     *  
     * @param b 
     *            true to enable sliding, false to disable it. 
     */ 
 public void setSlidingEnabled(boolean b) {  
        mViewAbove.setSlidingEnabled(b);  
    }  
 
 /** 
     * Checks if is sliding enabled. 
     *  
     * @return true, if is sliding enabled 
     */ 
 public boolean isSlidingEnabled() {  
 return mViewAbove.isSlidingEnabled();  
    }  
 
 /** 
     * Sets which side the SlidingMenu should appear on. 
     *  
     * @param mode 
     *            must be either SlidingMenu.LEFT or SlidingMenu.RIGHT 
     */ 
 public void setMode(int mode) {  
 if (mode != LEFT && mode != RIGHT && mode != LEFT_RIGHT) {  
 throw new IllegalStateException(  
 "SlidingMenu mode must be LEFT, RIGHT, or LEFT_RIGHT");  
        }  
        mViewBehind.setMode(mode);  
    }  
 
 /** 
     * Returns the current side that the SlidingMenu is on. 
     *  
     * @return the current mode, either SlidingMenu.LEFT or SlidingMenu.RIGHT 
     */ 
 public int getMode() {  
 return mViewBehind.getMode();  
    }  
 
 /** 
     * Sets whether or not the SlidingMenu is in static mode (i.e. nothing is 
     * moving and everything is showing) 
     *  
     * @param b 
     *            true to set static mode, false to disable static mode. 
     */ 
 public void setStatic(boolean b) {  
 if (b) {  
            setSlidingEnabled(false);  
            mViewAbove.setCustomViewBehind(null);  
            mViewAbove.setCurrentItem(1);  
 // mViewBehind.setCurrentItem(0); 
        } else {  
            mViewAbove.setCurrentItem(1);  
 // mViewBehind.setCurrentItem(1); 
            mViewAbove.setCustomViewBehind(mViewBehind);  
            setSlidingEnabled(true);  
        }  
    }  
 
 /** 
     * Opens the menu and shows the menu view. 
     */ 
 public void showMenu() {  
        showMenu(true);  
    }  
 
 /** 
     * Opens the menu and shows the menu view. 
     *  
     * @param animate 
     *            true to animate the transition, false to ignore animation 
     */ 
 public void showMenu(boolean animate) {  
        mViewAbove.setCurrentItem(0, animate);  
    }  
 
 /** 
     * Opens the menu and shows the secondary menu view. Will default to the 
     * regular menu if there is only one. 
     */ 
 public void showSecondaryMenu() {  
        showSecondaryMenu(true);  
    }  
 
 /** 
     * Opens the menu and shows the secondary (right) menu view. Will default to 
     * the regular menu if there is only one. 
     *  
     * @param animate 
     *            true to animate the transition, false to ignore animation 
     */ 
 public void showSecondaryMenu(boolean animate) {  
        mViewAbove.setCurrentItem(2, animate);  
    }  
 
 /** 
     * Closes the menu and shows the above view. 
     */ 
 public void showContent() {  
        showContent(true);  
    }  
 
 /** 
     * Closes the menu and shows the above view. 
     *  
     * @param animate 
     *            true to animate the transition, false to ignore animation 
     */ 
 public void showContent(boolean animate) {  
        mViewAbove.setCurrentItem(1, animate);  
    }  
 
 /** 
     * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa. 
     */ 
 public void toggle() {  
        toggle(true);  
    }  
 
 /** 
     * Toggle the SlidingMenu. If it is open, it will be closed, and vice versa. 
     *  
     * @param animate 
     *            true to animate the transition, false to ignore animation 
     */ 
 public void toggle(boolean animate) {  
 if (isMenuShowing()) {  
            showContent(animate);  
        } else {  
            showMenu(animate);  
        }  
    }  
 
 /** 
     * Checks if is the behind view showing. 
     *  
     * @return Whether or not the behind view is showing 
     */ 
 public boolean isMenuShowing() {  
 return mViewAbove.getCurrentItem() == 0 
                || mViewAbove.getCurrentItem() == 2;  
    }  
 
 /** 
     * Checks if is the behind view showing. 
     *  
     * @return Whether or not the behind view is showing 
     */ 
 public boolean isSecondaryMenuShowing() {  
 return mViewAbove.getCurrentItem() == 2;  
    }  
 
 /** 
     * Gets the behind offset. 
     *  
     * @return The margin on the right of the screen that the behind view 
     *         scrolls to 
     */ 
 public int getBehindOffset() {  
 return ((RelativeLayout.LayoutParams) mViewBehind.getLayoutParams()).rightMargin;  
    }  
 
 /** 
     * Sets the behind offset. 
     *  
     * @param i 
     *            The margin, in pixels, on the right of the screen that the 
     *            behind view scrolls to. 
     */ 
 public void setBehindOffset(int i) {  
 // RelativeLayout.LayoutParams params = 
 // ((RelativeLayout.LayoutParams)mViewBehind.getLayoutParams()); 
 // int bottom = params.bottomMargin; 
 // int top = params.topMargin; 
 // int left = params.leftMargin; 
 // params.setMargins(left, top, i, bottom); 
        mViewBehind.setWidthOffset(i);  
    }  
 
 /** 
     * Sets the behind offset. 
     *  
     * @param resID 
     *            The dimension resource id to be set as the behind offset. The 
     *            menu, when open, will leave this width margin on the right of 
     *            the screen. 
     */ 
 public void setBehindOffsetRes(int resID) {  
 int i = (int) getContext().getResources().getDimension(resID);  
        setBehindOffset(i);  
    }  
 
 /** 
     * Sets the above offset. 
     *  
     * @param i 
     *            the new above offset, in pixels 
     */ 
 public void setAboveOffset(int i) {  
        mViewAbove.setAboveOffset(i);  
    }  
 
 /** 
     * Sets the above offset. 
     *  
     * @param resID 
     *            The dimension resource id to be set as the above offset. 
     */ 
 public void setAboveOffsetRes(int resID) {  
 int i = (int) getContext().getResources().getDimension(resID);  
        setAboveOffset(i);  
    }  
 
 /** 
     * Sets the behind width. 
     *  
     * @param i 
     *            The width the Sliding Menu will open to, in pixels 
     */ 
 @SuppressWarnings("deprecation")  
 public void setBehindWidth(int i) {  
 int width;  
        Display display = ((WindowManager) getContext().getSystemService(  
                Context.WINDOW_SERVICE)).getDefaultDisplay();  
 try {  
            Class<?> cls = Display.class;  
            Class<?>[] parameterTypes = { Point.class };  
            Point parameter = new Point();  
            Method method = cls.getMethod("getSize", parameterTypes);  
            method.invoke(display, parameter);  
            width = parameter.x;  
        } catch (Exception e) {  
            width = display.getWidth();  
        }  
        setBehindOffset(width - i);  
    }  
 
 /** 
     * Sets the behind width. 
     *  
     * @param res 
     *            The dimension resource id to be set as the behind width 
     *            offset. The menu, when open, will open this wide. 
     */ 
 public void setBehindWidthRes(int res) {  
 int i = (int) getContext().getResources().getDimension(res);  
        setBehindWidth(i);  
    }  
 
 /** 
     * Gets the behind scroll scale. 
     *  
     * @return The scale of the parallax scroll 
     */ 
 public float getBehindScrollScale() {  
 return mViewBehind.getScrollScale();  
    }  
 
 /** 
     * Gets the touch mode margin threshold 
     *  
     * @return the touch mode margin threshold 
     */ 
 public int getTouchmodeMarginThreshold() {  
 return mViewBehind.getMarginThreshold();  
    }  
 
 /** 
     * Set the touch mode margin threshold 
     *  
     * @param touchmodeMarginThreshold 
     */ 
 public void setTouchmodeMarginThreshold(int touchmodeMarginThreshold) {  
        mViewBehind.setMarginThreshold(touchmodeMarginThreshold);  
    }  
 
 /** 
     * Sets the behind scroll scale. 
     *  
     * @param f 
     *            The scale of the parallax scroll (i.e. 1.0f scrolls 1 pixel 
     *            for every 1 pixel that the above view scrolls and 0.0f scrolls 
     *            0 pixels) 
     */ 
 public void setBehindScrollScale(float f) {  
 if (f < 0 && f > 1)  
 throw new IllegalStateException(  
 "ScrollScale must be between 0 and 1");  
        mViewBehind.setScrollScale(f);  
    }  
 
 /** 
     * Sets the behind canvas transformer. 
     *  
     * @param t 
     *            the new behind canvas transformer 
     */ 
 public void setBehindCanvasTransformer(CanvasTransformer t) {  
        mViewBehind.setCanvasTransformer(t);  
    }  
 
 /** 
     * 设置右侧视图的转场动画 
     *  
     * @param t 
     *            the new above canvas transformer 
     */ 
 public void setAboveCanvasTransformer(CanvasTransformer t) {  
        mViewAbove.setCanvasTransformer(t);  
    }  
 
 /** 
     * Gets the touch mode above. 
     *  
     * @return the touch mode above 
     */ 
 public int getTouchModeAbove() {  
 return mViewAbove.getTouchMode();  
    }  
 
 /** 
     * Controls whether the SlidingMenu can be opened with a swipe gesture. 
     * Options are {@link #TOUCHMODE_MARGIN TOUCHMODE_MARGIN}, 
     * {@link #TOUCHMODE_FULLSCREEN TOUCHMODE_FULLSCREEN}, or 
     * {@link #TOUCHMODE_NONE TOUCHMODE_NONE} 
     *  
     * @param i 
     *            the new touch mode 
     */ 
 public void setTouchModeAbove(int i) {  
 if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN  
                && i != TOUCHMODE_NONE) {  
 throw new IllegalStateException(  
 "TouchMode must be set to either" 
                            + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE.");  
        }  
        mViewAbove.setTouchMode(i);  
    }  
 
 /** 
     * Controls whether the SlidingMenu can be opened with a swipe gesture. 
     * Options are {@link #TOUCHMODE_MARGIN TOUCHMODE_MARGIN}, 
     * {@link #TOUCHMODE_FULLSCREEN TOUCHMODE_FULLSCREEN}, or 
     * {@link #TOUCHMODE_NONE TOUCHMODE_NONE} 
     *  
     * @param i 
     *            the new touch mode 
     */ 
 public void setTouchModeBehind(int i) {  
 if (i != TOUCHMODE_FULLSCREEN && i != TOUCHMODE_MARGIN  
                && i != TOUCHMODE_NONE) {  
 throw new IllegalStateException(  
 "TouchMode must be set to either" 
                            + "TOUCHMODE_FULLSCREEN or TOUCHMODE_MARGIN or TOUCHMODE_NONE.");  
        }  
        mViewBehind.setTouchMode(i);  
    }  
 
 /** 
     * Sets the shadow drawable. 
     *  
     * @param resId 
     *            the resource ID of the new shadow drawable 
     */ 
 public void setShadowDrawable(int resId) {  
        setShadowDrawable(getContext().getResources().getDrawable(resId));  
    }  
 
 /** 
     * Sets the shadow drawable. 
     *  
     * @param d 
     *            the new shadow drawable 
     */ 
 public void setShadowDrawable(Drawable d) {  
        mViewBehind.setShadowDrawable(d);  
    }  
 
 /** 
     * Sets the secondary (right) shadow drawable. 
     *  
     * @param resId 
     *            the resource ID of the new shadow drawable 
     */ 
 public void setSecondaryShadowDrawable(int resId) {  
        setSecondaryShadowDrawable(getContext().getResources().getDrawable(  
                resId));  
    }  
 
 /** 
     * Sets the secondary (right) shadow drawable. 
     *  
     * @param d 
     *            the new shadow drawable 
     */ 
 public void setSecondaryShadowDrawable(Drawable d) {  
        mViewBehind.setSecondaryShadowDrawable(d);  
    }  
 
 /** 
     * Sets the shadow width. 
     *  
     * @param resId 
     *            The dimension resource id to be set as the shadow width. 
     */ 
 public void setShadowWidthRes(int resId) {  
        setShadowWidth((int) getResources().getDimension(resId));  
    }  
 
 /** 
     * Sets the shadow width. 
     *  
     * @param pixels 
     *            the new shadow width, in pixels 
     */ 
 public void setShadowWidth(int pixels) {  
        mViewBehind.setShadowWidth(pixels);  
    }  
 
 /** 
     * Enables or disables the SlidingMenu's fade in and out 
     *  
     * @param b 
     *            true to enable fade, false to disable it 
     */ 
 public void setFadeEnabled(boolean b) {  
        mViewBehind.setFadeEnabled(b);  
    }  
 
 /** 
     * Sets how much the SlidingMenu fades in and out. Fade must be enabled, see 
     * {@link #setFadeEnabled(boolean) setFadeEnabled(boolean)} 
     *  
     * @param f 
     *            the new fade degree, between 0.0f and 1.0f 
     */ 
 public void setFadeDegree(float f) {  
        mViewBehind.setFadeDegree(f);  
    }  
 
 /** 
     * Enables or disables whether the selector is drawn 
     *  
     * @param b 
     *            true to draw the selector, false to not draw the selector 
     */ 
 public void setSelectorEnabled(boolean b) {  
        mViewBehind.setSelectorEnabled(true);  
    }  
 
 /** 
     * Sets the selected view. The selector will be drawn here 
     *  
     * @param v 
     *            the new selected view 
     */ 
 public void setSelectedView(View v) {  
        mViewBehind.setSelectedView(v);  
    }  
 
 /** 
     * Sets the selector drawable. 
     *  
     * @param res 
     *            a resource ID for the selector drawable 
     */ 
 public void setSelectorDrawable(int res) {  
        mViewBehind.setSelectorBitmap(BitmapFactory.decodeResource(  
                getResources(), res));  
    }  
 
 /** 
     * Sets the selector drawable. 
     *  
     * @param b 
     *            the new selector bitmap 
     */ 
 public void setSelectorBitmap(Bitmap b) {  
        mViewBehind.setSelectorBitmap(b);  
    }  
 
 /** 
     * Add a View ignored by the Touch Down event when mode is Fullscreen 
     *  
     * @param v 
     *            a view to be ignored 
     */ 
 public void addIgnoredView(View v) {  
        mViewAbove.addIgnoredView(v);  
    }  
 
 /** 
     * Remove a View ignored by the Touch Down event when mode is Fullscreen 
     *  
     * @param v 
     *            a view not wanted to be ignored anymore 
     */ 
 public void removeIgnoredView(View v) {  
        mViewAbove.removeIgnoredView(v);  
    }  
 
 /** 
     * Clear the list of Views ignored by the Touch Down event when mode is 
     * Fullscreen 
     */ 
 public void clearIgnoredViews() {  
        mViewAbove.clearIgnoredViews();  
    }  
 
 /** 
     * Sets the OnOpenListener. {@link OnOpenListener#onOpen() 
     * OnOpenListener.onOpen()} will be called when the SlidingMenu is opened 
     *  
     * @param listener 
     *            the new OnOpenListener 
     */ 
 public void setOnOpenListener(OnOpenListener listener) {  
 // mViewAbove.setOnOpenListener(listener); 
        mOpenListener = listener;  
    }  
 
 /** 
     * Sets the OnOpenListner for secondary menu {@link OnOpenListener#onOpen() 
     * OnOpenListener.onOpen()} will be called when the secondary SlidingMenu is 
     * opened 
     *  
     * @param listener 
     *            the new OnOpenListener 
     */ 
 
 public void setSecondaryOnOpenListner(OnOpenListener listener) {  
        mSecondaryOpenListner = listener;  
    }  
 
 /** 
     * Sets the OnCloseListener. {@link OnCloseListener#onClose() 
     * OnCloseListener.onClose()} will be called when any one of the SlidingMenu 
     * is closed 
     *  
     * @param listener 
     *            the new setOnCloseListener 
     */ 
 public void setOnCloseListener(OnCloseListener listener) {  
 // mViewAbove.setOnCloseListener(listener); 
        mCloseListener = listener;  
    }  
 
 /** 
     * Sets the OnOpenedListener. {@link OnOpenedListener#onOpened() 
     * OnOpenedListener.onOpened()} will be called after the SlidingMenu is 
     * opened 
     *  
     * @param listener 
     *            the new OnOpenedListener 
     */ 
 public void setOnOpenedListener(OnOpenedListener listener) {  
        mViewAbove.setOnOpenedListener(listener);  
    }  
 
 /** 
     * Sets the OnClosedListener. {@link OnClosedListener#onClosed() 
     * OnClosedListener.onClosed()} will be called after the SlidingMenu is 
     * closed 
     *  
     * @param listener 
     *            the new OnClosedListener 
     */ 
 public void setOnClosedListener(OnClosedListener listener) {  
        mViewAbove.setOnClosedListener(listener);  
    }  
 
 public static class SavedState extends BaseSavedState {  
 
 private final int mItem;  
 
 public SavedState(Parcelable superState, int item) {  
 super(superState);  
            mItem = item;  
        }  
 
 private SavedState(Parcel in) {  
 super(in);  
            mItem = in.readInt();  
        }  
 
 public int getItem() {  
 return mItem;  
        }  
 
 /* 
         * (non-Javadoc) 
         *  
         * @see android.view.AbsSavedState#writeToParcel(android.os.Parcel, int) 
         */ 
 public void writeToParcel(Parcel out, int flags) {  
 super.writeToParcel(out, flags);  
            out.writeInt(mItem);  
        }  
 
 public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {  
 public SavedState createFromParcel(Parcel in) {  
 return new SavedState(in);  
            }  
 
 public SavedState[] newArray(int size) {  
 return new SavedState[size];  
            }  
        };  
 
    }  
 
 /* 
     * (non-Javadoc) 
     *  
     * @see android.view.View#onSaveInstanceState() 
     */ 
 @Override 
 protected Parcelable onSaveInstanceState() {  
        Parcelable superState = super.onSaveInstanceState();  
        SavedState ss = new SavedState(superState, mViewAbove.getCurrentItem());  
 return ss;  
    }  
 
 /* 
     * (non-Javadoc) 
     *  
     * @see android.view.View#onRestoreInstanceState(android.os.Parcelable) 
     */ 
 @Override 
 protected void onRestoreInstanceState(Parcelable state) {  
        SavedState ss = (SavedState) state;  
 super.onRestoreInstanceState(ss.getSuperState());  
        mViewAbove.setCurrentItem(ss.getItem());  
    }  
 
 /* 
     * (non-Javadoc) 
     *  
     * @see android.view.ViewGroup#fitSystemWindows(android.graphics.Rect) 
     */ 
 @SuppressLint("NewApi")  
 @Override 
 protected boolean fitSystemWindows(Rect insets) {  
 int leftPadding = insets.left;  
 int rightPadding = insets.right;  
 int topPadding = insets.top;  
 int bottomPadding = insets.bottom;  
 if (!mActionbarOverlay) {  
            Log.v(TAG, "setting padding!");  
            setPadding(leftPadding, topPadding, rightPadding, bottomPadding);  
        }  
 return true;  
    }  
 
 @TargetApi(Build.VERSION_CODES.HONEYCOMB)  
 public void manageLayers(float percentOpen) {  
 if (Build.VERSION.SDK_INT < 11)  
 return;  
 
 boolean layer = percentOpen > 0.0f && percentOpen < 1.0f;  
 final int layerType = layer ? View.LAYER_TYPE_HARDWARE  
                : View.LAYER_TYPE_NONE;  
 
 if (layerType != getContent().getLayerType()) {  
            getHandler().post(new Runnable() {  
 public void run() {  
                    Log.v(TAG, "changing layerType. hardware? " 
                            + (layerType == View.LAYER_TYPE_HARDWARE));  
                    getContent().setLayerType(layerType, null);  
                    getMenu().setLayerType(layerType, null);  
 if (getSecondaryMenu() != null) {  
                        getSecondaryMenu().setLayerType(layerType, null);  
                    }  
                }  
            });  
        }  
    }  
 
}  
代码语言:java
复制
public class CustomViewAbove extends ViewGroup {  
 
 private static final String TAG = "CustomViewAbove";  
 private static final boolean DEBUG = false;  
 
 private static final boolean USE_CACHE = false;  
 
 private static final int MAX_SETTLE_DURATION = 600; // ms 
 private static final int MIN_DISTANCE_FOR_FLING = 25; // dips 
 
 private static final Interpolator sInterpolator = new Interpolator() {  
 public float getInterpolation(float t) {  
            t -= 1.0f;  
 return t * t * t * t * t + 1.0f;  
        }  
    };  
 
 private View mContent;  
 
 private int mCurItem;  
 private Scroller mScroller;  
 
 private boolean mScrollingCacheEnabled;  
 
 private boolean mScrolling;  
 
 private boolean mIsBeingDragged;  
 private boolean mIsUnableToDrag;  
 private int mTouchSlop;  
 private float mInitialMotionX;  
 /** 
     * Position of the last motion event. 
     */ 
 private float mLastMotionX;  
 private float mLastMotionY;  
 /** 
     * ID of the active pointer. This is used to retain consistency during 
     * drags/flings if multiple pointers are used. 
     */ 
 protected int mActivePointerId = INVALID_POINTER;  
 /** 
     * Sentinel value for no current active pointer. 
     * Used by {@link #mActivePointerId}. 
     */ 
 private static final int INVALID_POINTER = -1;  
 /** 保存转场动画的变量*/ 
 private CanvasTransformer mTransformer;  
 
 /** 
     * Determines speed during touch scrolling 
     */ 
 protected VelocityTracker mVelocityTracker;  
 private int mMinimumVelocity;  
 protected int mMaximumVelocity;  
 private int mFlingDistance;  
 
 private CustomViewBehind mViewBehind;  
 //  private int mMode; 
 private boolean mEnabled = true;  
 
 private OnPageChangeListener mOnPageChangeListener;  
 private OnPageChangeListener mInternalPageChangeListener;  
 
 //  private OnCloseListener mCloseListener; 
 //  private OnOpenListener mOpenListener; 
 private OnClosedListener mClosedListener;  
 private OnOpenedListener mOpenedListener;  
 
 private List<View> mIgnoredViews = new ArrayList<View>();  
 
 //  private int mScrollState = SCROLL_STATE_IDLE; 
 
 /** 
     * Callback interface for responding to changing state of the selected page. 
     */ 
 public interface OnPageChangeListener {  
 
 /** 
         * This method will be invoked when the current page is scrolled, either as part 
         * of a programmatically initiated smooth scroll or a user initiated touch scroll. 
         * 
         * @param position Position index of the first page currently being displayed. 
         *                 Page position+1 will be visible if positionOffset is nonzero. 
         * @param positionOffset Value from [0, 1) indicating the offset from the page at position. 
         * @param positionOffsetPixels Value in pixels indicating the offset from position. 
         */ 
 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);  
 
 /** 
         * This method will be invoked when a new page becomes selected. Animation is not 
         * necessarily complete. 
         * 
         * @param position Position index of the new selected page. 
         */ 
 public void onPageSelected(int position);  
 
    }  
 
 /** 
     * Simple implementation of the {@link OnPageChangeListener} interface with stub 
     * implementations of each method. Extend this if you do not intend to override 
     * every method of {@link OnPageChangeListener}. 
     */ 
 public static class SimpleOnPageChangeListener implements OnPageChangeListener {  
 
 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {  
 // This space for rent 
        }  
 
 public void onPageSelected(int position) {  
 // This space for rent 
        }  
 
 public void onPageScrollStateChanged(int state) {  
 // This space for rent 
        }  
 
    }  
 
 public CustomViewAbove(Context context) {  
 this(context, null);  
    }  
 
 public CustomViewAbove(Context context, AttributeSet attrs) {  
 super(context, attrs);  
        initCustomViewAbove();  
    }  
 
 void initCustomViewAbove() {  
        setWillNotDraw(false);  
        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);  
        setFocusable(true);  
 final Context context = getContext();  
        mScroller = new Scroller(context, sInterpolator);  
 final ViewConfiguration configuration = ViewConfiguration.get(context);  
        mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);  
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();  
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();  
        setInternalPageChangeListener(new SimpleOnPageChangeListener() {  
 public void onPageSelected(int position) {  
 if (mViewBehind != null) {  
 switch (position) {  
 case 0:  
 case 2:  
                        mViewBehind.setChildrenEnabled(true);  
 break;  
 case 1:  
                        mViewBehind.setChildrenEnabled(false);  
 break;  
                    }  
                }  
            }  
        });  
 
 final float density = context.getResources().getDisplayMetrics().density;  
        mFlingDistance = (int) (MIN_DISTANCE_FOR_FLING * density);  
    }  
 
 /** 
     * Set the currently selected page. If the CustomViewPager has already been through its first 
     * layout there will be a smooth animated transition between the current item and the 
     * specified item. 
     * 
     * @param item Item index to select 
     */ 
 public void setCurrentItem(int item) {  
        setCurrentItemInternal(item, true, false);  
    }  
 
 /** 
     * Set the currently selected page. 
     * 
     * @param item Item index to select 
     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately 
     */ 
 public void setCurrentItem(int item, boolean smoothScroll) {  
        setCurrentItemInternal(item, smoothScroll, false);  
    }  
 
 public int getCurrentItem() {  
 return mCurItem;  
    }  
 
 void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {  
        setCurrentItemInternal(item, smoothScroll, always, 0);  
    }  
 
 void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {  
 if (!always && mCurItem == item) {  
            setScrollingCacheEnabled(false);  
 return;  
        }  
 
        item = mViewBehind.getMenuPage(item);  
 
 final boolean dispatchSelected = mCurItem != item;  
        mCurItem = item;  
 final int destX = getDestScrollX(mCurItem);  
 if (dispatchSelected && mOnPageChangeListener != null) {  
            mOnPageChangeListener.onPageSelected(item);  
        }  
 if (dispatchSelected && mInternalPageChangeListener != null) {  
            mInternalPageChangeListener.onPageSelected(item);  
        }  
 if (smoothScroll) {  
            smoothScrollTo(destX, 0, velocity);  
        } else {  
            completeScroll();  
            scrollTo(destX, 0);  
        }  
    }  
 
 /** 
     * Set a listener that will be invoked whenever the page changes or is incrementally 
     * scrolled. See {@link OnPageChangeListener}. 
     * 
     * @param listener Listener to set 
     */ 
 public void setOnPageChangeListener(OnPageChangeListener listener) {  
        mOnPageChangeListener = listener;  
    }  
 /* 
    public void setOnOpenListener(OnOpenListener l) { 
        mOpenListener = l; 
    } 
 
    public void setOnCloseListener(OnCloseListener l) { 
        mCloseListener = l; 
    } 
     */ 
 public void setOnOpenedListener(OnOpenedListener l) {  
        mOpenedListener = l;  
    }  
 
 public void setOnClosedListener(OnClosedListener l) {  
        mClosedListener = l;  
    }  
 
 /** 
     * Set a separate OnPageChangeListener for internal use by the support library. 
     * 
     * @param listener Listener to set 
     * @return The old listener that was set, if any. 
     */ 
    OnPageChangeListener setInternalPageChangeListener(OnPageChangeListener listener) {  
        OnPageChangeListener oldListener = mInternalPageChangeListener;  
        mInternalPageChangeListener = listener;  
 return oldListener;  
    }  
 
 public void addIgnoredView(View v) {  
 if (!mIgnoredViews.contains(v)) {  
            mIgnoredViews.add(v);  
        }  
    }  
 
 public void removeIgnoredView(View v) {  
        mIgnoredViews.remove(v);  
    }  
 
 public void clearIgnoredViews() {  
        mIgnoredViews.clear();  
    }  
 
 // We want the duration of the page snap animation to be influenced by the distance that 
 // the screen has to travel, however, we don't want this duration to be effected in a 
 // purely linear fashion. Instead, we use this method to moderate the effect that the distance 
 // of travel has on the overall snap duration. 
 float distanceInfluenceForSnapDuration(float f) {  
        f -= 0.5f; // center the values about 0. 
        f *= 0.3f * Math.PI / 2.0f;  
 return (float) FloatMath.sin(f);  
    }  
 
 public int getDestScrollX(int page) {  
 switch (page) {  
 case 0:  
 case 2:  
 return mViewBehind.getMenuLeft(mContent, page);  
 case 1:  
 return mContent.getLeft();  
        }  
 return 0;  
    }  
 
 private int getLeftBound() {  
 return mViewBehind.getAbsLeftBound(mContent);  
    }  
 
 private int getRightBound() {  
 return mViewBehind.getAbsRightBound(mContent);  
    }  
 
 public int getContentLeft() {  
 return mContent.getLeft() + mContent.getPaddingLeft();  
    }  
 
 public boolean isMenuOpen() {  
 return mCurItem == 0 || mCurItem == 2;  
    }  
 
 private boolean isInIgnoredView(MotionEvent ev) {  
        Rect rect = new Rect();  
 for (View v : mIgnoredViews) {  
            v.getHitRect(rect);  
 if (rect.contains((int)ev.getX(), (int)ev.getY())) return true;  
        }  
 return false;  
    }  
 
 public int getBehindWidth() {  
 if (mViewBehind == null) {  
 return 0;  
        } else {  
 return mViewBehind.getBehindWidth();  
        }  
    }  
 
 public int getChildWidth(int i) {  
 switch (i) {  
 case 0:  
 return getBehindWidth();  
 case 1:  
 return mContent.getWidth();  
 default:  
 return 0;  
        }  
    }  
 
 public boolean isSlidingEnabled() {  
 return mEnabled;  
    }  
 
 public void setSlidingEnabled(boolean b) {  
        mEnabled = b;  
    }  
 
 /** 
     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 
     * 
     * @param x the number of pixels to scroll by on the X axis 
     * @param y the number of pixels to scroll by on the Y axis 
     */ 
 void smoothScrollTo(int x, int y) {  
        smoothScrollTo(x, y, 0);  
    }  
 
 /** 
     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately. 
     * 
     * @param x the number of pixels to scroll by on the X axis 
     * @param y the number of pixels to scroll by on the Y axis 
     * @param velocity the velocity associated with a fling, if applicable. (0 otherwise) 
     */ 
 void smoothScrollTo(int x, int y, int velocity) {  
 if (getChildCount() == 0) {  
 // Nothing to do. 
            setScrollingCacheEnabled(false);  
 return;  
        }  
 int sx = getScrollX();  
 int sy = getScrollY();  
 int dx = x - sx;  
 int dy = y - sy;  
 if (dx == 0 && dy == 0) {  
            completeScroll();  
 if (isMenuOpen()) {  
 if (mOpenedListener != null)  
                    mOpenedListener.onOpened();  
            } else {  
 if (mClosedListener != null)  
                    mClosedListener.onClosed();  
            }  
 return;  
        }  
 
        setScrollingCacheEnabled(true);  
        mScrolling = true;  
 
 final int width = getBehindWidth();  
 final int halfWidth = width / 2;  
 final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dx) / width);  
 final float distance = halfWidth + halfWidth *  
                distanceInfluenceForSnapDuration(distanceRatio);  
 
 int duration = 0;  
        velocity = Math.abs(velocity);  
 if (velocity > 0) {  
            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));  
        } else {  
 final float pageDelta = (float) Math.abs(dx) / width;  
            duration = (int) ((pageDelta + 1) * 100);  
            duration = MAX_SETTLE_DURATION;  
        }  
        duration = Math.min(duration, MAX_SETTLE_DURATION);  
 
        mScroller.startScroll(sx, sy, dx, dy, duration);  
        invalidate();  
    }  
 
 public void setContent(View v) {  
 if (mContent != null)   
 this.removeView(mContent);  
        mContent = v;  
        addView(mContent);  
    }  
 
 public View getContent() {  
 return mContent;  
    }  
 
 public void setCustomViewBehind(CustomViewBehind cvb) {  
        mViewBehind = cvb;  
    }  
 
 @Override 
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
 
 int width = getDefaultSize(0, widthMeasureSpec);  
 int height = getDefaultSize(0, heightMeasureSpec);  
        setMeasuredDimension(width, height);  
 
 final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);  
 final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);  
        mContent.measure(contentWidth, contentHeight);  
    }  
 
 @Override 
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
 super.onSizeChanged(w, h, oldw, oldh);  
 // Make sure scroll position is set correctly. 
 if (w != oldw) {  
 // [ChrisJ] - This fixes the onConfiguration change for orientation issue.. 
 // maybe worth having a look why the recomputeScroll pos is screwing 
 // up? 
            completeScroll();  
            scrollTo(getDestScrollX(mCurItem), getScrollY());  
        }  
    }  
 
 @Override 
 protected void onLayout(boolean changed, int l, int t, int r, int b) {  
 final int width = r - l;  
 final int height = b - t;  
        mContent.layout(0, 0, width, height);  
    }  
 
 public void setAboveOffset(int i) {  
 //      RelativeLayout.LayoutParams params = ((RelativeLayout.LayoutParams)mContent.getLayoutParams()); 
 //      params.setMargins(i, params.topMargin, params.rightMargin, params.bottomMargin); 
        mContent.setPadding(i, mContent.getPaddingTop(),   
                mContent.getPaddingRight(), mContent.getPaddingBottom());  
    }  
 
 
 @Override 
 public void computeScroll() {  
 if (!mScroller.isFinished()) {  
 if (mScroller.computeScrollOffset()) {  
 int oldX = getScrollX();  
 int oldY = getScrollY();  
 int x = mScroller.getCurrX();  
 int y = mScroller.getCurrY();  
 
 if (oldX != x || oldY != y) {  
                    scrollTo(x, y);  
                    pageScrolled(x);  
                }  
 
 // Keep on drawing until the animation has finished. 
                invalidate();  
 return;  
            }  
        }  
 
 // Done with scroll, clean up state. 
        completeScroll();  
    }  
 
 private void pageScrolled(int xpos) {  
 final int widthWithMargin = getWidth();  
 final int position = xpos / widthWithMargin;  
 final int offsetPixels = xpos % widthWithMargin;  
 final float offset = (float) offsetPixels / widthWithMargin;  
 
        onPageScrolled(position, offset, offsetPixels);  
    }  
 
 /** 
     * This method will be invoked when the current page is scrolled, either as part 
     * of a programmatically initiated smooth scroll or a user initiated touch scroll. 
     * If you override this method you must call through to the superclass implementation 
     * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled 
     * returns. 
     * 
     * @param position Position index of the first page currently being displayed. 
     *                 Page position+1 will be visible if positionOffset is nonzero. 
     * @param offset Value from [0, 1) indicating the offset from the page at position. 
     * @param offsetPixels Value in pixels indicating the offset from position. 
     */ 
 protected void onPageScrolled(int position, float offset, int offsetPixels) {  
 if (mOnPageChangeListener != null) {  
            mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);  
        }  
 if (mInternalPageChangeListener != null) {  
            mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);  
        }  
    }  
 
 private void completeScroll() {  
 boolean needPopulate = mScrolling;  
 if (needPopulate) {  
 // Done with scroll, no longer want to cache view drawing. 
            setScrollingCacheEnabled(false);  
            mScroller.abortAnimation();  
 int oldX = getScrollX();  
 int oldY = getScrollY();  
 int x = mScroller.getCurrX();  
 int y = mScroller.getCurrY();  
 if (oldX != x || oldY != y) {  
                scrollTo(x, y);  
            }  
 if (isMenuOpen()) {  
 if (mOpenedListener != null)  
                    mOpenedListener.onOpened();  
            } else {  
 if (mClosedListener != null)  
                    mClosedListener.onClosed();  
            }  
        }  
        mScrolling = false;  
    }  
 
 protected int mTouchMode = SlidingMenu.TOUCHMODE_MARGIN;  
 
 public void setTouchMode(int i) {  
        mTouchMode = i;  
    }  
 
 public int getTouchMode() {  
 return mTouchMode;  
    }  
 
 private boolean thisTouchAllowed(MotionEvent ev) {  
 int x = (int) (ev.getX() + mScrollX);  
 if (isMenuOpen()) {  
 return mViewBehind.menuOpenTouchAllowed(mContent, mCurItem, x);  
        } else {  
 switch (mTouchMode) {  
 case SlidingMenu.TOUCHMODE_FULLSCREEN:  
 return !isInIgnoredView(ev);  
 case SlidingMenu.TOUCHMODE_NONE:  
 return false;  
 case SlidingMenu.TOUCHMODE_MARGIN:  
 return mViewBehind.marginTouchAllowed(mContent, x);  
            }  
        }  
 return false;  
    }  
 
 private boolean thisSlideAllowed(float dx) {  
 boolean allowed = false;  
 if (isMenuOpen()) {  
            allowed = mViewBehind.menuOpenSlideAllowed(dx);  
        } else {  
            allowed = mViewBehind.menuClosedSlideAllowed(dx);  
        }  
 if (DEBUG)  
            Log.v(TAG, "this slide allowed " + allowed + " dx: " + dx);  
 return allowed;  
    }  
 
 private int getPointerIndex(MotionEvent ev, int id) {  
 int activePointerIndex = MotionEventCompat.findPointerIndex(ev, id);  
 if (activePointerIndex == -1)  
            mActivePointerId = INVALID_POINTER;  
 return activePointerIndex;  
    }  
 
 private boolean mQuickReturn = false;  
 
 @Override 
 public boolean onInterceptTouchEvent(MotionEvent ev) {  
 
 if (!mEnabled)  
 return false;  
 
 final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;  
 
 if (DEBUG)  
 if (action == MotionEvent.ACTION_DOWN)  
                Log.v(TAG, "Received ACTION_DOWN");  
 
 if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP  
                || (action != MotionEvent.ACTION_DOWN && mIsUnableToDrag)) {  
            endDrag();  
 return false;  
        }  
 
 switch (action) {  
 case MotionEvent.ACTION_MOVE:  
            determineDrag(ev);  
 break;  
 case MotionEvent.ACTION_DOWN:  
 int index = MotionEventCompat.getActionIndex(ev);  
            mActivePointerId = MotionEventCompat.getPointerId(ev, index);  
 if (mActivePointerId == INVALID_POINTER)  
 break;  
            mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);  
            mLastMotionY = MotionEventCompat.getY(ev, index);  
 if (thisTouchAllowed(ev)) {  
                mIsBeingDragged = false;  
                mIsUnableToDrag = false;  
 if (isMenuOpen() && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {  
                    mQuickReturn = true;  
                }  
            } else {  
                mIsUnableToDrag = true;  
            }  
 break;  
 case MotionEventCompat.ACTION_POINTER_UP:  
            onSecondaryPointerUp(ev);  
 break;  
        }  
 
 if (!mIsBeingDragged) {  
 if (mVelocityTracker == null) {  
                mVelocityTracker = VelocityTracker.obtain();  
            }  
            mVelocityTracker.addMovement(ev);  
        }  
 return mIsBeingDragged || mQuickReturn;  
    }  
 
 
 @Override 
 public boolean onTouchEvent(MotionEvent ev) {  
 
 if (!mEnabled)  
 return false;  
 
 if (!mIsBeingDragged && !thisTouchAllowed(ev))  
 return false;  
 
 //      if (!mIsBeingDragged && !mQuickReturn) 
 //          return false; 
 
 final int action = ev.getAction();  
 
 if (mVelocityTracker == null) {  
            mVelocityTracker = VelocityTracker.obtain();  
        }  
        mVelocityTracker.addMovement(ev);  
 
 switch (action & MotionEventCompat.ACTION_MASK) {  
 case MotionEvent.ACTION_DOWN:  
 /* 
             * If being flinged and user touches, stop the fling. isFinished 
             * will be false if being flinged. 
             */ 
            completeScroll();  
 
 // Remember where the motion event started 
 int index = MotionEventCompat.getActionIndex(ev);  
            mActivePointerId = MotionEventCompat.getPointerId(ev, index);  
            mLastMotionX = mInitialMotionX = ev.getX();  
 break;  
 case MotionEvent.ACTION_MOVE:  
 if (!mIsBeingDragged) {   
                determineDrag(ev);  
 if (mIsUnableToDrag)  
 return false;  
            }  
 if (mIsBeingDragged) {  
 // Scroll to follow the motion event 
 final int activePointerIndex = getPointerIndex(ev, mActivePointerId);  
 if (mActivePointerId == INVALID_POINTER)  
 break;  
 final float x = MotionEventCompat.getX(ev, activePointerIndex);  
 final float deltaX = mLastMotionX - x;  
                mLastMotionX = x;  
 float oldScrollX = getScrollX();  
 float scrollX = oldScrollX + deltaX;  
 final float leftBound = getLeftBound();  
 final float rightBound = getRightBound();  
 if (scrollX < leftBound) {  
                    scrollX = leftBound;  
                } else if (scrollX > rightBound) {  
                    scrollX = rightBound;  
                }  
 // Don't lose the rounded component 
                mLastMotionX += scrollX - (int) scrollX;  
                scrollTo((int) scrollX, getScrollY());  
                pageScrolled((int) scrollX);  
            }  
 break;  
 case MotionEvent.ACTION_UP:  
 if (mIsBeingDragged) {  
 final VelocityTracker velocityTracker = mVelocityTracker;  
                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
 int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(  
                        velocityTracker, mActivePointerId);  
 final int scrollX = getScrollX();  
 final float pageOffset = (float) (scrollX - getDestScrollX(mCurItem)) / getBehindWidth();  
 final int activePointerIndex = getPointerIndex(ev, mActivePointerId);  
 if (mActivePointerId != INVALID_POINTER) {  
 final float x = MotionEventCompat.getX(ev, activePointerIndex);  
 final int totalDelta = (int) (x - mInitialMotionX);  
 int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);  
                    setCurrentItemInternal(nextPage, true, true, initialVelocity);  
                } else {      
                    setCurrentItemInternal(mCurItem, true, true, initialVelocity);  
                }  
                mActivePointerId = INVALID_POINTER;  
                endDrag();  
            } else if (mQuickReturn && mViewBehind.menuTouchInQuickReturn(mContent, mCurItem, ev.getX() + mScrollX)) {  
 // close the menu 
                setCurrentItem(1);  
                endDrag();  
            }  
 break;  
 case MotionEvent.ACTION_CANCEL:  
 if (mIsBeingDragged) {  
                setCurrentItemInternal(mCurItem, true, true);  
                mActivePointerId = INVALID_POINTER;  
                endDrag();  
            }  
 break;  
 case MotionEventCompat.ACTION_POINTER_DOWN: {  
 final int indexx = MotionEventCompat.getActionIndex(ev);  
            mLastMotionX = MotionEventCompat.getX(ev, indexx);  
            mActivePointerId = MotionEventCompat.getPointerId(ev, indexx);  
 break;  
        }  
 case MotionEventCompat.ACTION_POINTER_UP:  
            onSecondaryPointerUp(ev);  
 int pointerIndex = getPointerIndex(ev, mActivePointerId);  
 if (mActivePointerId == INVALID_POINTER)  
 break;  
            mLastMotionX = MotionEventCompat.getX(ev, pointerIndex);  
 break;  
        }  
 return true;  
    }  
 
 private void determineDrag(MotionEvent ev) {  
 final int activePointerId = mActivePointerId;  
 final int pointerIndex = getPointerIndex(ev, activePointerId);  
 if (activePointerId == INVALID_POINTER || pointerIndex == INVALID_POINTER)  
 return;  
 final float x = MotionEventCompat.getX(ev, pointerIndex);  
 final float dx = x - mLastMotionX;  
 final float xDiff = Math.abs(dx);  
 final float y = MotionEventCompat.getY(ev, pointerIndex);  
 final float dy = y - mLastMotionY;  
 final float yDiff = Math.abs(dy);  
 if (xDiff > (isMenuOpen()?mTouchSlop/2:mTouchSlop) && xDiff > yDiff && thisSlideAllowed(dx)) {          
            startDrag();  
            mLastMotionX = x;  
            mLastMotionY = y;  
            setScrollingCacheEnabled(true);  
 // TODO add back in touch slop check 
        } else if (xDiff > mTouchSlop) {  
            mIsUnableToDrag = true;  
        }  
    }  
 
 @Override 
 public void scrollTo(int x, int y) {  
 super.scrollTo(x, y);  
        mScrollX = x;  
        mViewBehind.scrollBehindTo(mContent, x, y);   
       ((SlidingMenu)getParent()).manageLayers(getPercentOpen());  
 
 if (mTransformer != null) {  
            invalidate();  
        }  
    }  
 
 private int determineTargetPage(float pageOffset, int velocity, int deltaX) {  
 int targetPage = mCurItem;  
 if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {  
 if (velocity > 0 && deltaX > 0) {  
                targetPage -= 1;  
            } else if (velocity < 0 && deltaX < 0){  
                targetPage += 1;  
            }  
        } else {  
            targetPage = (int) Math.round(mCurItem + pageOffset);  
        }  
 return targetPage;  
    }  
 
 protected float getPercentOpen() {  
 return Math.abs(mScrollX-mContent.getLeft()) / getBehindWidth();  
    }  
 
 @Override 
 protected void dispatchDraw(Canvas canvas) {  
 // 这句要注释掉,否则会出现2个右侧的视图,一个有转场动画,一个没有转场动画 
 // super.dispatchDraw(canvas); 
 // Draw the margin drawable if needed. 
        mViewBehind.drawShadow(mContent, canvas);  
        mViewBehind.drawFade(mContent, canvas, getPercentOpen());  
        mViewBehind.drawSelector(mContent, canvas, getPercentOpen());  
 // 设置右侧视图的转场效果,主要是修改Canvas。 
 if (mTransformer != null) {  
            canvas.save();  
            mTransformer.transformCanvas(canvas, getPercentOpen());  
 super.dispatchDraw(canvas);  
            canvas.restore();  
        } else {  
 super.dispatchDraw(canvas);  
        }  
    }  
 
 // variables for drawing 
 private float mScrollX = 0.0f;  
 
 private void onSecondaryPointerUp(MotionEvent ev) {  
 if (DEBUG) Log.v(TAG, "onSecondaryPointerUp called");  
 final int pointerIndex = MotionEventCompat.getActionIndex(ev);  
 final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);  
 if (pointerId == mActivePointerId) {  
 // This was our active pointer going up. Choose a new 
 // active pointer and adjust accordingly. 
 final int newPointerIndex = pointerIndex == 0 ? 1 : 0;  
            mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);  
            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);  
 if (mVelocityTracker != null) {  
                mVelocityTracker.clear();  
            }  
        }  
    }  
 
 private void startDrag() {  
        mIsBeingDragged = true;  
        mQuickReturn = false;  
    }  
 
 private void endDrag() {  
        mQuickReturn = false;  
        mIsBeingDragged = false;  
        mIsUnableToDrag = false;  
        mActivePointerId = INVALID_POINTER;  
 
 if (mVelocityTracker != null) {  
            mVelocityTracker.recycle();  
            mVelocityTracker = null;  
        }  
    }  
 
 private void setScrollingCacheEnabled(boolean enabled) {  
 if (mScrollingCacheEnabled != enabled) {  
            mScrollingCacheEnabled = enabled;  
 if (USE_CACHE) {  
 final int size = getChildCount();  
 for (int i = 0; i < size; ++i) {  
 final View child = getChildAt(i);  
 if (child.getVisibility() != GONE) {  
                        child.setDrawingCacheEnabled(enabled);  
                    }  
                }  
            }  
        }  
    }  
 
 /** 
     * Tests scrollability within child views of v given a delta of dx. 
     * 
     * @param v View to test for horizontal scrollability 
     * @param checkV Whether the view v passed should itself be checked for scrollability (true), 
     *               or just its children (false). 
     * @param dx Delta scrolled in pixels 
     * @param x X coordinate of the active touch point 
     * @param y Y coordinate of the active touch point 
     * @return true if child views of v can be scrolled by delta of dx. 
     */ 
 protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {  
 if (v instanceof ViewGroup) {  
 final ViewGroup group = (ViewGroup) v;  
 final int scrollX = v.getScrollX();  
 final int scrollY = v.getScrollY();  
 final int count = group.getChildCount();  
 // Count backwards - let topmost views consume scroll distance first. 
 for (int i = count - 1; i >= 0; i--) {  
 final View child = group.getChildAt(i);  
 if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&  
                        y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&  
                        canScroll(child, true, dx, x + scrollX - child.getLeft(),  
                                y + scrollY - child.getTop())) {  
 return true;  
                }  
            }  
        }  
 
 return checkV && ViewCompat.canScrollHorizontally(v, -dx);  
    }  
 
 
 @Override 
 public boolean dispatchKeyEvent(KeyEvent event) {  
 // Let the focused view and/or our descendants get the key first 
 return super.dispatchKeyEvent(event) || executeKeyEvent(event);  
    }  
 
 /** 
     * You can call this function yourself to have the scroll view perform 
     * scrolling from a key event, just as if the event had been dispatched to 
     * it by the view hierarchy. 
     * 
     * @param event The key event to execute. 
     * @return Return true if the event was handled, else false. 
     */ 
 public boolean executeKeyEvent(KeyEvent event) {  
 boolean handled = false;  
 if (event.getAction() == KeyEvent.ACTION_DOWN) {  
 switch (event.getKeyCode()) {  
 case KeyEvent.KEYCODE_DPAD_LEFT:  
                handled = arrowScroll(FOCUS_LEFT);  
 break;  
 case KeyEvent.KEYCODE_DPAD_RIGHT:  
                handled = arrowScroll(FOCUS_RIGHT);  
 break;  
 case KeyEvent.KEYCODE_TAB:  
 if (Build.VERSION.SDK_INT >= 11) {  
 // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD 
 // before Android 3.0. Ignore the tab key on those devices. 
 if (KeyEventCompat.hasNoModifiers(event)) {  
                        handled = arrowScroll(FOCUS_FORWARD);  
                    } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {  
                        handled = arrowScroll(FOCUS_BACKWARD);  
                    }  
                }  
 break;  
            }  
        }  
 return handled;  
    }  
 
 public boolean arrowScroll(int direction) {  
        View currentFocused = findFocus();  
 if (currentFocused == this) currentFocused = null;  
 
 boolean handled = false;  
 
        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,  
                direction);  
 if (nextFocused != null && nextFocused != currentFocused) {  
 if (direction == View.FOCUS_LEFT) {  
                handled = nextFocused.requestFocus();  
            } else if (direction == View.FOCUS_RIGHT) {  
 // If there is nothing to the right, or this is causing us to 
 // jump to the left, then what we really want to do is page right. 
 if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) {  
                    handled = pageRight();  
                } else {  
                    handled = nextFocused.requestFocus();  
                }  
            }  
        } else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {  
 // Trying to move left and nothing there; try to page. 
            handled = pageLeft();  
        } else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {  
 // Trying to move right and nothing there; try to page. 
            handled = pageRight();  
        }  
 if (handled) {  
            playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));  
        }  
 return handled;  
    }  
 
 boolean pageLeft() {  
 if (mCurItem > 0) {  
            setCurrentItem(mCurItem-1, true);  
 return true;  
        }  
 return false;  
    }  
 
 boolean pageRight() {  
 if (mCurItem < 1) {  
            setCurrentItem(mCurItem+1, true);  
 return true;  
        }  
 return false;  
    }  
 
 public void setCanvasTransformer(CanvasTransformer t) {  
        mTransformer = t;  
    }  
 
}  

如果想要使用这个侧滑菜单的动画效果,直接替换这两个类即可。同时,并不会影响SlidingMenu的固有功能。

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

代码语言:java
复制
SlidingMenu sm = getSlidingMenu();  
sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);  
sm.setFadeEnabled(false);  
sm.setBehindScrollScale(0.25f);  
sm.setFadeDegree(0.25f);  
 
// 配置背景图片 
sm.setBackgroundImage(R.drawable.img_frame_background);  
// 设置专场动画效果 
sm.setBehindCanvasTransformer(new SlidingMenu.CanvasTransformer() {  
 @Override 
 public void transformCanvas(Canvas canvas, float percentOpen) {  
 float scale = (float) (percentOpen * 0.25 + 0.75);  
        canvas.scale(scale, scale, -canvas.getWidth() / 2,  
                canvas.getHeight() / 2);  
    }  
});  
 
sm.setAboveCanvasTransformer(new SlidingMenu.CanvasTransformer() {  
 @Override 
 public void transformCanvas(Canvas canvas, float percentOpen) {  
 float scale = (float) (1 - percentOpen * 0.25);  
        canvas.scale(scale, scale, 0, canvas.getHeight() / 2);  
    }  
});  

大功告成!

最后,附上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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2014-10-21 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档