MainLayout.java 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package com.ivalentin.gm;
  2. import android.annotation.SuppressLint;
  3. import android.content.Context;
  4. import android.os.Handler;
  5. import android.util.AttributeSet;
  6. import android.view.MotionEvent;
  7. import android.view.View;
  8. import android.view.animation.Interpolator;
  9. import android.widget.LinearLayout;
  10. import android.widget.Scroller;
  11. /**
  12. * Extension of LinearLayout to be used in the app. Contains the slider menu.
  13. *
  14. * @author Iñigo Valentin
  15. *
  16. */
  17. public class MainLayout extends LinearLayout {
  18. //The width of the view
  19. private int mainLayoutWidth;
  20. //The menu view
  21. private View menu;
  22. //The content view
  23. private View content;
  24. //The margin of the menu
  25. private static int menuRightMargin = 25;
  26. /**
  27. * Possible states of the menu
  28. */
  29. private enum MenuState {
  30. HIDING, HIDDEN, SHOWING, SHOWN,
  31. }
  32. private int contentXOffset;
  33. private MenuState currentMenuState = MenuState.HIDDEN;
  34. private Scroller menuScroller = new Scroller(this.getContext(), new EaseInInterpolator());
  35. private Runnable menuRunnable = new MenuRunnable();
  36. private Handler menuHandler = new Handler();
  37. private int prevX = 0;
  38. private boolean isDragging = false;
  39. private int lastDiffX = 0;
  40. /**
  41. * Constructor.
  42. *
  43. * @param context Context of the app.
  44. * @param attrs Atributes.
  45. */
  46. public MainLayout(Context context, AttributeSet attrs) {
  47. super(context, attrs);
  48. }
  49. /**
  50. * Constructor.
  51. *
  52. * @param context Context of the app.
  53. */
  54. public MainLayout(Context context) {
  55. super(context);
  56. }
  57. @Override
  58. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  59. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  60. mainLayoutWidth = MeasureSpec.getSize(widthMeasureSpec);
  61. menuRightMargin = mainLayoutWidth * 25 / 100;
  62. }
  63. @SuppressLint("ClickableViewAccessibility") //I don't need it.
  64. @Override
  65. protected void onAttachedToWindow() {
  66. super.onAttachedToWindow();
  67. menu = this.getChildAt(0);
  68. content = this.getChildAt(1);
  69. content.setOnTouchListener(new OnTouchListener() {
  70. @Override
  71. public boolean onTouch(View v, MotionEvent event) {
  72. return MainLayout.this.onContentTouch(event);
  73. }
  74. });
  75. menu.setVisibility(View.GONE);
  76. }
  77. @Override
  78. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  79. if (changed) {
  80. LayoutParams contentLayoutParams = (LayoutParams) content.getLayoutParams();
  81. contentLayoutParams.height = this.getHeight();
  82. contentLayoutParams.width = this.getWidth();
  83. LayoutParams menuLayoutParams = (LayoutParams) menu.getLayoutParams();
  84. menuLayoutParams.height = this.getHeight();
  85. menuLayoutParams.width = this.getWidth() - menuRightMargin;
  86. }
  87. this.requestDisallowInterceptTouchEvent(true);
  88. menu.layout(left, top, right - menuRightMargin, bottom);
  89. content.layout(left + contentXOffset, top, right + contentXOffset, bottom);
  90. }
  91. /**
  92. * Changes the menu state.
  93. * Closes it if it's open. Opens it if it's closed.
  94. */
  95. public void toggleMenu() {
  96. if (currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
  97. return;
  98. switch (currentMenuState) {
  99. case HIDDEN:
  100. currentMenuState = MenuState.SHOWING;
  101. menu.setVisibility(View.VISIBLE);
  102. menuScroller.startScroll(0, 0, menu.getLayoutParams().width, 0, GM.MENU_SLIDING_DURATION);
  103. break;
  104. case SHOWN:
  105. currentMenuState = MenuState.HIDING;
  106. menuScroller.startScroll(contentXOffset, 0, -contentXOffset, 0, GM.MENU_SLIDING_DURATION);
  107. break;
  108. default:
  109. break;
  110. }
  111. menuHandler.postDelayed(menuRunnable, GM.MENU_QUERY_INTERVAL);
  112. this.invalidate();
  113. }
  114. /**
  115. * Handles the animation of the sliding menu.
  116. *
  117. * @author seavenois
  118. *
  119. * @see Runnable
  120. *
  121. */
  122. protected class MenuRunnable implements Runnable {
  123. @Override
  124. public void run() {
  125. boolean isScrolling = menuScroller.computeScrollOffset();
  126. adjustContentPosition(isScrolling);
  127. }
  128. }
  129. /**
  130. * Moves the content view when the menu is slided.
  131. *
  132. * @param isScrolling Indicates if the menu is being slided.
  133. */
  134. private void adjustContentPosition(boolean isScrolling) {
  135. int scrollerXOffset = menuScroller.getCurrX();
  136. content.offsetLeftAndRight(scrollerXOffset - contentXOffset);
  137. contentXOffset = scrollerXOffset;
  138. this.invalidate();
  139. if (isScrolling)
  140. menuHandler.postDelayed(menuRunnable, GM.MENU_QUERY_INTERVAL);
  141. else
  142. this.onMenuSlidingComplete();
  143. }
  144. /**
  145. * Called when the menu has been slided completely.
  146. * Fixes the position.
  147. */
  148. private void onMenuSlidingComplete() {
  149. switch (currentMenuState) {
  150. case SHOWING:
  151. currentMenuState = MenuState.SHOWN;
  152. break;
  153. case HIDING:
  154. currentMenuState = MenuState.HIDDEN;
  155. menu.setVisibility(View.GONE);
  156. break;
  157. }
  158. }
  159. /**
  160. * Defines the menu animation frame rate.
  161. *
  162. * @author Iñigo Valentin
  163. *
  164. */
  165. protected class EaseInInterpolator implements Interpolator {
  166. @Override
  167. public float getInterpolation(float t) {
  168. return (float) Math.pow(t - 1, 5) + 1;
  169. }
  170. }
  171. /**
  172. * Register touch events.
  173. * Used to show and hide the menu.
  174. *
  175. * @param event The MotionEvent triggering it.
  176. * @return True if the menu is fully opened or closed and a touch event is happening
  177. */
  178. public boolean onContentTouch(MotionEvent event) {
  179. if (currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
  180. return false;
  181. int curX = (int) event.getRawX();
  182. int diffX;
  183. switch (event.getAction()) {
  184. case MotionEvent.ACTION_DOWN:
  185. prevX = curX;
  186. return true;
  187. case MotionEvent.ACTION_MOVE:
  188. //int width = this.getWidth();
  189. //if (event.getX() < (float) width / 4){
  190. if (!isDragging) {
  191. isDragging = true;
  192. menu.setVisibility(View.VISIBLE);
  193. }
  194. diffX = curX - prevX;
  195. if (contentXOffset + diffX <= 0) {
  196. diffX = -contentXOffset;
  197. }
  198. else if (contentXOffset + diffX > mainLayoutWidth - menuRightMargin) {
  199. diffX = mainLayoutWidth - menuRightMargin - contentXOffset;
  200. }
  201. content.offsetLeftAndRight(diffX);
  202. contentXOffset += diffX;
  203. this.invalidate();
  204. prevX = curX;
  205. lastDiffX = diffX;
  206. //}
  207. return true;
  208. case MotionEvent.ACTION_UP:
  209. if (lastDiffX > 0) {
  210. currentMenuState = MenuState.SHOWING;
  211. menuScroller.startScroll(contentXOffset, 0, menu.getLayoutParams().width - contentXOffset, 0, GM.MENU_SLIDING_DURATION);
  212. }
  213. else if (lastDiffX < 0) {
  214. currentMenuState = MenuState.HIDING;
  215. menuScroller.startScroll(contentXOffset, 0, -contentXOffset, 0, GM.MENU_SLIDING_DURATION);
  216. }
  217. menuHandler.postDelayed(menuRunnable, GM.MENU_QUERY_INTERVAL);
  218. this.invalidate();
  219. isDragging = false;
  220. prevX = 0;
  221. lastDiffX = 0;
  222. return true;
  223. default:
  224. break;
  225. }
  226. return false;
  227. }
  228. }