MenuDrawer.java 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652
  1. package net.simonvt.menudrawer;
  2. import net.simonvt.menudrawer.compat.ActionBarHelper;
  3. import android.app.Activity;
  4. import android.content.Context;
  5. import android.content.res.TypedArray;
  6. import android.graphics.Bitmap;
  7. import android.graphics.BitmapFactory;
  8. import android.graphics.Canvas;
  9. import android.graphics.Rect;
  10. import android.graphics.drawable.Drawable;
  11. import android.graphics.drawable.GradientDrawable;
  12. import android.os.Build;
  13. import android.os.Bundle;
  14. import android.os.Parcel;
  15. import android.os.Parcelable;
  16. import android.util.AttributeSet;
  17. import android.util.Log;
  18. import android.view.LayoutInflater;
  19. import android.view.View;
  20. import android.view.ViewGroup;
  21. import android.view.ViewParent;
  22. import android.view.ViewTreeObserver;
  23. import android.view.animation.AccelerateInterpolator;
  24. import android.view.animation.Interpolator;
  25. public abstract class MenuDrawer extends ViewGroup {
  26. /**
  27. * Callback interface for changing state of the drawer.
  28. */
  29. public interface OnDrawerStateChangeListener {
  30. /**
  31. * Called when the drawer state changes.
  32. *
  33. * @param oldState The old drawer state.
  34. * @param newState The new drawer state.
  35. */
  36. void onDrawerStateChange(int oldState, int newState);
  37. /**
  38. * Called when the drawer slides.
  39. *
  40. * @param openRatio Ratio for how open the menu is.
  41. * @param offsetPixels Current offset of the menu in pixels.
  42. */
  43. void onDrawerSlide(float openRatio, int offsetPixels);
  44. }
  45. /**
  46. * Callback that is invoked when the drawer is in the process of deciding whether it should intercept the touch
  47. * event. This lets the listener decide if the pointer is on a view that would disallow dragging of the drawer.
  48. * This is only called when the touch mode is {@link #TOUCH_MODE_FULLSCREEN}.
  49. */
  50. public interface OnInterceptMoveEventListener {
  51. /**
  52. * Called for each child the pointer i on when the drawer is deciding whether to intercept the touch event.
  53. *
  54. * @param v View to test for draggability
  55. * @param delta Delta drag in pixels
  56. * @param x X coordinate of the active touch point
  57. * @param y Y coordinate of the active touch point
  58. * @return true if view is draggable by delta dx.
  59. */
  60. boolean isViewDraggable(View v, int delta, int x, int y);
  61. }
  62. public enum Type {
  63. /**
  64. * Positions the drawer behind the content.
  65. */
  66. BEHIND,
  67. /**
  68. * A static drawer that can not be dragged.
  69. */
  70. STATIC,
  71. /**
  72. * Positions the drawer on top of the content.
  73. */
  74. OVERLAY,
  75. }
  76. /**
  77. * Tag used when logging.
  78. */
  79. private static final String TAG = "MenuDrawer";
  80. /**
  81. * Indicates whether debug code should be enabled.
  82. */
  83. private static final boolean DEBUG = false;
  84. /**
  85. * The time between each frame when animating the drawer.
  86. */
  87. protected static final int ANIMATION_DELAY = 1000 / 60;
  88. /**
  89. * The default touch bezel size of the drawer in dp.
  90. */
  91. private static final int DEFAULT_DRAG_BEZEL_DP = 24;
  92. /**
  93. * The default drop shadow size in dp.
  94. */
  95. private static final int DEFAULT_DROP_SHADOW_DP = 6;
  96. /**
  97. * Drag mode for sliding only the content view.
  98. */
  99. public static final int MENU_DRAG_CONTENT = 0;
  100. /**
  101. * Drag mode for sliding the entire window.
  102. */
  103. public static final int MENU_DRAG_WINDOW = 1;
  104. /**
  105. * Disallow opening the drawer by dragging the screen.
  106. */
  107. public static final int TOUCH_MODE_NONE = 0;
  108. /**
  109. * Allow opening drawer only by dragging on the edge of the screen.
  110. */
  111. public static final int TOUCH_MODE_BEZEL = 1;
  112. /**
  113. * Allow opening drawer by dragging anywhere on the screen.
  114. */
  115. public static final int TOUCH_MODE_FULLSCREEN = 2;
  116. /**
  117. * Indicates that the drawer is currently closed.
  118. */
  119. public static final int STATE_CLOSED = 0;
  120. /**
  121. * Indicates that the drawer is currently closing.
  122. */
  123. public static final int STATE_CLOSING = 1;
  124. /**
  125. * Indicates that the drawer is currently being dragged by the user.
  126. */
  127. public static final int STATE_DRAGGING = 2;
  128. /**
  129. * Indicates that the drawer is currently opening.
  130. */
  131. public static final int STATE_OPENING = 4;
  132. /**
  133. * Indicates that the drawer is currently open.
  134. */
  135. public static final int STATE_OPEN = 8;
  136. /**
  137. * Indicates whether to use {@link View#setTranslationX(float)} when positioning views.
  138. */
  139. static final boolean USE_TRANSLATIONS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
  140. /**
  141. * Time to animate the indicator to the new active view.
  142. */
  143. static final int INDICATOR_ANIM_DURATION = 800;
  144. /**
  145. * The maximum animation duration.
  146. */
  147. private static final int DEFAULT_ANIMATION_DURATION = 600;
  148. /**
  149. * Interpolator used when animating the drawer open/closed.
  150. */
  151. protected static final Interpolator SMOOTH_INTERPOLATOR = new SmoothInterpolator();
  152. /**
  153. * Interpolator used for stretching/retracting the active indicator.
  154. */
  155. protected static final Interpolator INDICATOR_INTERPOLATOR = new AccelerateInterpolator();
  156. /**
  157. * Drawable used as menu overlay.
  158. */
  159. protected Drawable mMenuOverlay;
  160. /**
  161. * Defines whether the drop shadow is enabled.
  162. */
  163. protected boolean mDropShadowEnabled;
  164. /**
  165. * The color of the drop shadow.
  166. */
  167. protected int mDropShadowColor;
  168. /**
  169. * Drawable used as content drop shadow onto the menu.
  170. */
  171. protected Drawable mDropShadowDrawable;
  172. private boolean mCustomDropShadow;
  173. /**
  174. * The size of the content drop shadow.
  175. */
  176. protected int mDropShadowSize;
  177. /**
  178. * Bitmap used to indicate the active view.
  179. */
  180. protected Bitmap mActiveIndicator;
  181. /**
  182. * The currently active view.
  183. */
  184. protected View mActiveView;
  185. /**
  186. * Position of the active view. This is compared to View#getTag(R.id.mdActiveViewPosition) when drawing the
  187. * indicator.
  188. */
  189. protected int mActivePosition;
  190. /**
  191. * Whether the indicator should be animated between positions.
  192. */
  193. private boolean mAllowIndicatorAnimation;
  194. /**
  195. * Used when reading the position of the active view.
  196. */
  197. protected final Rect mActiveRect = new Rect();
  198. /**
  199. * Temporary {@link Rect} used for deciding whether the view should be invalidated so the indicator can be redrawn.
  200. */
  201. private final Rect mTempRect = new Rect();
  202. /**
  203. * The custom menu view set by the user.
  204. */
  205. private View mMenuView;
  206. /**
  207. * The parent of the menu view.
  208. */
  209. protected BuildLayerFrameLayout mMenuContainer;
  210. /**
  211. * The parent of the content view.
  212. */
  213. protected BuildLayerFrameLayout mContentContainer;
  214. /**
  215. * The size of the menu (width or height depending on the gravity).
  216. */
  217. protected int mMenuSize;
  218. /**
  219. * Indicates whether the menu is currently visible.
  220. */
  221. protected boolean mMenuVisible;
  222. /**
  223. * The drag mode of the drawer. Can be either {@link #MENU_DRAG_CONTENT} or {@link #MENU_DRAG_WINDOW}.
  224. */
  225. private int mDragMode = MENU_DRAG_CONTENT;
  226. /**
  227. * The current drawer state.
  228. *
  229. * @see #STATE_CLOSED
  230. * @see #STATE_CLOSING
  231. * @see #STATE_DRAGGING
  232. * @see #STATE_OPENING
  233. * @see #STATE_OPEN
  234. */
  235. protected int mDrawerState = STATE_CLOSED;
  236. /**
  237. * The touch bezel size of the drawer in px.
  238. */
  239. protected int mTouchBezelSize;
  240. /**
  241. * The touch area size of the drawer in px.
  242. */
  243. protected int mTouchSize;
  244. /**
  245. * Listener used to dispatch state change events.
  246. */
  247. private OnDrawerStateChangeListener mOnDrawerStateChangeListener;
  248. /**
  249. * Touch mode for the Drawer.
  250. * Possible values are {@link #TOUCH_MODE_NONE}, {@link #TOUCH_MODE_BEZEL} or {@link #TOUCH_MODE_FULLSCREEN}
  251. * Default: {@link #TOUCH_MODE_BEZEL}
  252. */
  253. protected int mTouchMode = TOUCH_MODE_BEZEL;
  254. /**
  255. * Indicates whether to use {@link View#LAYER_TYPE_HARDWARE} when animating the drawer.
  256. */
  257. protected boolean mHardwareLayersEnabled = true;
  258. /**
  259. * The Activity the drawer is attached to.
  260. */
  261. private Activity mActivity;
  262. /**
  263. * Scroller used when animating the indicator to a new position.
  264. */
  265. private FloatScroller mIndicatorScroller;
  266. /**
  267. * Runnable used when animating the indicator to a new position.
  268. */
  269. private Runnable mIndicatorRunnable = new Runnable() {
  270. @Override
  271. public void run() {
  272. animateIndicatorInvalidate();
  273. }
  274. };
  275. /**
  276. * The start position of the indicator when animating it to a new position.
  277. */
  278. protected int mIndicatorStartPos;
  279. /**
  280. * [0..1] value indicating the current progress of the animation.
  281. */
  282. protected float mIndicatorOffset;
  283. /**
  284. * Whether the indicator is currently animating.
  285. */
  286. protected boolean mIndicatorAnimating;
  287. /**
  288. * Bundle used to hold the drawers state.
  289. */
  290. protected Bundle mState;
  291. /**
  292. * The maximum duration of open/close animations.
  293. */
  294. protected int mMaxAnimationDuration = DEFAULT_ANIMATION_DURATION;
  295. /**
  296. * Callback that lets the listener override intercepting of touch events.
  297. */
  298. protected OnInterceptMoveEventListener mOnInterceptMoveEventListener;
  299. protected SlideDrawable mSlideDrawable;
  300. protected Drawable mThemeUpIndicator;
  301. protected boolean mDrawerIndicatorEnabled;
  302. private ActionBarHelper mActionBarHelper;
  303. private int mCurrentUpContentDesc;
  304. private int mDrawerOpenContentDesc;
  305. private int mDrawerClosedContentDesc;
  306. /**
  307. * The position of the drawer.
  308. */
  309. private Position mPosition;
  310. private Position mResolvedPosition;
  311. private final Rect mIndicatorClipRect = new Rect();
  312. protected boolean mIsStatic;
  313. protected final Rect mDropShadowRect = new Rect();
  314. /**
  315. * Current offset.
  316. */
  317. protected float mOffsetPixels;
  318. /**
  319. * Whether an overlay should be drawn as the drawer is opened and closed.
  320. */
  321. protected boolean mDrawOverlay;
  322. /**
  323. * Attaches the MenuDrawer to the Activity.
  324. *
  325. * @param activity The activity that the MenuDrawer will be attached to.
  326. * @return The created MenuDrawer instance.
  327. */
  328. public static MenuDrawer attach(Activity activity) {
  329. return attach(activity, Type.BEHIND);
  330. }
  331. /**
  332. * Attaches the MenuDrawer to the Activity.
  333. *
  334. * @param activity The activity the menu drawer will be attached to.
  335. * @param type The {@link Type} of the drawer.
  336. * @return The created MenuDrawer instance.
  337. */
  338. public static MenuDrawer attach(Activity activity, Type type) {
  339. return attach(activity, type, Position.START);
  340. }
  341. /**
  342. * Attaches the MenuDrawer to the Activity.
  343. *
  344. * @param activity The activity the menu drawer will be attached to.
  345. * @param position Where to position the menu.
  346. * @return The created MenuDrawer instance.
  347. */
  348. public static MenuDrawer attach(Activity activity, Position position) {
  349. return attach(activity, Type.BEHIND, position);
  350. }
  351. /**
  352. * Attaches the MenuDrawer to the Activity.
  353. *
  354. * @param activity The activity the menu drawer will be attached to.
  355. * @param type The {@link Type} of the drawer.
  356. * @param position Where to position the menu.
  357. * @return The created MenuDrawer instance.
  358. */
  359. public static MenuDrawer attach(Activity activity, Type type, Position position) {
  360. return attach(activity, type, position, MENU_DRAG_CONTENT);
  361. }
  362. /**
  363. * Attaches the MenuDrawer to the Activity.
  364. *
  365. * @param activity The activity the menu drawer will be attached to.
  366. * @param type The {@link Type} of the drawer.
  367. * @param position Where to position the menu.
  368. * @param dragMode The drag mode of the drawer. Can be either {@link MenuDrawer#MENU_DRAG_CONTENT}
  369. * or {@link MenuDrawer#MENU_DRAG_WINDOW}.
  370. * @return The created MenuDrawer instance.
  371. */
  372. public static MenuDrawer attach(Activity activity, Type type, Position position, int dragMode) {
  373. MenuDrawer menuDrawer = createMenuDrawer(activity, dragMode, position, type);
  374. menuDrawer.setId(R.id.md__drawer);
  375. switch (dragMode) {
  376. case MenuDrawer.MENU_DRAG_CONTENT:
  377. attachToContent(activity, menuDrawer);
  378. break;
  379. case MenuDrawer.MENU_DRAG_WINDOW:
  380. attachToDecor(activity, menuDrawer);
  381. break;
  382. default:
  383. throw new RuntimeException("Unknown menu mode: " + dragMode);
  384. }
  385. return menuDrawer;
  386. }
  387. /**
  388. * Constructs the appropriate MenuDrawer based on the position.
  389. */
  390. private static MenuDrawer createMenuDrawer(Activity activity, int dragMode, Position position, Type type) {
  391. MenuDrawer drawer;
  392. if (type == Type.STATIC) {
  393. drawer = new StaticDrawer(activity);
  394. } else if (type == Type.OVERLAY) {
  395. drawer = new OverlayDrawer(activity, dragMode);
  396. if (position == Position.LEFT || position == Position.START) {
  397. drawer.setupUpIndicator(activity);
  398. }
  399. } else {
  400. drawer = new SlidingDrawer(activity, dragMode);
  401. if (position == Position.LEFT || position == Position.START) {
  402. drawer.setupUpIndicator(activity);
  403. }
  404. }
  405. drawer.mDragMode = dragMode;
  406. drawer.setPosition(position);
  407. return drawer;
  408. }
  409. /**
  410. * Attaches the menu drawer to the content view.
  411. */
  412. private static void attachToContent(Activity activity, MenuDrawer menuDrawer) {
  413. /**
  414. * Do not call mActivity#setContentView.
  415. * E.g. if using with a ListActivity, Activity#setContentView is overridden and dispatched to
  416. * MenuDrawer#setContentView, which then again would call Activity#setContentView.
  417. */
  418. ViewGroup content = (ViewGroup) activity.findViewById(android.R.id.content);
  419. content.removeAllViews();
  420. content.addView(menuDrawer, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
  421. }
  422. /**
  423. * Attaches the menu drawer to the window.
  424. */
  425. private static void attachToDecor(Activity activity, MenuDrawer menuDrawer) {
  426. ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
  427. ViewGroup decorChild = (ViewGroup) decorView.getChildAt(0);
  428. decorView.removeAllViews();
  429. decorView.addView(menuDrawer, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
  430. menuDrawer.mContentContainer.addView(decorChild, decorChild.getLayoutParams());
  431. }
  432. MenuDrawer(Activity activity, int dragMode) {
  433. this(activity);
  434. mActivity = activity;
  435. mDragMode = dragMode;
  436. }
  437. public MenuDrawer(Context context) {
  438. this(context, null);
  439. }
  440. public MenuDrawer(Context context, AttributeSet attrs) {
  441. this(context, attrs, R.attr.menuDrawerStyle);
  442. }
  443. public MenuDrawer(Context context, AttributeSet attrs, int defStyle) {
  444. super(context, attrs, defStyle);
  445. initDrawer(context, attrs, defStyle);
  446. }
  447. protected void initDrawer(Context context, AttributeSet attrs, int defStyle) {
  448. setWillNotDraw(false);
  449. setFocusable(false);
  450. TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MenuDrawer, R.attr.menuDrawerStyle,
  451. R.style.Widget_MenuDrawer);
  452. final Drawable contentBackground = a.getDrawable(R.styleable.MenuDrawer_mdContentBackground);
  453. final Drawable menuBackground = a.getDrawable(R.styleable.MenuDrawer_mdMenuBackground);
  454. mMenuSize = a.getDimensionPixelSize(R.styleable.MenuDrawer_mdMenuSize, dpToPx(240));
  455. final int indicatorResId = a.getResourceId(R.styleable.MenuDrawer_mdActiveIndicator, 0);
  456. if (indicatorResId != 0) {
  457. mActiveIndicator = BitmapFactory.decodeResource(getResources(), indicatorResId);
  458. }
  459. mDropShadowEnabled = a.getBoolean(R.styleable.MenuDrawer_mdDropShadowEnabled, true);
  460. mDropShadowDrawable = a.getDrawable(R.styleable.MenuDrawer_mdDropShadow);
  461. if (mDropShadowDrawable == null) {
  462. mDropShadowColor = a.getColor(R.styleable.MenuDrawer_mdDropShadowColor, 0xFF000000);
  463. } else {
  464. mCustomDropShadow = true;
  465. }
  466. mDropShadowSize = a.getDimensionPixelSize(R.styleable.MenuDrawer_mdDropShadowSize,
  467. dpToPx(DEFAULT_DROP_SHADOW_DP));
  468. mTouchBezelSize = a.getDimensionPixelSize(R.styleable.MenuDrawer_mdTouchBezelSize,
  469. dpToPx(DEFAULT_DRAG_BEZEL_DP));
  470. mAllowIndicatorAnimation = a.getBoolean(R.styleable.MenuDrawer_mdAllowIndicatorAnimation, false);
  471. mMaxAnimationDuration = a.getInt(R.styleable.MenuDrawer_mdMaxAnimationDuration, DEFAULT_ANIMATION_DURATION);
  472. final int slideDrawableResId = a.getResourceId(R.styleable.MenuDrawer_mdSlideDrawable, -1);
  473. if (slideDrawableResId != -1) {
  474. setSlideDrawable(slideDrawableResId);
  475. }
  476. mDrawerOpenContentDesc = a.getResourceId(R.styleable.MenuDrawer_mdDrawerOpenUpContentDescription, 0);
  477. mDrawerClosedContentDesc = a.getResourceId(R.styleable.MenuDrawer_mdDrawerClosedUpContentDescription, 0);
  478. mDrawOverlay = a.getBoolean(R.styleable.MenuDrawer_mdDrawOverlay, true);
  479. final int position = a.getInt(R.styleable.MenuDrawer_mdPosition, 0);
  480. setPosition(Position.fromValue(position));
  481. a.recycle();
  482. mMenuContainer = new NoClickThroughFrameLayout(context);
  483. mMenuContainer.setId(R.id.md__menu);
  484. mMenuContainer.setBackgroundDrawable(menuBackground);
  485. mContentContainer = new NoClickThroughFrameLayout(context);
  486. mContentContainer.setId(R.id.md__content);
  487. mContentContainer.setBackgroundDrawable(contentBackground);
  488. mMenuOverlay = new ColorDrawable(0xFF000000);
  489. mIndicatorScroller = new FloatScroller(SMOOTH_INTERPOLATOR);
  490. }
  491. @Override
  492. protected void onFinishInflate() {
  493. super.onFinishInflate();
  494. View menu = findViewById(R.id.mdMenu);
  495. if (menu != null) {
  496. removeView(menu);
  497. setMenuView(menu);
  498. }
  499. View content = findViewById(R.id.mdContent);
  500. if (content != null) {
  501. removeView(content);
  502. setContentView(content);
  503. }
  504. if (getChildCount() > 2) {
  505. throw new IllegalStateException(
  506. "Menu and content view added in xml must have id's @id/mdMenu and @id/mdContent");
  507. }
  508. }
  509. protected int dpToPx(int dp) {
  510. return (int) (getResources().getDisplayMetrics().density * dp + 0.5f);
  511. }
  512. protected boolean isViewDescendant(View v) {
  513. ViewParent parent = v.getParent();
  514. while (parent != null) {
  515. if (parent == this) {
  516. return true;
  517. }
  518. parent = parent.getParent();
  519. }
  520. return false;
  521. }
  522. @Override
  523. protected void onAttachedToWindow() {
  524. super.onAttachedToWindow();
  525. getViewTreeObserver().addOnScrollChangedListener(mScrollListener);
  526. }
  527. @Override
  528. protected void onDetachedFromWindow() {
  529. getViewTreeObserver().removeOnScrollChangedListener(mScrollListener);
  530. super.onDetachedFromWindow();
  531. }
  532. private boolean shouldDrawIndicator() {
  533. return mActiveView != null && mActiveIndicator != null && isViewDescendant(mActiveView);
  534. }
  535. @Override
  536. protected void dispatchDraw(Canvas canvas) {
  537. super.dispatchDraw(canvas);
  538. final int offsetPixels = (int) mOffsetPixels;
  539. if (mDrawOverlay && offsetPixels != 0) {
  540. drawOverlay(canvas);
  541. }
  542. if (mDropShadowEnabled && (offsetPixels != 0 || mIsStatic)) {
  543. drawDropShadow(canvas);
  544. }
  545. if (shouldDrawIndicator() && (offsetPixels != 0 || mIsStatic)) {
  546. drawIndicator(canvas);
  547. }
  548. }
  549. protected abstract void drawOverlay(Canvas canvas);
  550. private void drawDropShadow(Canvas canvas) {
  551. // Can't pass the position to the constructor, so wait with loading the drawable until the drop shadow is
  552. // actually drawn.
  553. if (mDropShadowDrawable == null) {
  554. setDropShadowColor(mDropShadowColor);
  555. }
  556. updateDropShadowRect();
  557. mDropShadowDrawable.setBounds(mDropShadowRect);
  558. mDropShadowDrawable.draw(canvas);
  559. }
  560. protected void updateDropShadowRect() {
  561. // This updates the rect for the static and sliding drawer. The overlay drawer has its own implementation.
  562. switch (getPosition()) {
  563. case LEFT:
  564. mDropShadowRect.top = 0;
  565. mDropShadowRect.bottom = getHeight();
  566. mDropShadowRect.right = ViewHelper.getLeft(mContentContainer);
  567. mDropShadowRect.left = mDropShadowRect.right - mDropShadowSize;
  568. break;
  569. case TOP:
  570. mDropShadowRect.left = 0;
  571. mDropShadowRect.right = getWidth();
  572. mDropShadowRect.bottom = ViewHelper.getTop(mContentContainer);
  573. mDropShadowRect.top = mDropShadowRect.bottom - mDropShadowSize;
  574. break;
  575. case RIGHT:
  576. mDropShadowRect.top = 0;
  577. mDropShadowRect.bottom = getHeight();
  578. mDropShadowRect.left = ViewHelper.getRight(mContentContainer);
  579. mDropShadowRect.right = mDropShadowRect.left + mDropShadowSize;
  580. break;
  581. case BOTTOM:
  582. mDropShadowRect.left = 0;
  583. mDropShadowRect.right = getWidth();
  584. mDropShadowRect.top = ViewHelper.getBottom(mContentContainer);
  585. mDropShadowRect.bottom = mDropShadowRect.top + mDropShadowSize;
  586. break;
  587. }
  588. }
  589. private void drawIndicator(Canvas canvas) {
  590. Integer position = (Integer) mActiveView.getTag(R.id.mdActiveViewPosition);
  591. final int pos = position == null ? 0 : position;
  592. if (pos == mActivePosition) {
  593. updateIndicatorClipRect();
  594. canvas.save();
  595. canvas.clipRect(mIndicatorClipRect);
  596. int drawLeft = 0;
  597. int drawTop = 0;
  598. switch (getPosition()) {
  599. case LEFT:
  600. case TOP:
  601. drawLeft = mIndicatorClipRect.left;
  602. drawTop = mIndicatorClipRect.top;
  603. break;
  604. case RIGHT:
  605. drawLeft = mIndicatorClipRect.right - mActiveIndicator.getWidth();
  606. drawTop = mIndicatorClipRect.top;
  607. break;
  608. case BOTTOM:
  609. drawLeft = mIndicatorClipRect.left;
  610. drawTop = mIndicatorClipRect.bottom - mActiveIndicator.getHeight();
  611. }
  612. canvas.drawBitmap(mActiveIndicator, drawLeft, drawTop, null);
  613. canvas.restore();
  614. }
  615. }
  616. /**
  617. * Update the {@link Rect} where the indicator is drawn.
  618. */
  619. protected void updateIndicatorClipRect() {
  620. mActiveView.getDrawingRect(mActiveRect);
  621. offsetDescendantRectToMyCoords(mActiveView, mActiveRect);
  622. final float openRatio = mIsStatic ? 1.0f : Math.abs(mOffsetPixels) / mMenuSize;
  623. final float interpolatedRatio = 1.f - INDICATOR_INTERPOLATOR.getInterpolation((1.f - openRatio));
  624. final int indicatorWidth = mActiveIndicator.getWidth();
  625. final int indicatorHeight = mActiveIndicator.getHeight();
  626. final int interpolatedWidth = (int) (indicatorWidth * interpolatedRatio);
  627. final int interpolatedHeight = (int) (indicatorHeight * interpolatedRatio);
  628. final int startPos = mIndicatorStartPos;
  629. int left = 0;
  630. int top = 0;
  631. int right = 0;
  632. int bottom = 0;
  633. switch (getPosition()) {
  634. case LEFT:
  635. case RIGHT:
  636. final int finalTop = mActiveRect.top + ((mActiveRect.height() - indicatorHeight) / 2);
  637. if (mIndicatorAnimating) {
  638. top = (int) (startPos + ((finalTop - startPos) * mIndicatorOffset));
  639. } else {
  640. top = finalTop;
  641. }
  642. bottom = top + indicatorHeight;
  643. break;
  644. case TOP:
  645. case BOTTOM:
  646. final int finalLeft = mActiveRect.left + ((mActiveRect.width() - indicatorWidth) / 2);
  647. if (mIndicatorAnimating) {
  648. left = (int) (startPos + ((finalLeft - startPos) * mIndicatorOffset));
  649. } else {
  650. left = finalLeft;
  651. }
  652. right = left + indicatorWidth;
  653. break;
  654. }
  655. switch (getPosition()) {
  656. case LEFT: {
  657. right = ViewHelper.getLeft(mContentContainer);
  658. left = right - interpolatedWidth;
  659. break;
  660. }
  661. case TOP: {
  662. bottom = ViewHelper.getTop(mContentContainer);
  663. top = bottom - interpolatedHeight;
  664. break;
  665. }
  666. case RIGHT: {
  667. left = ViewHelper.getRight(mContentContainer);
  668. right = left + interpolatedWidth;
  669. break;
  670. }
  671. case BOTTOM: {
  672. top = ViewHelper.getBottom(mContentContainer);
  673. bottom = top + interpolatedHeight;
  674. break;
  675. }
  676. }
  677. mIndicatorClipRect.left = left;
  678. mIndicatorClipRect.top = top;
  679. mIndicatorClipRect.right = right;
  680. mIndicatorClipRect.bottom = bottom;
  681. }
  682. private void setPosition(Position position) {
  683. mPosition = position;
  684. mResolvedPosition = getPosition();
  685. }
  686. protected Position getPosition() {
  687. final int layoutDirection = ViewHelper.getLayoutDirection(this);
  688. switch (mPosition) {
  689. case START:
  690. if (layoutDirection == LAYOUT_DIRECTION_RTL) {
  691. return Position.RIGHT;
  692. } else {
  693. return Position.LEFT;
  694. }
  695. case END:
  696. if (layoutDirection == LAYOUT_DIRECTION_RTL) {
  697. return Position.LEFT;
  698. } else {
  699. return Position.RIGHT;
  700. }
  701. }
  702. return mPosition;
  703. }
  704. @Override
  705. public void onRtlPropertiesChanged(int layoutDirection) {
  706. super.onRtlPropertiesChanged(layoutDirection);
  707. if (!mCustomDropShadow) setDropShadowColor(mDropShadowColor);
  708. if (getPosition() != mResolvedPosition) {
  709. mResolvedPosition = getPosition();
  710. setOffsetPixels(mOffsetPixels * -1);
  711. }
  712. if (mSlideDrawable != null) mSlideDrawable.setIsRtl(layoutDirection == LAYOUT_DIRECTION_RTL);
  713. requestLayout();
  714. invalidate();
  715. }
  716. /**
  717. * Sets the number of pixels the content should be offset.
  718. *
  719. * @param offsetPixels The number of pixels to offset the content by.
  720. */
  721. protected void setOffsetPixels(float offsetPixels) {
  722. final int oldOffset = (int) mOffsetPixels;
  723. final int newOffset = (int) offsetPixels;
  724. mOffsetPixels = offsetPixels;
  725. if (mSlideDrawable != null) {
  726. final float offset = Math.abs(mOffsetPixels) / mMenuSize;
  727. mSlideDrawable.setOffset(offset);
  728. updateUpContentDescription();
  729. }
  730. if (newOffset != oldOffset) {
  731. onOffsetPixelsChanged(newOffset);
  732. mMenuVisible = newOffset != 0;
  733. // Notify any attached listeners of the current open ratio
  734. final float openRatio = ((float) Math.abs(newOffset)) / mMenuSize;
  735. dispatchOnDrawerSlide(openRatio, newOffset);
  736. }
  737. }
  738. /**
  739. * Called when the number of pixels the content should be offset by has changed.
  740. *
  741. * @param offsetPixels The number of pixels to offset the content by.
  742. */
  743. protected abstract void onOffsetPixelsChanged(int offsetPixels);
  744. /**
  745. * Toggles the menu open and close with animation.
  746. */
  747. public void toggleMenu() {
  748. toggleMenu(true);
  749. }
  750. /**
  751. * Toggles the menu open and close.
  752. *
  753. * @param animate Whether open/close should be animated.
  754. */
  755. public abstract void toggleMenu(boolean animate);
  756. /**
  757. * Animates the menu open.
  758. */
  759. public void openMenu() {
  760. openMenu(true);
  761. }
  762. /**
  763. * Opens the menu.
  764. *
  765. * @param animate Whether open/close should be animated.
  766. */
  767. public abstract void openMenu(boolean animate);
  768. /**
  769. * Animates the menu closed.
  770. */
  771. public void closeMenu() {
  772. closeMenu(true);
  773. }
  774. /**
  775. * Closes the menu.
  776. *
  777. * @param animate Whether open/close should be animated.
  778. */
  779. public abstract void closeMenu(boolean animate);
  780. /**
  781. * Indicates whether the menu is currently visible.
  782. *
  783. * @return True if the menu is open, false otherwise.
  784. */
  785. public abstract boolean isMenuVisible();
  786. /**
  787. * Set the size of the menu drawer when open.
  788. *
  789. * @param size The size of the menu.
  790. */
  791. public abstract void setMenuSize(int size);
  792. /**
  793. * Returns the size of the menu.
  794. *
  795. * @return The size of the menu.
  796. */
  797. public int getMenuSize() {
  798. return mMenuSize;
  799. }
  800. /**
  801. * Set the active view.
  802. * If the mdActiveIndicator attribute is set, this View will have the indicator drawn next to it.
  803. *
  804. * @param v The active view.
  805. */
  806. public void setActiveView(View v) {
  807. setActiveView(v, 0);
  808. }
  809. /**
  810. * Set the active view.
  811. * If the mdActiveIndicator attribute is set, this View will have the indicator drawn next to it.
  812. *
  813. * @param v The active view.
  814. * @param position Optional position, usually used with ListView. v.setTag(R.id.mdActiveViewPosition, position)
  815. * must be called first.
  816. */
  817. public void setActiveView(View v, int position) {
  818. final View oldView = mActiveView;
  819. mActiveView = v;
  820. mActivePosition = position;
  821. if (mAllowIndicatorAnimation && oldView != null) {
  822. startAnimatingIndicator();
  823. }
  824. invalidate();
  825. }
  826. /**
  827. * Sets whether the indicator should be animated between active views.
  828. *
  829. * @param animate Whether the indicator should be animated between active views.
  830. */
  831. public void setAllowIndicatorAnimation(boolean animate) {
  832. if (animate != mAllowIndicatorAnimation) {
  833. mAllowIndicatorAnimation = animate;
  834. completeAnimatingIndicator();
  835. }
  836. }
  837. /**
  838. * Indicates whether the indicator should be animated between active views.
  839. *
  840. * @return Whether the indicator should be animated between active views.
  841. */
  842. public boolean getAllowIndicatorAnimation() {
  843. return mAllowIndicatorAnimation;
  844. }
  845. /**
  846. * Scroll listener that checks whether the active view has moved before the drawer is invalidated.
  847. */
  848. private ViewTreeObserver.OnScrollChangedListener mScrollListener = new ViewTreeObserver.OnScrollChangedListener() {
  849. @Override
  850. public void onScrollChanged() {
  851. if (mActiveView != null && isViewDescendant(mActiveView)) {
  852. mActiveView.getDrawingRect(mTempRect);
  853. offsetDescendantRectToMyCoords(mActiveView, mTempRect);
  854. if (mTempRect.left != mActiveRect.left || mTempRect.top != mActiveRect.top
  855. || mTempRect.right != mActiveRect.right || mTempRect.bottom != mActiveRect.bottom) {
  856. invalidate();
  857. }
  858. }
  859. }
  860. };
  861. /**
  862. * Starts animating the indicator to a new position.
  863. */
  864. private void startAnimatingIndicator() {
  865. mIndicatorStartPos = getIndicatorStartPos();
  866. mIndicatorAnimating = true;
  867. mIndicatorScroller.startScroll(0.0f, 1.0f, INDICATOR_ANIM_DURATION);
  868. animateIndicatorInvalidate();
  869. }
  870. /**
  871. * Returns the start position of the indicator.
  872. *
  873. * @return The start position of the indicator.
  874. */
  875. private int getIndicatorStartPos() {
  876. switch (getPosition()) {
  877. case TOP:
  878. return mIndicatorClipRect.left;
  879. case RIGHT:
  880. return mIndicatorClipRect.top;
  881. case BOTTOM:
  882. return mIndicatorClipRect.left;
  883. default:
  884. return mIndicatorClipRect.top;
  885. }
  886. }
  887. /**
  888. * Compute the touch area based on the touch mode.
  889. */
  890. protected void updateTouchAreaSize() {
  891. if (mTouchMode == TOUCH_MODE_BEZEL) {
  892. mTouchSize = mTouchBezelSize;
  893. } else if (mTouchMode == TOUCH_MODE_FULLSCREEN) {
  894. mTouchSize = getMeasuredWidth();
  895. } else {
  896. mTouchSize = 0;
  897. }
  898. }
  899. /**
  900. * Callback when each frame in the indicator animation should be drawn.
  901. */
  902. private void animateIndicatorInvalidate() {
  903. if (mIndicatorScroller.computeScrollOffset()) {
  904. mIndicatorOffset = mIndicatorScroller.getCurr();
  905. invalidate();
  906. if (!mIndicatorScroller.isFinished()) {
  907. postOnAnimation(mIndicatorRunnable);
  908. return;
  909. }
  910. }
  911. completeAnimatingIndicator();
  912. }
  913. /**
  914. * Called when the indicator animation has completed.
  915. */
  916. private void completeAnimatingIndicator() {
  917. mIndicatorOffset = 1.0f;
  918. mIndicatorAnimating = false;
  919. invalidate();
  920. }
  921. /**
  922. * Enables or disables offsetting the menu when dragging the drawer.
  923. *
  924. * @param offsetMenu True to offset the menu, false otherwise.
  925. */
  926. public abstract void setOffsetMenuEnabled(boolean offsetMenu);
  927. /**
  928. * Indicates whether the menu is being offset when dragging the drawer.
  929. *
  930. * @return True if the menu is being offset, false otherwise.
  931. */
  932. public abstract boolean getOffsetMenuEnabled();
  933. /**
  934. * Get the current state of the drawer.
  935. *
  936. * @return The state of the drawer.
  937. */
  938. public int getDrawerState() {
  939. return mDrawerState;
  940. }
  941. /**
  942. * Register a callback to be invoked when the drawer state changes.
  943. *
  944. * @param listener The callback that will run.
  945. */
  946. public void setOnDrawerStateChangeListener(OnDrawerStateChangeListener listener) {
  947. mOnDrawerStateChangeListener = listener;
  948. }
  949. /**
  950. * Register a callback that will be invoked when the drawer is about to intercept touch events.
  951. *
  952. * @param listener The callback that will be invoked.
  953. */
  954. public void setOnInterceptMoveEventListener(OnInterceptMoveEventListener listener) {
  955. mOnInterceptMoveEventListener = listener;
  956. }
  957. /**
  958. * Defines whether the drop shadow is enabled.
  959. *
  960. * @param enabled Whether the drop shadow is enabled.
  961. */
  962. public void setDropShadowEnabled(boolean enabled) {
  963. mDropShadowEnabled = enabled;
  964. invalidate();
  965. }
  966. protected GradientDrawable.Orientation getDropShadowOrientation() {
  967. // Gets the orientation for the static and sliding drawer. The overlay drawer provides its own implementation.
  968. switch (getPosition()) {
  969. case TOP:
  970. return GradientDrawable.Orientation.BOTTOM_TOP;
  971. case RIGHT:
  972. return GradientDrawable.Orientation.LEFT_RIGHT;
  973. case BOTTOM:
  974. return GradientDrawable.Orientation.TOP_BOTTOM;
  975. default:
  976. return GradientDrawable.Orientation.RIGHT_LEFT;
  977. }
  978. }
  979. /**
  980. * Sets the color of the drop shadow.
  981. *
  982. * @param color The color of the drop shadow.
  983. */
  984. public void setDropShadowColor(int color) {
  985. GradientDrawable.Orientation orientation = getDropShadowOrientation();
  986. final int endColor = color & 0x00FFFFFF;
  987. mDropShadowDrawable = new GradientDrawable(orientation,
  988. new int[] {
  989. color,
  990. endColor,
  991. });
  992. invalidate();
  993. }
  994. /**
  995. * Sets the drawable of the drop shadow.
  996. *
  997. * @param drawable The drawable of the drop shadow.
  998. */
  999. public void setDropShadow(Drawable drawable) {
  1000. mDropShadowDrawable = drawable;
  1001. mCustomDropShadow = drawable != null;
  1002. invalidate();
  1003. }
  1004. /**
  1005. * Sets the drawable of the drop shadow.
  1006. *
  1007. * @param resId The resource identifier of the the drawable.
  1008. */
  1009. public void setDropShadow(int resId) {
  1010. setDropShadow(getResources().getDrawable(resId));
  1011. }
  1012. /**
  1013. * Returns the drawable of the drop shadow.
  1014. */
  1015. public Drawable getDropShadow() {
  1016. return mDropShadowDrawable;
  1017. }
  1018. /**
  1019. * Sets the size of the drop shadow.
  1020. *
  1021. * @param size The size of the drop shadow in px.
  1022. */
  1023. public void setDropShadowSize(int size) {
  1024. mDropShadowSize = size;
  1025. invalidate();
  1026. }
  1027. /**
  1028. * Animates the drawer slightly open until the user opens the drawer.
  1029. */
  1030. public abstract void peekDrawer();
  1031. /**
  1032. * Animates the drawer slightly open. If delay is larger than 0, this happens until the user opens the drawer.
  1033. *
  1034. * @param delay The delay (in milliseconds) between each run of the animation. If 0, this animation is only run
  1035. * once.
  1036. */
  1037. public abstract void peekDrawer(long delay);
  1038. /**
  1039. * Animates the drawer slightly open. If delay is larger than 0, this happens until the user opens the drawer.
  1040. *
  1041. * @param startDelay The delay (in milliseconds) until the animation is first run.
  1042. * @param delay The delay (in milliseconds) between each run of the animation. If 0, this animation is only run
  1043. * once.
  1044. */
  1045. public abstract void peekDrawer(long startDelay, long delay);
  1046. /**
  1047. * Enables or disables the user of {@link View#LAYER_TYPE_HARDWARE} when animations views.
  1048. *
  1049. * @param enabled Whether hardware layers are enabled.
  1050. */
  1051. public abstract void setHardwareLayerEnabled(boolean enabled);
  1052. /**
  1053. * Sets the maximum duration of open/close animations.
  1054. *
  1055. * @param duration The maximum duration in milliseconds.
  1056. */
  1057. public void setMaxAnimationDuration(int duration) {
  1058. mMaxAnimationDuration = duration;
  1059. }
  1060. /**
  1061. * Sets whether an overlay should be drawn when sliding the drawer.
  1062. *
  1063. * @param drawOverlay Whether an overlay should be drawn when sliding the drawer.
  1064. */
  1065. public void setDrawOverlay(boolean drawOverlay) {
  1066. mDrawOverlay = drawOverlay;
  1067. }
  1068. /**
  1069. * Gets whether an overlay is drawn when sliding the drawer.
  1070. *
  1071. * @return Whether an overlay is drawn when sliding the drawer.
  1072. */
  1073. public boolean getDrawOverlay() {
  1074. return mDrawOverlay;
  1075. }
  1076. protected void updateUpContentDescription() {
  1077. final int upContentDesc = isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc;
  1078. if (mDrawerIndicatorEnabled && mActionBarHelper != null && upContentDesc != mCurrentUpContentDesc) {
  1079. mCurrentUpContentDesc = upContentDesc;
  1080. mActionBarHelper.setActionBarDescription(upContentDesc);
  1081. }
  1082. }
  1083. /**
  1084. * Sets the drawable used as the drawer indicator.
  1085. *
  1086. * @param drawable The drawable used as the drawer indicator.
  1087. */
  1088. public void setSlideDrawable(int drawableRes) {
  1089. setSlideDrawable(getResources().getDrawable(drawableRes));
  1090. }
  1091. /**
  1092. * Sets the drawable used as the drawer indicator.
  1093. *
  1094. * @param drawable The drawable used as the drawer indicator.
  1095. */
  1096. public void setSlideDrawable(Drawable drawable) {
  1097. mSlideDrawable = new SlideDrawable(drawable);
  1098. mSlideDrawable.setIsRtl(ViewHelper.getLayoutDirection(this) == LAYOUT_DIRECTION_RTL);
  1099. if (mActionBarHelper != null) {
  1100. mActionBarHelper.setDisplayShowHomeAsUpEnabled(true);
  1101. if (mDrawerIndicatorEnabled) {
  1102. mActionBarHelper.setActionBarUpIndicator(mSlideDrawable,
  1103. isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc);
  1104. }
  1105. }
  1106. }
  1107. /**
  1108. * Sets up the drawer indicator. It cna then be shown with {@link #setDrawerIndicatorEnabled(boolean)}.
  1109. *
  1110. * @param activity The activity the drawer is attached to.
  1111. */
  1112. public void setupUpIndicator(Activity activity) {
  1113. if (mActionBarHelper == null) {
  1114. mActionBarHelper = new ActionBarHelper(activity);
  1115. mThemeUpIndicator = mActionBarHelper.getThemeUpIndicator();
  1116. if (mDrawerIndicatorEnabled) {
  1117. mActionBarHelper.setActionBarUpIndicator(mSlideDrawable,
  1118. isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc);
  1119. }
  1120. }
  1121. }
  1122. /**
  1123. * Sets whether the drawer indicator should be enabled. {@link #setupUpIndicator(Activity)} must be
  1124. * called first.
  1125. *
  1126. * @param enabled Whether the drawer indicator should enabled.
  1127. */
  1128. public void setDrawerIndicatorEnabled(boolean enabled) {
  1129. if (mActionBarHelper == null) {
  1130. throw new IllegalStateException("setupUpIndicator(Activity) has not been called");
  1131. }
  1132. mDrawerIndicatorEnabled = enabled;
  1133. if (enabled) {
  1134. mActionBarHelper.setActionBarUpIndicator(mSlideDrawable,
  1135. isMenuVisible() ? mDrawerOpenContentDesc : mDrawerClosedContentDesc);
  1136. } else {
  1137. mActionBarHelper.setActionBarUpIndicator(mThemeUpIndicator, 0);
  1138. }
  1139. }
  1140. /**
  1141. * Indicates whether the drawer indicator is currently enabled.
  1142. *
  1143. * @return Whether the drawer indicator is enabled.
  1144. */
  1145. public boolean isDrawerIndicatorEnabled() {
  1146. return mDrawerIndicatorEnabled;
  1147. }
  1148. /**
  1149. * Returns the ViewGroup used as a parent for the menu view.
  1150. *
  1151. * @return The menu view's parent.
  1152. */
  1153. public ViewGroup getMenuContainer() {
  1154. return mMenuContainer;
  1155. }
  1156. /**
  1157. * Returns the ViewGroup used as a parent for the content view.
  1158. *
  1159. * @return The content view's parent.
  1160. */
  1161. public ViewGroup getContentContainer() {
  1162. if (mDragMode == MENU_DRAG_CONTENT) {
  1163. return mContentContainer;
  1164. } else {
  1165. return (ViewGroup) findViewById(android.R.id.content);
  1166. }
  1167. }
  1168. /**
  1169. * Set the menu view from a layout resource.
  1170. *
  1171. * @param layoutResId Resource ID to be inflated.
  1172. */
  1173. public void setMenuView(int layoutResId) {
  1174. mMenuContainer.removeAllViews();
  1175. mMenuView = LayoutInflater.from(getContext()).inflate(layoutResId, mMenuContainer, false);
  1176. mMenuContainer.addView(mMenuView);
  1177. }
  1178. /**
  1179. * Set the menu view to an explicit view.
  1180. *
  1181. * @param view The menu view.
  1182. */
  1183. public void setMenuView(View view) {
  1184. setMenuView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
  1185. }
  1186. /**
  1187. * Set the menu view to an explicit view.
  1188. *
  1189. * @param view The menu view.
  1190. * @param params Layout parameters for the view.
  1191. */
  1192. public void setMenuView(View view, LayoutParams params) {
  1193. mMenuView = view;
  1194. mMenuContainer.removeAllViews();
  1195. mMenuContainer.addView(view, params);
  1196. }
  1197. /**
  1198. * Returns the menu view.
  1199. *
  1200. * @return The menu view.
  1201. */
  1202. public View getMenuView() {
  1203. return mMenuView;
  1204. }
  1205. /**
  1206. * Set the content from a layout resource.
  1207. *
  1208. * @param layoutResId Resource ID to be inflated.
  1209. */
  1210. public void setContentView(int layoutResId) {
  1211. switch (mDragMode) {
  1212. case MenuDrawer.MENU_DRAG_CONTENT:
  1213. mContentContainer.removeAllViews();
  1214. LayoutInflater.from(getContext()).inflate(layoutResId, mContentContainer, true);
  1215. break;
  1216. case MenuDrawer.MENU_DRAG_WINDOW:
  1217. mActivity.setContentView(layoutResId);
  1218. break;
  1219. }
  1220. }
  1221. /**
  1222. * Set the content to an explicit view.
  1223. *
  1224. * @param view The desired content to display.
  1225. */
  1226. public void setContentView(View view) {
  1227. setContentView(view, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
  1228. }
  1229. /**
  1230. * Set the content to an explicit view.
  1231. *
  1232. * @param view The desired content to display.
  1233. * @param params Layout parameters for the view.
  1234. */
  1235. public void setContentView(View view, LayoutParams params) {
  1236. switch (mDragMode) {
  1237. case MenuDrawer.MENU_DRAG_CONTENT:
  1238. mContentContainer.removeAllViews();
  1239. mContentContainer.addView(view, params);
  1240. break;
  1241. case MenuDrawer.MENU_DRAG_WINDOW:
  1242. mActivity.setContentView(view, params);
  1243. break;
  1244. }
  1245. }
  1246. protected void setDrawerState(int state) {
  1247. if (state != mDrawerState) {
  1248. final int oldState = mDrawerState;
  1249. mDrawerState = state;
  1250. if (mOnDrawerStateChangeListener != null) mOnDrawerStateChangeListener.onDrawerStateChange(oldState, state);
  1251. if (DEBUG) logDrawerState(state);
  1252. }
  1253. }
  1254. protected void logDrawerState(int state) {
  1255. switch (state) {
  1256. case STATE_CLOSED:
  1257. Log.d(TAG, "[DrawerState] STATE_CLOSED");
  1258. break;
  1259. case STATE_CLOSING:
  1260. Log.d(TAG, "[DrawerState] STATE_CLOSING");
  1261. break;
  1262. case STATE_DRAGGING:
  1263. Log.d(TAG, "[DrawerState] STATE_DRAGGING");
  1264. break;
  1265. case STATE_OPENING:
  1266. Log.d(TAG, "[DrawerState] STATE_OPENING");
  1267. break;
  1268. case STATE_OPEN:
  1269. Log.d(TAG, "[DrawerState] STATE_OPEN");
  1270. break;
  1271. default:
  1272. Log.d(TAG, "[DrawerState] Unknown: " + state);
  1273. }
  1274. }
  1275. /**
  1276. * Returns the touch mode.
  1277. */
  1278. public abstract int getTouchMode();
  1279. /**
  1280. * Sets the drawer touch mode. Possible values are {@link #TOUCH_MODE_NONE}, {@link #TOUCH_MODE_BEZEL} or
  1281. * {@link #TOUCH_MODE_FULLSCREEN}.
  1282. *
  1283. * @param mode The touch mode.
  1284. */
  1285. public abstract void setTouchMode(int mode);
  1286. /**
  1287. * Sets the size of the touch bezel.
  1288. *
  1289. * @param size The touch bezel size in px.
  1290. */
  1291. public abstract void setTouchBezelSize(int size);
  1292. /**
  1293. * Returns the size of the touch bezel in px.
  1294. */
  1295. public abstract int getTouchBezelSize();
  1296. @Override
  1297. public void postOnAnimation(Runnable action) {
  1298. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  1299. super.postOnAnimation(action);
  1300. } else {
  1301. postDelayed(action, ANIMATION_DELAY);
  1302. }
  1303. }
  1304. @Override
  1305. protected boolean fitSystemWindows(Rect insets) {
  1306. if (mDragMode == MENU_DRAG_WINDOW && mPosition != Position.BOTTOM) {
  1307. mMenuContainer.setPadding(0, insets.top, 0, 0);
  1308. }
  1309. return super.fitSystemWindows(insets);
  1310. }
  1311. protected void dispatchOnDrawerSlide(float openRatio, int offsetPixels) {
  1312. if (mOnDrawerStateChangeListener != null) {
  1313. mOnDrawerStateChangeListener.onDrawerSlide(openRatio, offsetPixels);
  1314. }
  1315. }
  1316. /**
  1317. * Saves the state of the drawer.
  1318. *
  1319. * @return Returns a Parcelable containing the drawer state.
  1320. */
  1321. public final Parcelable saveState() {
  1322. if (mState == null) mState = new Bundle();
  1323. saveState(mState);
  1324. return mState;
  1325. }
  1326. void saveState(Bundle state) {
  1327. // State saving isn't required for subclasses.
  1328. }
  1329. /**
  1330. * Restores the state of the drawer.
  1331. *
  1332. * @param in A parcelable containing the drawer state.
  1333. */
  1334. public void restoreState(Parcelable in) {
  1335. mState = (Bundle) in;
  1336. }
  1337. @Override
  1338. protected Parcelable onSaveInstanceState() {
  1339. Parcelable superState = super.onSaveInstanceState();
  1340. SavedState state = new SavedState(superState);
  1341. if (mState == null) mState = new Bundle();
  1342. saveState(mState);
  1343. state.mState = mState;
  1344. return state;
  1345. }
  1346. @Override
  1347. protected void onRestoreInstanceState(Parcelable state) {
  1348. SavedState savedState = (SavedState) state;
  1349. super.onRestoreInstanceState(savedState.getSuperState());
  1350. restoreState(savedState.mState);
  1351. }
  1352. static class SavedState extends BaseSavedState {
  1353. Bundle mState;
  1354. public SavedState(Parcelable superState) {
  1355. super(superState);
  1356. }
  1357. public SavedState(Parcel in) {
  1358. super(in);
  1359. mState = in.readBundle();
  1360. }
  1361. @Override
  1362. public void writeToParcel(Parcel dest, int flags) {
  1363. super.writeToParcel(dest, flags);
  1364. dest.writeBundle(mState);
  1365. }
  1366. @SuppressWarnings("UnusedDeclaration")
  1367. public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
  1368. @Override
  1369. public SavedState createFromParcel(Parcel in) {
  1370. return new SavedState(in);
  1371. }
  1372. @Override
  1373. public SavedState[] newArray(int size) {
  1374. return new SavedState[size];
  1375. }
  1376. };
  1377. }
  1378. }