diff --git a/README.md b/README.md index b44c8a3c0..d2081c04d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ SlidingMenu ([Play Store Demo][7]) =========== -SlidingMenu is an Open Source Android library that allows developers to easily create applications -with sliding menus like those made popular in the Google+, YouTube, and Facebook apps. Feel free +SlidingMenu is an Open Source Android library that allows developers to easily create applications +with sliding menus like those made popular in the Google+, YouTube, and Facebook apps. Feel free to use it all you want in your Android apps provided that you cite this project and include the license in your app. -SlidingMenu is currently used in some awesome Android apps. Here's a list of some of them: +SlidingMenu is currently used in some awesome Android apps. Here's a list of some of them: * [Foursquare][15] * [LinkedIn][19] * [Zappos][20] @@ -31,7 +31,7 @@ Also, you can follow the project on Twitter : [@SlidingMenu][1] Setup ----- -* In Eclipse, just import the library as an Android library project. Project > Clean to generate the binaries +* In Eclipse, just import the library as an Android library project. Project > Clean to generate the binaries you need, like R.java, etc. * Then, just add SlidingMenu as a dependency to your existing project and you're good to go! @@ -40,7 +40,7 @@ Setup with ActionBarSherlock * Setup as above. * Checkout a clean copy of [ActionBarSherlock][2] and import into your Eclipse workspace. * Add ActionBarSherlock as a dependency to SlidingMenu -* Go into the SlidingActivities that you plan on using make them extend Sherlock___Activity instead of ___Activity. +* Go into the SlidingActivities that you plan on using make them extend Sherlock___Activity instead of ___Activity. How to Integrate this Library into Your Projects ------------------------------------------------ @@ -52,8 +52,8 @@ and then calling `SlidingMenu.attachToActivity(Activity activity, SlidingMenu.SL does not. You can check it out in the example app AttachExample Activity. __2.__ You can embed the SlidingMenu at the Activity level by making your Activity extend `SlidingActivity`. -* In your Activity's onCreate method, you will have to call `setContentView`, as usual, and also -`setBehindContentView`, which has the same syntax as setContentView. `setBehindContentView` will place +* In your Activity's onCreate method, you will have to call `setContentView`, as usual, and also +`setBehindContentView`, which has the same syntax as setContentView. `setBehindContentView` will place the view in the "behind" portion of the SlidingMenu. You will have access to the `getSlidingMenu` method so you can customize the SlidingMenu to your liking. * If you want to use another library such as ActionBarSherlock, you can just change the SlidingActivities to extend @@ -85,8 +85,12 @@ public class SlidingExample extends Activity { menu.setFadeDegree(0.35f); menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT); menu.setMenu(R.layout.menu); + + // Set the ActionBarSlideIcon, that will replace the up indicator of the ActionBar + slidingMenu.setActionBarSlideIcon(new ActionBarSlideIcon(this, + R.drawable.ic_navigation_drawer, R.string.open_content_desc, R.string.close_content_desc); } - + } ``` @@ -110,12 +114,18 @@ If you decide to use SlidingMenu as a view, you can define it in your xml layout sliding:fadeEnabled="true|false" sliding:fadeDegree="float" sliding:selectorEnabled="true|false" - sliding:selectorDrawable="@drawable/YOUR_SELECTOR"/> + sliding:selectorDrawable="@drawable/YOUR_SELECTOR" + + sliding:actionBarIconSlideable="true" + sliding:actionBarSlideIconDrawable="@drawable/ic_navigation_drawer" + sliding:actionBarSlideIconOpenContentDesc="@string/open_string" + sliding:actionBarSlideIconCloseContentDesc="@string/close_string" + /> ``` NOTE : you cannot use both behindOffset and behindWidth. You will get an exception if you try. * `viewAbove` - a reference to the layout that you want to use as the above view of the SlidingMenu * `viewBehind` - a reference to the layout that you want to use as the behind view of the SlidingMenu -* `touchModeAbove` - an enum that designates what part of the screen is touchable when the above view is +* `touchModeAbove` - an enum that designates what part of the screen is touchable when the above view is showing. Margin means only the left margin. Fullscreen means the entire screen. Default is margin. * `behindOffset` - a dimension representing the number of pixels that you want the above view to show when the behind view is showing. Default is 0. @@ -137,11 +147,15 @@ view showing a selected view on the behind view. * `selectorDrawable` - a reference to a drawable to be used as the selector NOTE : in order to have the selector drawn, you must call SlidingMenu.setSelectedView(View v) with the selected view. Note that this will most likely not work with items in a ListView because of the way that Android recycles item views. +* `actionBarIconSlideable` - Set it to true if you want to enable the slide icon, that will replace the up indicator of the ActionBar. +* `actionBarSlideIconDrawable` - Set the drawable of the slide icon, that will replace the up indicator of the ActionBar. This is optional. If this one is not set, the up indicator of that has been specified in your app theme will be used (homeAsUpIndicator). +* `actionBarSlideIconOpenContentDesc` - This string will be shown as content description for the ActionBar home button, when the SlidingMenu is closed. This attribute is optional. +* `actionBarSlideIconCloseContentDesc`- This string will be shown as content description for the ActionBar home button, when the SlidingMenu is open. This attribute is optional. Caveats ------- * Your layouts have to be based on a viewgroup, unfortunatly this negates the `` optimisations. - + Developed By ------------ @@ -151,19 +165,19 @@ License ------- Copyright 2012 Jeremy Feinstein - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + [1]: http://twitter.com/slidingmenu [2]: http://actionbarsherlock.com/ [3]: https://play.google.com/store/apps/details?id=com.zappos.android&hl=en diff --git a/library/.classpath b/library/.classpath index 570891c68..98df56fc4 100644 --- a/library/.classpath +++ b/library/.classpath @@ -1,8 +1,9 @@ + - + diff --git a/library/libs/android-support-v4.jar b/library/libs/android-support-v4.jar index 6080877d4..cf12d2839 100644 Binary files a/library/libs/android-support-v4.jar and b/library/libs/android-support-v4.jar differ diff --git a/library/res/values/attrs.xml b/library/res/values/attrs.xml index d5ee00f32..0fb5d9917 100644 --- a/library/res/values/attrs.xml +++ b/library/res/values/attrs.xml @@ -40,6 +40,14 @@ + + + + + + + + \ No newline at end of file diff --git a/library/src/com/jeremyfeinstein/slidingmenu/lib/SlidingMenu.java b/library/src/com/jeremyfeinstein/slidingmenu/lib/SlidingMenu.java index b4483635f..6a75886e7 100644 --- a/library/src/com/jeremyfeinstein/slidingmenu/lib/SlidingMenu.java +++ b/library/src/com/jeremyfeinstein/slidingmenu/lib/SlidingMenu.java @@ -28,6 +28,7 @@ import android.widget.RelativeLayout; import com.jeremyfeinstein.slidingmenu.lib.CustomViewAbove.OnPageChangeListener; +import com.jeremyfeinstein.slidingmenu.lib.actionbar.ActionBarSlideIcon; public class SlidingMenu extends RelativeLayout { @@ -37,49 +38,61 @@ public class SlidingMenu extends RelativeLayout { 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 + /** + * 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 + /** + * 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 + /** + * 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. + /** + * 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. + /** + * 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. + /** + * 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 ActionBarSlideIcon mActionBarSlideIcon; - private CustomViewBehind mViewBehind; + private final CustomViewAbove mViewAbove; + + private final CustomViewBehind mViewBehind; private OnOpenListener mOpenListener; - + private OnOpenListener mSecondaryOpenListner; private OnCloseListener mCloseListener; + private OnSlideListener mOnSlideListener; + /** - * 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 addOnOpenListener method. When + * 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 addOnOpenListener method. When * the onOpen event occurs, that object's appropriate * method is invoked */ @@ -92,14 +105,13 @@ public interface OnOpenListener { } /** - * 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 addOnOpenedListener method. When + * 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 addOnOpenedListener method. When * the onOpened event occurs, that object's appropriate * method is invoked. - * + * * @see OnOpenedEvent */ public interface OnOpenedListener { @@ -111,14 +123,13 @@ public interface OnOpenedListener { } /** - * 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 addOnCloseListener method. When + * 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 addOnCloseListener method. When * the onClose event occurs, that object's appropriate * method is invoked. - * + * * @see OnCloseEvent */ public interface OnCloseListener { @@ -130,14 +141,13 @@ public interface OnCloseListener { } /** - * 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 addOnClosedListener method. When + * 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 addOnClosedListener method. When * the onClosed event occurs, that object's appropriate * method is invoked. - * + * * @see OnClosedEvent */ public interface OnClosedListener { @@ -155,17 +165,37 @@ public interface CanvasTransformer { /** * Transform canvas. - * - * @param canvas the canvas - * @param percentOpen the percent open + * + * @param canvas + * the canvas + * @param percentOpen + * the percent open */ public void transformCanvas(Canvas canvas, float percentOpen); } + /** + * This listener will observe the slide out offset of the left menu + * + * @author Hannes Dorfmann + * + */ + public interface OnSlideListener { + + /** + * This method is called to inform how wide open the sliding menu is. + * + * @param offset + * 0.0f means closed (not visible), 1.0f means fully opened + */ + public void onSlideMenu(float offset); + } + /** * Instantiates a new SlidingMenu. - * - * @param context the associated Context + * + * @param context + * the associated Context */ public SlidingMenu(Context context) { this(context, null); @@ -173,9 +203,11 @@ public SlidingMenu(Context context) { /** * Instantiates a new SlidingMenu and attach to Activity. - * - * @param activity the activity to attach slidingmenu - * @param slideStyle the slidingmenu style + * + * @param activity + * the activity to attach slidingmenu + * @param slideStyle + * the slidingmenu style */ public SlidingMenu(Activity activity, int slideStyle) { this(activity, null); @@ -184,9 +216,11 @@ public SlidingMenu(Activity activity, int slideStyle) { /** * Instantiates a new SlidingMenu. - * - * @param context the associated Context - * @param attrs the attrs + * + * @param context + * the associated Context + * @param attrs + * the attrs */ public SlidingMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); @@ -194,18 +228,23 @@ public SlidingMenu(Context context, AttributeSet attrs) { /** * Instantiates a new SlidingMenu. - * - * @param context the associated Context - * @param attrs the attrs - * @param defStyle the def style + * + * @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); - - LayoutParams behindParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + + 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); + LayoutParams aboveParams = new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); mViewAbove = new CustomViewAbove(context); addView(mViewAbove, aboveParams); // register the CustomViewBehind with the CustomViewAbove @@ -216,22 +255,27 @@ public SlidingMenu(Context context, AttributeSet attrs, int defStyle) { public static final int POSITION_CLOSE = 1; public static final int POSITION_SECONDARY_OPEN = 2; + @Override public void onPageScrolled(int position, float positionOffset, - int positionOffsetPixels) { } + int positionOffsetPixels) { + } + @Override 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 ) { + } else if (position == POSITION_SECONDARY_OPEN + && mSecondaryOpenListner != null) { mSecondaryOpenListner.onOpen(); } } }); // now style everything! - TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingMenu); + 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); @@ -241,52 +285,122 @@ public void onPageSelected(int position) { } else { setContent(new FrameLayout(context)); } - int viewBehind = ta.getResourceId(R.styleable.SlidingMenu_viewBehind, -1); + int viewBehind = ta.getResourceId(R.styleable.SlidingMenu_viewBehind, + -1); if (viewBehind != -1) { - setMenu(viewBehind); + setMenu(viewBehind); } else { setMenu(new FrameLayout(context)); } - int touchModeAbove = ta.getInt(R.styleable.SlidingMenu_touchModeAbove, TOUCHMODE_MARGIN); + int touchModeAbove = ta.getInt(R.styleable.SlidingMenu_touchModeAbove, + TOUCHMODE_MARGIN); setTouchModeAbove(touchModeAbove); - int touchModeBehind = ta.getInt(R.styleable.SlidingMenu_touchModeBehind, TOUCHMODE_MARGIN); + 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); + 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"); + 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); + float scrollOffsetBehind = ta.getFloat( + R.styleable.SlidingMenu_behindScrollScale, 0.33f); setBehindScrollScale(scrollOffsetBehind); - int shadowRes = ta.getResourceId(R.styleable.SlidingMenu_shadowDrawable, -1); + int shadowRes = ta.getResourceId( + R.styleable.SlidingMenu_shadowDrawable, -1); if (shadowRes != -1) { setShadowDrawable(shadowRes); } - int shadowWidth = (int) ta.getDimension(R.styleable.SlidingMenu_shadowWidth, 0); + int shadowWidth = (int) ta.getDimension( + R.styleable.SlidingMenu_shadowWidth, 0); setShadowWidth(shadowWidth); - boolean fadeEnabled = ta.getBoolean(R.styleable.SlidingMenu_fadeEnabled, true); + 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); + boolean selectorEnabled = ta.getBoolean( + R.styleable.SlidingMenu_selectorEnabled, false); setSelectorEnabled(selectorEnabled); - int selectorRes = ta.getResourceId(R.styleable.SlidingMenu_selectorDrawable, -1); + int selectorRes = ta.getResourceId( + R.styleable.SlidingMenu_selectorDrawable, -1); if (selectorRes != -1) setSelectorDrawable(selectorRes); + + // ActionBarSlideIcon + boolean xmlAttrActionBarSlideIconEnabled = ta.getBoolean( + R.styleable.SlidingMenu_actionBarIconSlideable, false); + + int xmlAttrActionBarSlideIconDrawable = ta.getResourceId( + R.styleable.SlidingMenu_actionBarSlideIconDrawable, -1); + + int xmlAttrActionBarSlideIconOpenContentDescRes = ta.getResourceId( + R.styleable.SlidingMenu_actionBarSlideIconOpenContentDesc, 0); + + int xmlAttrActionBarSlideIconCloseContentDescRes = ta.getResourceId( + R.styleable.SlidingMenu_actionBarSlideIconCloseContentDesc, 0); + ta.recycle(); + + if (context instanceof Activity) + initActionBarSlideIconFromXmlAttributes(((Activity) context), + xmlAttrActionBarSlideIconEnabled, + xmlAttrActionBarSlideIconDrawable, + xmlAttrActionBarSlideIconOpenContentDescRes, + xmlAttrActionBarSlideIconCloseContentDescRes); + else + Log.e(TAG, + "The context who has created the this View seems not to be an Activity. Therefore the ActionBarSlideIcon could not be instantiated"); + } + + /** + * This method initializes the {@link ActionBarSlideIcon}, that has been + * specified in the {@link SlidingMenu} xml. Since the + * {@link ActionBarSlideIcon} needs a reference to the attached activity, a + * {@link ActionBarSlideIcon} can not be created from the + * {@link #SlidingMenu(Context, AttributeSet, int)} constructor. So this + * method will be called from + * {@link #attachToActivity(Activity, int, boolean)} + * + * @param activity + */ + private void initActionBarSlideIconFromXmlAttributes(Activity activity, + boolean actionBarSlideIconEnabled, + int actionBarSlideIconDrawable, + int actionBarSlideIconOpenContentDescRes, + int actionBarSlideIconCloseContentDescRes) { + + if (actionBarSlideIconEnabled) { + + if (actionBarSlideIconDrawable != -1) { + setActionBarSlideIcon(new ActionBarSlideIcon(activity, + actionBarSlideIconDrawable, + actionBarSlideIconOpenContentDescRes, + actionBarSlideIconCloseContentDescRes)); + } else + setActionBarSlideIcon(new ActionBarSlideIcon(activity, + actionBarSlideIconOpenContentDescRes, + actionBarSlideIconOpenContentDescRes)); + } + } /** * Attaches the SlidingMenu to an entire Activity * - * @param activity the Activity - * @param slideStyle either SLIDING_CONTENT or SLIDING_WINDOW + * @param activity + * the Activity + * @param slideStyle + * either SLIDING_CONTENT or SLIDING_WINDOW */ public void attachToActivity(Activity activity, int slideStyle) { attachToActivity(activity, slideStyle, false); @@ -295,19 +409,26 @@ public void attachToActivity(Activity activity, int slideStyle) { /** * 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) { + * @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"); + 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"); + 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}); + TypedArray a = activity.getTheme().obtainStyledAttributes( + new int[] { android.R.attr.windowBackground }); int background = a.getResourceId(0, 0); a.recycle(); @@ -325,7 +446,8 @@ public void attachToActivity(Activity activity, int slideStyle, boolean actionba case SLIDING_CONTENT: mActionbarOverlay = actionbarOverlay; // take the above view out of - ViewGroup contentParent = (ViewGroup)activity.findViewById(android.R.id.content); + ViewGroup contentParent = (ViewGroup) activity + .findViewById(android.R.id.content); View content = contentParent.getChildAt(0); contentParent.removeView(content); contentParent.addView(this); @@ -335,13 +457,34 @@ public void attachToActivity(Activity activity, int slideStyle, boolean actionba 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 + * Sets the {@link ActionBarSlideIcon} + * + * @param slideIcon + */ + public void setActionBarSlideIcon(ActionBarSlideIcon slideIcon) { + this.mActionBarSlideIcon = slideIcon; + } + + /** + * Get the {@link ActionBarSlideIcon} + * + * @return the {@link ActionBarSlideIcon} or null, if no + * {@link ActionBarSlideIcon} has been set yet + */ + public ActionBarSlideIcon getSlideIcon() { + return mActionBarSlideIcon; + } + + /** + * 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)); @@ -349,8 +492,9 @@ public void setContent(int res) { /** * Set the above view content to the given View. - * - * @param view The desired content to display. + * + * @param view + * The desired content to display. */ public void setContent(View view) { mViewAbove.setContent(view); @@ -359,6 +503,7 @@ public void setContent(View view) { /** * Retrieves the current content. + * * @return the current content */ public View getContent() { @@ -366,10 +511,11 @@ public View 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 + * 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)); @@ -377,8 +523,9 @@ public void setMenu(int res) { /** * Set the behind view (menu) content to the given View. - * - * @param view The desired content to display. + * + * @param view + * The desired content to display. */ public void setMenu(View v) { mViewBehind.setContent(v); @@ -386,6 +533,7 @@ public void setMenu(View v) { /** * Retrieves the main menu. + * * @return the main menu */ public View getMenu() { @@ -393,10 +541,12 @@ public View getMenu() { } /** - * 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 + * 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)); @@ -404,27 +554,29 @@ public void setSecondaryMenu(int res) { /** * Set the secondary behind view (right menu) content to the given View. - * - * @param view The desired content to display. + * + * @param view + * The desired content to display. */ public void setSecondaryMenu(View v) { mViewBehind.setSecondaryContent(v); - // mViewBehind.invalidate(); + // 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. + * + * @param b + * true to enable sliding, false to disable it. */ public void setSlidingEnabled(boolean b) { mViewAbove.setSlidingEnabled(b); @@ -432,7 +584,7 @@ public void setSlidingEnabled(boolean b) { /** * Checks if is sliding enabled. - * + * * @return true, if is sliding enabled */ public boolean isSlidingEnabled() { @@ -441,17 +593,21 @@ public boolean isSlidingEnabled() { /** * Sets which side the SlidingMenu should appear on. - * @param mode must be either SlidingMenu.LEFT or SlidingMenu.RIGHT + * + * @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"); + 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() { @@ -459,19 +615,21 @@ public int 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. + * 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); + // mViewBehind.setCurrentItem(0); } else { mViewAbove.setCurrentItem(1); - // mViewBehind.setCurrentItem(1); + // mViewBehind.setCurrentItem(1); mViewAbove.setCustomViewBehind(mViewBehind); setSlidingEnabled(true); } @@ -486,26 +644,28 @@ public void showMenu() { /** * Opens the menu and shows the menu view. - * - * @param animate true to animate the transition, false to ignore animation + * + * @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. + * 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 + * 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); @@ -520,8 +680,9 @@ public void showContent() { /** * Closes the menu and shows the above view. - * - * @param animate true to animate the transition, false to ignore animation + * + * @param animate + * true to animate the transition, false to ignore animation */ public void showContent(boolean animate) { mViewAbove.setCurrentItem(1, animate); @@ -536,8 +697,9 @@ public void toggle() { /** * 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 + * + * @param animate + * true to animate the transition, false to ignore animation */ public void toggle(boolean animate) { if (isMenuShowing()) { @@ -549,16 +711,17 @@ public void toggle(boolean 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; + 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() { @@ -567,32 +730,38 @@ public boolean isSecondaryMenuShowing() { /** * Gets the behind offset. - * - * @return The margin on the right of the screen that the behind view scrolls to + * + * @return The margin on the right of the screen that the behind view + * scrolls to */ public int getBehindOffset() { - return ((RelativeLayout.LayoutParams)mViewBehind.getLayoutParams()).rightMargin; + 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. + * + * @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); + // 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. + * + * @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); @@ -601,8 +770,9 @@ public void setBehindOffsetRes(int resID) { /** * Sets the above offset. - * - * @param i the new above offset, in pixels + * + * @param i + * the new above offset, in pixels */ public void setAboveOffset(int i) { mViewAbove.setAboveOffset(i); @@ -610,8 +780,9 @@ public void setAboveOffset(int i) { /** * Sets the above offset. - * - * @param resID The dimension resource id to be set as 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); @@ -620,17 +791,18 @@ public void setAboveOffsetRes(int resID) { /** * Sets the behind width. - * - * @param i The width the Sliding Menu will open to, in pixels + * + * @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(); + Display display = ((WindowManager) getContext().getSystemService( + Context.WINDOW_SERVICE)).getDefaultDisplay(); try { Class cls = Display.class; - Class[] parameterTypes = {Point.class}; + Class[] parameterTypes = { Point.class }; Point parameter = new Point(); Method method = cls.getMethod("getSize", parameterTypes); method.invoke(display, parameter); @@ -638,14 +810,15 @@ public void setBehindWidth(int i) { } catch (Exception e) { width = display.getWidth(); } - setBehindOffset(width-i); + 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. + * + * @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); @@ -654,23 +827,25 @@ public void setBehindWidthRes(int res) { /** * 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) { @@ -679,20 +854,24 @@ public void setTouchmodeMarginThreshold(int 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) + * + * @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"); + 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 + * + * @param t + * the new behind canvas transformer */ public void setBehindCanvasTransformer(CanvasTransformer t) { mViewBehind.setCanvasTransformer(t); @@ -700,7 +879,7 @@ public void setBehindCanvasTransformer(CanvasTransformer t) { /** * Gets the touch mode above. - * + * * @return the touch mode above */ public int getTouchModeAbove() { @@ -709,40 +888,47 @@ public int getTouchModeAbove() { /** * 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 + * 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."); + 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 + * 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."); + 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 + * + * @param resId + * the resource ID of the new shadow drawable */ public void setShadowDrawable(int resId) { setShadowDrawable(getContext().getResources().getDrawable(resId)); @@ -750,8 +936,9 @@ public void setShadowDrawable(int resId) { /** * Sets the shadow drawable. - * - * @param d the new shadow drawable + * + * @param d + * the new shadow drawable */ public void setShadowDrawable(Drawable d) { mViewBehind.setShadowDrawable(d); @@ -759,17 +946,20 @@ public void setShadowDrawable(Drawable d) { /** * Sets the secondary (right) shadow drawable. - * - * @param resId the resource ID of the new shadow drawable + * + * @param resId + * the resource ID of the new shadow drawable */ public void setSecondaryShadowDrawable(int resId) { - setSecondaryShadowDrawable(getContext().getResources().getDrawable(resId)); + setSecondaryShadowDrawable(getContext().getResources().getDrawable( + resId)); } /** * Sets the secondary (right) shadow drawable. - * - * @param d the new shadow drawable + * + * @param d + * the new shadow drawable */ public void setSecondaryShadowDrawable(Drawable d) { mViewBehind.setSecondaryShadowDrawable(d); @@ -777,17 +967,19 @@ public void setSecondaryShadowDrawable(Drawable d) { /** * Sets the shadow width. - * - * @param resId The dimension resource id to be set as 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)); + setShadowWidth((int) getResources().getDimension(resId)); } /** * Sets the shadow width. - * - * @param pixels the new shadow width, in pixels + * + * @param pixels + * the new shadow width, in pixels */ public void setShadowWidth(int pixels) { mViewBehind.setShadowWidth(pixels); @@ -795,8 +987,9 @@ public void setShadowWidth(int pixels) { /** * Enables or disables the SlidingMenu's fade in and out - * - * @param b true to enable fade, false to disable it + * + * @param b + * true to enable fade, false to disable it */ public void setFadeEnabled(boolean b) { mViewBehind.setFadeEnabled(b); @@ -805,8 +998,9 @@ public void setFadeEnabled(boolean 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 + * + * @param f + * the new fade degree, between 0.0f and 1.0f */ public void setFadeDegree(float f) { mViewBehind.setFadeDegree(f); @@ -814,8 +1008,9 @@ public void setFadeDegree(float f) { /** * Enables or disables whether the selector is drawn - * - * @param b true to draw the selector, false to not draw the selector + * + * @param b + * true to draw the selector, false to not draw the selector */ public void setSelectorEnabled(boolean b) { mViewBehind.setSelectorEnabled(true); @@ -823,8 +1018,9 @@ public void setSelectorEnabled(boolean b) { /** * Sets the selected view. The selector will be drawn here - * - * @param v the new selected view + * + * @param v + * the new selected view */ public void setSelectedView(View v) { mViewBehind.setSelectedView(v); @@ -832,17 +1028,20 @@ public void setSelectedView(View v) { /** * Sets the selector drawable. - * - * @param res a resource ID for the selector drawable + * + * @param res + * a resource ID for the selector drawable */ public void setSelectorDrawable(int res) { - mViewBehind.setSelectorBitmap(BitmapFactory.decodeResource(getResources(), res)); + mViewBehind.setSelectorBitmap(BitmapFactory.decodeResource( + getResources(), res)); } /** * Sets the selector drawable. - * - * @param b the new selector bitmap + * + * @param b + * the new selector bitmap */ public void setSelectorBitmap(Bitmap b) { mViewBehind.setSelectorBitmap(b); @@ -850,8 +1049,9 @@ public void setSelectorBitmap(Bitmap b) { /** * Add a View ignored by the Touch Down event when mode is Fullscreen - * - * @param v a view to be ignored + * + * @param v + * a view to be ignored */ public void addIgnoredView(View v) { mViewAbove.addIgnoredView(v); @@ -859,69 +1059,93 @@ public void addIgnoredView(View v) { /** * Remove a View ignored by the Touch Down event when mode is Fullscreen - * - * @param v a view not wanted to be ignored anymore + * + * @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 + * 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 + * 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); + // 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 + * 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 + * @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 + * 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); + // 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 + * 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 + * 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); } + /** + * Set the {@link OnSlideListener} + * + * @param listener + */ + public void setOnSlideListener(OnSlideListener listener) { + this.mOnSlideListener = listener; + } + public static class SavedState extends BaseSavedState { private final int mItem; @@ -940,20 +1164,24 @@ public int getItem() { return mItem; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see android.view.AbsSavedState#writeToParcel(android.os.Parcel, int) */ + @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(mItem); } - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } + @Override public SavedState[] newArray(int size) { return new SavedState[size]; } @@ -961,7 +1189,9 @@ public SavedState[] newArray(int size) { } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see android.view.View#onSaveInstanceState() */ @Override @@ -971,17 +1201,21 @@ protected Parcelable onSaveInstanceState() { return ss; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see android.view.View#onRestoreInstanceState(android.os.Parcelable) */ @Override protected void onRestoreInstanceState(Parcelable state) { - SavedState ss = (SavedState)state; + SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); mViewAbove.setCurrentItem(ss.getItem()); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see android.view.ViewGroup#fitSystemWindows(android.graphics.Rect) */ @SuppressLint("NewApi") @@ -997,20 +1231,31 @@ protected boolean fitSystemWindows(Rect insets) { } return true; } - - private Handler mHandler = new Handler(); + + private final Handler mHandler = new Handler(); @TargetApi(Build.VERSION_CODES.HONEYCOMB) public void manageLayers(float percentOpen) { - if (Build.VERSION.SDK_INT < 11) return; + + if (mOnSlideListener != null) + mOnSlideListener.onSlideMenu(percentOpen); + + if (mActionBarSlideIcon != null) + mActionBarSlideIcon.setSlideOffset(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; + final int layerType = layer ? View.LAYER_TYPE_HARDWARE + : View.LAYER_TYPE_NONE; if (layerType != getContent().getLayerType()) { mHandler.post(new Runnable() { + @Override public void run() { - Log.v(TAG, "changing layerType. hardware? " + (layerType == View.LAYER_TYPE_HARDWARE)); + Log.v(TAG, "changing layerType. hardware? " + + (layerType == View.LAYER_TYPE_HARDWARE)); getContent().setLayerType(layerType, null); getMenu().setLayerType(layerType, null); if (getSecondaryMenu() != null) { diff --git a/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarHelper.java b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarHelper.java new file mode 100644 index 000000000..4df734a2a --- /dev/null +++ b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarHelper.java @@ -0,0 +1,105 @@ +package com.jeremyfeinstein.slidingmenu.lib.actionbar; + +import java.lang.reflect.Method; + +import android.app.Activity; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.Log; + +public final class ActionBarHelper { + + public static final String TAG = "ActionBarHelper"; + + public static final boolean DEBUG = false; + + private final Activity mActivity; + + private final ActionBarUpIndicator mUpIndicator; + + private boolean mUsesCompat; + + public ActionBarHelper(Activity activity) { + mActivity = activity; + + try { + Class clazz = activity.getClass(); + Method m = clazz.getMethod("getSupportActionBar"); + mUsesCompat = true; + } catch (NoSuchMethodException e) { + if (DEBUG) { + Log.e(TAG, "Activity " + activity.getClass().getSimpleName() + + " does not use a compatibility action bar", e); + } + } + + mUpIndicator = getUpIndicator(); + } + + /** + * Get the concrete and correct {@link ActionBarUpIndicator} depending on + * the Android version + * + * @return + */ + private ActionBarUpIndicator getUpIndicator() { + if (mUsesCompat + && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return new CompatUpIndicator(mActivity); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + return new NativeUpIndicator(mActivity); + } + + return null; + } + + /** + * Set the {@link ActionBarUpIndicator}s drawable and content description + * + * @param drawable + * @param contentDesc + */ + public void setActionBarUpIndicator(Drawable drawable, int contentDesc) { + + if (mUpIndicator != null) { + mUpIndicator.setDrawable(mActivity, drawable); + mUpIndicator.setContentDescription(mActivity, contentDesc); + } + } + + /** + * Set the content description of the up indicator + * + * @param contentDesc + */ + public void setActionBarUpDescription(int contentDesc) { + + if (mUpIndicator != null) { + mUpIndicator.setContentDescription(mActivity, contentDesc); + } + + } + + /** + * Get the up indicator that has been set by the app theme + * + * @return The themed up indicator or null + */ + public Drawable getThemeUpIndicator() { + + if (mUpIndicator != null) { + return mUpIndicator.getThemeUpIndicator(mActivity); + } + + return null; + } + + /** + * + * @param enabled + */ + public void setDisplayShowHomeAsUpEnabled(boolean enabled) { + if (mUpIndicator != null) + mUpIndicator.setDisplayHomeAsUpEnabled(mActivity, enabled); + } +} diff --git a/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarSlideIcon.java b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarSlideIcon.java new file mode 100644 index 000000000..0d1af3be3 --- /dev/null +++ b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarSlideIcon.java @@ -0,0 +1,352 @@ +package com.jeremyfeinstein.slidingmenu.lib.actionbar; + +import android.app.Activity; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.drawable.Drawable; + +import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu; + +/** + * + * @author Hannes Dorfmann + * + */ +public class ActionBarSlideIcon { + + /** + * This is a drawable can slide. This will wrap the home icon and allows + * that to slide + * + * @author Hannes Dorfmann + * + */ + private static class SlideDrawable extends Drawable implements + Drawable.Callback { + private Drawable mWrapped; + private float mOffset; + private float mOffsetBy; + + private final Rect mTmpRect = new Rect(); + + public SlideDrawable(Drawable wrapped) { + mWrapped = wrapped; + } + + public void setOffset(float offset) { + mOffset = offset; + invalidateSelf(); + } + + public float getOffset() { + return mOffset; + } + + public void setOffsetBy(float offsetBy) { + mOffsetBy = offsetBy; + invalidateSelf(); + } + + @Override + public void draw(Canvas canvas) { + mWrapped.copyBounds(mTmpRect); + canvas.save(); + canvas.translate(mOffsetBy * mTmpRect.width() * -mOffset, 0); + mWrapped.draw(canvas); + canvas.restore(); + + } + + @Override + public void setChangingConfigurations(int configs) { + mWrapped.setChangingConfigurations(configs); + } + + @Override + public int getChangingConfigurations() { + return mWrapped.getChangingConfigurations(); + } + + @Override + public void setDither(boolean dither) { + mWrapped.setDither(dither); + } + + @Override + public void setFilterBitmap(boolean filter) { + mWrapped.setFilterBitmap(filter); + } + + @Override + public void setAlpha(int alpha) { + mWrapped.setAlpha(alpha); + } + + @Override + public void setColorFilter(ColorFilter cf) { + mWrapped.setColorFilter(cf); + } + + @Override + public void setColorFilter(int color, PorterDuff.Mode mode) { + mWrapped.setColorFilter(color, mode); + } + + @Override + public void clearColorFilter() { + mWrapped.clearColorFilter(); + } + + @Override + public boolean isStateful() { + return mWrapped.isStateful(); + } + + @Override + public boolean setState(int[] stateSet) { + return mWrapped.setState(stateSet); + } + + @Override + public int[] getState() { + return mWrapped.getState(); + } + + @Override + public Drawable getCurrent() { + return mWrapped.getCurrent(); + } + + @Override + public boolean setVisible(boolean visible, boolean restart) { + return super.setVisible(visible, restart); + } + + @Override + public int getOpacity() { + return mWrapped.getOpacity(); + } + + @Override + public Region getTransparentRegion() { + return mWrapped.getTransparentRegion(); + } + + @Override + protected boolean onStateChange(int[] state) { + mWrapped.setState(state); + return super.onStateChange(state); + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + mWrapped.setBounds(bounds); + } + + @Override + public int getIntrinsicWidth() { + return mWrapped.getIntrinsicWidth(); + } + + @Override + public int getIntrinsicHeight() { + return mWrapped.getIntrinsicHeight(); + } + + @Override + public int getMinimumWidth() { + return mWrapped.getMinimumWidth(); + } + + @Override + public int getMinimumHeight() { + return mWrapped.getMinimumHeight(); + } + + @Override + public boolean getPadding(Rect padding) { + return mWrapped.getPadding(padding); + } + + @Override + public ConstantState getConstantState() { + return super.getConstantState(); + } + + @Override + public void invalidateDrawable(Drawable who) { + if (who == mWrapped) { + invalidateSelf(); + } + } + + @Override + public void scheduleDrawable(Drawable who, Runnable what, long when) { + if (who == mWrapped) { + scheduleSelf(what, when); + } + } + + @Override + public void unscheduleDrawable(Drawable who, Runnable what) { + if (who == mWrapped) { + unscheduleSelf(what); + } + } + } + + /** + * The {@link SlideDrawable} that will replace the up Indicator + */ + private SlideDrawable mSlideDrawble; + + private ActionBarHelper mActionBarHelper = null; + + private int mCloseContentDescription; + private int mOpenContentDescription; + + /** + * Creates a new {@link ActionBarSlideIcon} + * + * @param activity + * The activity + * @param slideDrawable + * The {@link Drawable} that can slide + * @param openContentDescRes + * A String resource to describe the "open drawer" action for + * accessibility + * @param closeContentDescRes + * A String resource to describe the "close drawer" action for + * accessibility + */ + public ActionBarSlideIcon(Activity activity, Drawable slideDrawable, + int openContentDescRes, int closeContentDescRes) { + + initActionBar(activity, slideDrawable, openContentDescRes, + closeContentDescRes); + } + + /** + * Creates a new {@link ActionBarSlideIcon} + * + * @param activity + * The activity + * @param drawableRes + * The resource id of the drawable that will replace the up + * indicator icon + * @param openContentDescRes + * A String resource to describe the "open drawer" action for + * accessibility + * @param closeContentDescRes + * A String resource to describe the "close drawer" action for + * accessibility + */ + public ActionBarSlideIcon(Activity activity, int drawableRes, + int openContentDescRes, int closeContentDescRes) { + + this(activity, activity.getResources().getDrawable(drawableRes), + openContentDescRes, closeContentDescRes); + } + + /** + * Creates a new {@link ActionBarSlideIcon}. Instead of specifying the + * drawable that should be replace the up indicator the default up indicator + * (specified in the apps theme) will be used to slide + * + * @param activity + * @param openContentDescRes + * @param closeContentDescRes + */ + public ActionBarSlideIcon(Activity activity, int openContentDescRes, + int closeContentDescRes) { + + if (mActionBarHelper == null) + mActionBarHelper = new ActionBarHelper(activity); + + Drawable themedIcon = mActionBarHelper.getThemeUpIndicator(); + + if (themedIcon == null) + throw new IllegalStateException( + "The theme of you app has not specified an up indicator icon"); + + initActionBar(activity, themedIcon, openContentDescRes, + closeContentDescRes); + + } + + /** + * Initializes the required components. This method is called from the + * constructors. + * + * @param activity + * @param slideDrawable + * @param openContentDescRes + * @param closeContentDescRes + */ + private void initActionBar(Activity activity, Drawable slideDrawable, + int openContentDescRes, int closeContentDescRes) { + + mCloseContentDescription = closeContentDescRes; + mOpenContentDescription = openContentDescRes; + + mSlideDrawble = new SlideDrawable(slideDrawable); + mSlideDrawble.setOffsetBy(1.f / 3); + + if (mActionBarHelper == null) + mActionBarHelper = new ActionBarHelper(activity); + + mActionBarHelper.setActionBarUpIndicator(mSlideDrawble, + mOpenContentDescription); + mActionBarHelper.setDisplayShowHomeAsUpEnabled(true); + } + + /** + * Get the {@link Drawable} that will slide + * + * @return + */ + public Drawable getDrawable() { + return mSlideDrawble.mWrapped; + } + + /** + * Set the {@link Drawable} of the ActionBar + * + * @param drawable + */ + public void setDrawable(Drawable drawable) { + mSlideDrawble.mWrapped = drawable; + mSlideDrawble.invalidateSelf(); + } + + /** + * Set the offset, how far the menu has been slide out. This method should + * only be accessed from {@link SlidingMenu} + * + * @param offset + */ + public void setSlideOffset(float offset) { + + if (offset == 0) + mActionBarHelper.setActionBarUpDescription(mOpenContentDescription); + + if (offset == 1) + mActionBarHelper + .setActionBarUpDescription(mCloseContentDescription); + + float glyphOffset = mSlideDrawble.getOffset(); + if (offset > 0.5f) { + glyphOffset = Math.max(glyphOffset, + Math.max(0.f, offset - 0.5f) * 2); + } else { + glyphOffset = Math.min(glyphOffset, offset * 2); + } + mSlideDrawble.setOffset(glyphOffset); + + } + +} diff --git a/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarUpIndicator.java b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarUpIndicator.java new file mode 100644 index 000000000..dc67b416f --- /dev/null +++ b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/ActionBarUpIndicator.java @@ -0,0 +1,44 @@ +package com.jeremyfeinstein.slidingmenu.lib.actionbar; + +import android.app.Activity; +import android.graphics.drawable.Drawable; + +/** + * This interface provides the basic methods to access the ActionBar up + * indicator + * + * @author Hannes Dorfmann + * + */ +public interface ActionBarUpIndicator { + + /** + * Sets the content description of the up indicator + * + * @param contentDescResId + */ + public void setContentDescription(Activity activity, int contentDescResId); + + /** + * Sets the drawable that will replace the default up indicator + * + * @param activity + * @param drawable + * The new drawable that will replace the default up indicator + * drawable + */ + public void setDrawable(Activity activity, Drawable drawable); + + /** + * Enables the up method button to be clickable + */ + public void setDisplayHomeAsUpEnabled(Activity activity, boolean enabled); + + /** + * Get the Drawable that has been specified as up indicator in the theme + * + * @param activity + * @return + */ + public Drawable getThemeUpIndicator(Activity activity); +} diff --git a/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/CompatUpIndicator.java b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/CompatUpIndicator.java new file mode 100644 index 000000000..36cfef27d --- /dev/null +++ b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/CompatUpIndicator.java @@ -0,0 +1,120 @@ +package com.jeremyfeinstein.slidingmenu.lib.actionbar; + +import java.lang.reflect.Method; + +import android.app.Activity; +import android.graphics.drawable.Drawable; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + + +/** + * This is the implementation of the {@link ActionBarUpIndicator} for the compat + * libraries like ActionBarSherlock ActionBar. I assume that the compat Activity + * has a mehtod getSupportActionBar. + * + * @author Hannes Dorfmann + * + */ +public class CompatUpIndicator implements ActionBarUpIndicator { + + private ImageView mUpIndicatorView; + private Object mActionBar; + private Method mHomeAsUpEnabled; + + public CompatUpIndicator(Activity activity) { + try { + String appPackage = activity.getPackageName(); + + try { + // Attempt to find ActionBarSherlock up indicator + final int homeId = activity.getResources().getIdentifier( + "abs__home", "id", appPackage); + View v = activity.findViewById(homeId); + ViewGroup parent = (ViewGroup) v.getParent(); + final int upId = activity.getResources().getIdentifier( + "abs__up", "id", appPackage); + mUpIndicatorView = (ImageView) parent.findViewById(upId); + } catch (Throwable t) { + if (ActionBarHelper.DEBUG) { + Log.e(ActionBarHelper.TAG, "ABS action bar not found", t); + } + } + + if (mUpIndicatorView == null) { + // Attempt to find AppCompat up indicator + final int homeId = activity.getResources().getIdentifier( + "home", "id", appPackage); + View v = activity.findViewById(homeId); + ViewGroup parent = (ViewGroup) v.getParent(); + final int upId = activity.getResources().getIdentifier("up", + "id", appPackage); + mUpIndicatorView = (ImageView) parent.findViewById(upId); + } + + Class supportActivity = activity.getClass(); + Method getActionBar = supportActivity + .getMethod("getSupportActionBar"); + + mActionBar = getActionBar.invoke(activity, null); + Class supportActionBar = mActionBar.getClass(); + mHomeAsUpEnabled = supportActionBar.getMethod( + "setDisplayHomeAsUpEnabled", Boolean.TYPE); + + } catch (Throwable t) { + if (ActionBarHelper.DEBUG) { + Log.e(ActionBarHelper.TAG, + "Unable to init IndicatorWrapper for ABS", t); + } + } + } + + @Override + public void setContentDescription(Activity activity, int contentDescResId) { + + if (mUpIndicatorView != null) { + final String contentDescription = contentDescResId == 0 ? null + : activity.getString(contentDescResId); + mUpIndicatorView.setContentDescription(contentDescription); + } + + } + + @Override + public void setDrawable(Activity activity, Drawable drawable) { + + if (mUpIndicatorView != null) { + mUpIndicatorView.setImageDrawable(drawable); + } + + } + + @Override + public void setDisplayHomeAsUpEnabled(Activity activity, boolean enabled) { + + if (mHomeAsUpEnabled != null) { + try { + mHomeAsUpEnabled.invoke(mActionBar, enabled); + } catch (Throwable t) { + if (ActionBarHelper.DEBUG) { + Log.e(ActionBarHelper.TAG, + "Unable to call setHomeAsUpEnabled", t); + } + } + } + + } + + @Override + public Drawable getThemeUpIndicator(Activity activity) { + + if (mUpIndicatorView != null) { + return mUpIndicatorView.getDrawable(); + } + + return null; + } + +} diff --git a/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/NativeUpIndicator.java b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/NativeUpIndicator.java new file mode 100644 index 000000000..c0457f51a --- /dev/null +++ b/library/src/com/jeremyfeinstein/slidingmenu/lib/actionbar/NativeUpIndicator.java @@ -0,0 +1,126 @@ +package com.jeremyfeinstein.slidingmenu.lib.actionbar; + +import java.lang.reflect.Method; + +import android.annotation.TargetApi; +import android.app.ActionBar; +import android.app.Activity; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + + +/** + * This is the implementation of the {@link ActionBarUpIndicator} for the native + * ActionBar + * + * @author Hannes Dorfmann + * + */ +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +public class NativeUpIndicator implements ActionBarUpIndicator { + + private static final int[] THEME_ATTRS = new int[] { android.R.attr.homeAsUpIndicator }; + + public Method setHomeAsUpIndicator; + public Method setHomeActionContentDescription; + public ImageView upIndicatorView; + + public NativeUpIndicator(Activity activity) { + try { + setHomeAsUpIndicator = ActionBar.class.getDeclaredMethod( + "setHomeAsUpIndicator", Drawable.class); + setHomeActionContentDescription = ActionBar.class + .getDeclaredMethod("setHomeActionContentDescription", + Integer.TYPE); + + // If we got the method we won't need the stuff below. + return; + } catch (Throwable t) { + // Oh well. We'll use the other mechanism below instead. + } + + final View home = activity.findViewById(android.R.id.home); + if (home == null) { + // Action bar doesn't have a known configuration, an OEM messed + // with things. + return; + } + + final ViewGroup parent = (ViewGroup) home.getParent(); + final int childCount = parent.getChildCount(); + if (childCount != 2) { + // No idea which one will be the right one, an OEM messed with + // things. + return; + } + + final View first = parent.getChildAt(0); + final View second = parent.getChildAt(1); + final View up = first.getId() == android.R.id.home ? second : first; + + if (up instanceof ImageView) { + // Jackpot! (Probably...) + upIndicatorView = (ImageView) up; + } + } + + @Override + public void setContentDescription(Activity activity, int contentDescResId) { + + try { + final ActionBar actionBar = activity.getActionBar(); + setHomeActionContentDescription.invoke(actionBar, contentDescResId); + } catch (Throwable t) { + if (ActionBarHelper.DEBUG) + Log.e(ActionBarHelper.TAG, + "Couldn't set content description via JB-MR2 API", t); + } + } + + @Override + public void setDrawable(Activity activity, Drawable drawable) { + if (setHomeAsUpIndicator != null) { + try { + final ActionBar actionBar = activity.getActionBar(); + setHomeAsUpIndicator.invoke(actionBar, drawable); + + } catch (Throwable t) { + if (ActionBarHelper.DEBUG) + Log.e(ActionBarHelper.TAG, + "Couldn't set home-as-up indicator via JB-MR2 API", + t); + } + } else if (upIndicatorView != null) { + upIndicatorView.setImageDrawable(drawable); + } else { + if (ActionBarHelper.DEBUG) + Log.e(ActionBarHelper.TAG, "Couldn't set home-as-up indicator"); + } + + } + + @Override + public void setDisplayHomeAsUpEnabled(Activity activity, boolean enabled) { + + ActionBar actionBar = activity.getActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(enabled); + } + + } + + @Override + public Drawable getThemeUpIndicator(Activity activity) { + + final TypedArray a = activity.obtainStyledAttributes(THEME_ATTRS); + final Drawable result = a.getDrawable(0); + a.recycle(); + return result; + + } +} \ No newline at end of file