Tuesday, 1 April 2014

Simple Sliding Menu Example in Android

As of now we know that sliding menu is a simple dragging menu from any one side. This post will help you to create a simple sliding menu in Android. 
Here sliding menu is a listview which is in INVISIBLE state. It will comes to VISIBLE state once you click the menu button. And it will go to INVISIBLE state again once you pick the item from listview. To change the state we are using animation, that is why it looks like sliding. 

Complete code: 

MainActivity.java

  1. package com.android.slidingmenuexample;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v4.app.Fragment;  
  5. import android.support.v4.app.FragmentActivity;  
  6. import android.support.v4.app.FragmentManager;  
  7. import android.support.v4.app.FragmentTransaction;  
  8. import android.view.Menu;  
  9. import android.view.View;  
  10. import android.view.View.OnClickListener;  
  11. import android.widget.AdapterView;  
  12. import android.widget.ArrayAdapter;  
  13. import android.widget.Button;  
  14. import android.widget.ListView;  
  15. import android.widget.AdapterView.OnItemClickListener;  
  16. import android.widget.TextView;  
  17.   
  18. public class MainActivity extends FragmentActivity {  
  19.   
  20.  MainLayout mLayout;  
  21.  private ListView lvMenu;  
  22.  private String[] lvMenuItems;  
  23.  Button btMenu;  
  24.  TextView tvTitle;  
  25.   
  26.  @Override  
  27.  protected void onCreate(Bundle savedInstanceState) {  
  28.   super.onCreate(savedInstanceState);  
  29.   mLayout = (MainLayout) this.getLayoutInflater().inflate(  
  30.     R.layout.activity_main, null);  
  31.   setContentView(mLayout);  
  32.   
  33.   lvMenuItems = getResources().getStringArray(R.array.menu_items);  
  34.   lvMenu = (ListView) findViewById(R.id.menu_listview);  
  35.   lvMenu.setAdapter(new ArrayAdapter<String>(this,  
  36.     android.R.layout.simple_list_item_1, lvMenuItems));  
  37.   lvMenu.setOnItemClickListener(new OnItemClickListener() {  
  38.    @Override  
  39.    public void onItemClick(AdapterView<?> parent, View view,  
  40.      int position, long id) {  
  41.     onMenuItemClick(parent, view, position, id);  
  42.    }  
  43.   
  44.   });  
  45.   
  46.   btMenu = (Button) findViewById(R.id.button_menu);  
  47.   btMenu.setOnClickListener(new OnClickListener() {  
  48.    @Override  
  49.    public void onClick(View v) {  
  50.     // Show/hide the menu  
  51.     toggleMenu(v);  
  52.    }  
  53.   });  
  54.   
  55.   tvTitle = (TextView) findViewById(R.id.activity_main_content_title);  
  56.   
  57.   FragmentManager fm = MainActivity.this.getSupportFragmentManager();  
  58.   FragmentTransaction ft = fm.beginTransaction();  
  59.   Layout1 fragment = new Layout1();  
  60.   ft.add(R.id.activity_main_content_fragment, fragment);  
  61.   ft.commit();  
  62.   
  63.  }  
  64.   
  65.  @Override  
  66.  public boolean onCreateOptionsMenu(Menu menu) {  
  67.   getMenuInflater().inflate(R.menu.main, menu);  
  68.   return true;  
  69.  }  
  70.   
  71.  public void toggleMenu(View v) {  
  72.   mLayout.toggleMenu();  
  73.  }  
  74.   
  75.  private void onMenuItemClick(AdapterView<?> parent, View view,  
  76.    int position, long id) {  
  77.   String selectedItem = lvMenuItems[position];  
  78.   String currentItem = tvTitle.getText().toString();  
  79.   if (selectedItem.compareTo(currentItem) == 0) {  
  80.    mLayout.toggleMenu();  
  81.    return;  
  82.   }  
  83.   
  84.   FragmentManager fm = MainActivity.this.getSupportFragmentManager();  
  85.   FragmentTransaction ft = fm.beginTransaction();  
  86.   Fragment fragment = null;  
  87.   
  88.   if (selectedItem.compareTo("Layout 1") == 0) {  
  89.    fragment = new Layout1();  
  90.   } else if (selectedItem.compareTo("Layout 2") == 0) {  
  91.    fragment = new Layout2();  
  92.   }  
  93.   
  94.   if (fragment != null) {  
  95.    ft.replace(R.id.activity_main_content_fragment, fragment);  
  96.    ft.commit();  
  97.    tvTitle.setText(selectedItem);  
  98.   }  
  99.   mLayout.toggleMenu();  
  100.  }  
  101.   
  102.  @Override  
  103.  public void onBackPressed() {  
  104.   if (mLayout.isMenuShown()) {  
  105.    mLayout.toggleMenu();  
  106.   } else {  
  107.    super.onBackPressed();  
  108.   }  
  109.  }  
  110. }  


MainLayout.java

  1. package com.android.slidingmenuexample;  
  2.   
  3. import android.content.Context;  
  4. import android.os.Handler;  
  5. import android.util.AttributeSet;  
  6. import android.util.Log;  
  7. import android.view.MotionEvent;  
  8. import android.view.View;  
  9. import android.view.animation.Interpolator;  
  10. import android.widget.LinearLayout;  
  11. import android.widget.Scroller;  
  12.   
  13. public class MainLayout extends LinearLayout {  
  14.   
  15.  private static final int SLIDING_DURATION = 500;  
  16.  private static final int QUERY_INTERVAL = 16;  
  17.  int mainLayoutWidth;  
  18.  private View menu;  
  19.  private View content;  
  20.  private static int menuRightMargin = 15;  
  21.   
  22.  private enum MenuState {  
  23.   HIDING, HIDDEN, SHOWING, SHOWN,  
  24.  };  
  25.   
  26.  private int contentXOffset;  
  27.  private MenuState currentMenuState = MenuState.HIDDEN;  
  28.  private Scroller menuScroller = new Scroller(this.getContext(),  
  29.    new EaseInInterpolator());  
  30.  private Runnable menuRunnable = new MenuRunnable();  
  31.  private Handler menuHandler = new Handler();  
  32.  int prevX = 0;  
  33.  boolean isDragging = false;  
  34.  int lastDiffX = 0;  
  35.   
  36.  public MainLayout(Context context, AttributeSet attrs) {  
  37.   super(context, attrs);  
  38.  }  
  39.   
  40.  public MainLayout(Context context) {  
  41.   super(context);  
  42.  }  
  43.   
  44.  @Override  
  45.  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  46.   super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  47.   
  48.   mainLayoutWidth = MeasureSpec.getSize(widthMeasureSpec);  
  49.   menuRightMargin = mainLayoutWidth * 10 / 100;  
  50.  }  
  51.   
  52.  @Override  
  53.  protected void onAttachedToWindow() {  
  54.   super.onAttachedToWindow();  
  55.   
  56.   menu = this.getChildAt(0);  
  57.   content = this.getChildAt(1);  
  58.   content.setOnTouchListener(new OnTouchListener() {  
  59.    @Override  
  60.    public boolean onTouch(View v, MotionEvent event) {  
  61.     return MainLayout.this.onContentTouch(v, event);  
  62.    }  
  63.   });  
  64.   menu.setVisibility(View.GONE);  
  65.  }  
  66.   
  67.  @Override  
  68.  protected void onLayout(boolean changed, int left, int top, int right,  
  69.    int bottom) {  
  70.   if (changed) {  
  71.    LayoutParams contentLayoutParams = (LayoutParams) content  
  72.      .getLayoutParams();  
  73.    contentLayoutParams.height = this.getHeight();  
  74.    contentLayoutParams.width = this.getWidth();  
  75.    LayoutParams menuLayoutParams = (LayoutParams) menu  
  76.      .getLayoutParams();  
  77.    menuLayoutParams.height = this.getHeight();  
  78.    menuLayoutParams.width = this.getWidth() - menuRightMargin;  
  79.   }  
  80.   menu.layout(left, top, right - menuRightMargin, bottom);  
  81.   content.layout(left + contentXOffset, top, right + contentXOffset,  
  82.     bottom);  
  83.   
  84.  }  
  85.   
  86.  public void toggleMenu() {  
  87.   
  88.   if (currentMenuState == MenuState.HIDING  
  89.     || currentMenuState == MenuState.SHOWING)  
  90.    return;  
  91.   
  92.   switch (currentMenuState) {  
  93.   case HIDDEN:  
  94.    currentMenuState = MenuState.SHOWING;  
  95.    menu.setVisibility(View.VISIBLE);  
  96.    menuScroller.startScroll(00, menu.getLayoutParams().width, 0,  
  97.      SLIDING_DURATION);  
  98.    break;  
  99.   case SHOWN:  
  100.    currentMenuState = MenuState.HIDING;  
  101.    menuScroller.startScroll(contentXOffset, 0, -contentXOffset, 0,  
  102.      SLIDING_DURATION);  
  103.    break;  
  104.   default:  
  105.    break;  
  106.   }  
  107.   menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);  
  108.   this.invalidate();  
  109.  }  
  110.   
  111.  protected class MenuRunnable implements Runnable {  
  112.   @Override  
  113.   public void run() {  
  114.    boolean isScrolling = menuScroller.computeScrollOffset();  
  115.    adjustContentPosition(isScrolling);  
  116.   }  
  117.  }  
  118.   
  119.  private void adjustContentPosition(boolean isScrolling) {  
  120.   int scrollerXOffset = menuScroller.getCurrX();  
  121.   
  122.   content.offsetLeftAndRight(scrollerXOffset - contentXOffset);  
  123.   
  124.   contentXOffset = scrollerXOffset;  
  125.   this.invalidate();  
  126.   if (isScrolling)  
  127.    menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);  
  128.   else  
  129.    this.onMenuSlidingComplete();  
  130.  }  
  131.   
  132.  private void onMenuSlidingComplete() {  
  133.   switch (currentMenuState) {  
  134.   case SHOWING:  
  135.    currentMenuState = MenuState.SHOWN;  
  136.    break;  
  137.   case HIDING:  
  138.    currentMenuState = MenuState.HIDDEN;  
  139.    menu.setVisibility(View.GONE);  
  140.    break;  
  141.   default:  
  142.    return;  
  143.   }  
  144.  }  
  145.   
  146.  protected class EaseInInterpolator implements Interpolator {  
  147.   @Override  
  148.   public float getInterpolation(float t) {  
  149.    return (float) Math.pow(t - 15) + 1;  
  150.   }  
  151.   
  152.  }  
  153.   
  154.  public boolean isMenuShown() {  
  155.   return currentMenuState == MenuState.SHOWN;  
  156.  }  
  157.   
  158.  public boolean onContentTouch(View v, MotionEvent event) {  
  159.   if (currentMenuState == MenuState.HIDING  
  160.     || currentMenuState == MenuState.SHOWING)  
  161.    return false;  
  162.   int curX = (int) event.getRawX();  
  163.   int diffX = 0;  
  164.   
  165.   switch (event.getAction()) {  
  166.   case MotionEvent.ACTION_DOWN:  
  167.   
  168.    prevX = curX;  
  169.    return true;  
  170.   
  171.   case MotionEvent.ACTION_MOVE:  
  172.    if (!isDragging) {  
  173.     isDragging = true;  
  174.     menu.setVisibility(View.VISIBLE);  
  175.    }  
  176.    diffX = curX - prevX;  
  177.    if (contentXOffset + diffX <= 0) {  
  178.     diffX = -contentXOffset;  
  179.    } else if (contentXOffset + diffX > mainLayoutWidth  
  180.      - menuRightMargin) {  
  181.     diffX = mainLayoutWidth - menuRightMargin - contentXOffset;  
  182.    }  
  183.    content.offsetLeftAndRight(diffX);  
  184.    contentXOffset += diffX;  
  185.    this.invalidate();  
  186.   
  187.    prevX = curX;  
  188.    lastDiffX = diffX;  
  189.    return true;  
  190.   
  191.   case MotionEvent.ACTION_UP:  
  192.    Log.d("MainLayout.java onContentTouch()""Up lastDiffX "  
  193.      + lastDiffX);  
  194.   
  195.    if (lastDiffX > 0) {  
  196.     currentMenuState = MenuState.SHOWING;  
  197.     menuScroller.startScroll(contentXOffset, 0,  
  198.       menu.getLayoutParams().width - contentXOffset, 0,  
  199.       SLIDING_DURATION);  
  200.    } else if (lastDiffX < 0) {  
  201.     currentMenuState = MenuState.HIDING;  
  202.     menuScroller.startScroll(contentXOffset, 0, -contentXOffset, 0,  
  203.       SLIDING_DURATION);  
  204.    }  
  205.    menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);  
  206.    this.invalidate();  
  207.    isDragging = false;  
  208.    prevX = 0;  
  209.    lastDiffX = 0;  
  210.    return true;  
  211.   
  212.   default:  
  213.    break;  
  214.   }  
  215.   
  216.   return false;  
  217.  }  
  218. }  


Layout1.java

  1. package com.android.slidingmenuexample;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v4.app.Fragment;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8.   
  9. public class Layout1 extends Fragment {  
  10.   
  11.     @Override  
  12.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  13.             Bundle savedInstanceState) {  
  14.         View view = inflater.inflate(R.layout.fragment_layout1, null);  
  15.         return view;  
  16.     }  
  17. }  


Layout2.java

  1. package com.android.slidingmenuexample;  
  2.   
  3. import android.os.Bundle;  
  4. import android.support.v4.app.Fragment;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8.   
  9. public class Layout2 extends Fragment {  
  10.   
  11.  @Override  
  12.  public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  13.    Bundle savedInstanceState) {  
  14.   View view = inflater.inflate(R.layout.fragment_layout2, null);  
  15.   return view;  
  16.  }  
  17. }  


activity_main.xml
  1. <com.android.slidingmenuexample.MainLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent" >  
  4.       
  5.      <LinearLayout  
  6.         xmlns:android="http://schemas.android.com/apk/res/android"  
  7.         android:layout_width="match_parent"  
  8.         android:layout_height="match_parent"  
  9.         android:orientation="vertical" >  
  10.   
  11.         <ListView  
  12.             android:id="@+id/menu_listview"  
  13.             android:layout_width="wrap_content"  
  14.             android:layout_height="match_parent"  
  15.             android:background="@android:color/holo_purple"  
  16.             android:cacheColorHint="#00000000" >  
  17.         </ListView>  
  18.     </LinearLayout>  
  19.   
  20.     <LinearLayout  
  21.         xmlns:android="http://schemas.android.com/apk/res/android"  
  22.         android:layout_width="match_parent"  
  23.         android:layout_height="match_parent"  
  24.         android:orientation="vertical" >  
  25.     
  26.         <!-- This acts as Actionbar -->  
  27.         <LinearLayout  
  28.          android:layout_width="match_parent"  
  29.          android:layout_height="wrap_content"  
  30.          android:background="@android:color/darker_gray"  
  31.          android:orientation="horizontal" >  
  32.               
  33.             <Button  
  34.              android:layout_width="wrap_content"  
  35.              android:layout_height="wrap_content"  
  36.              android:onClick="toggleMenu"  
  37.              android:text="Menu"  
  38.              android:id="@+id/button_menu" />  
  39.               
  40.             <TextView  
  41.         android:layout_width="0dp"  
  42.         android:layout_height="wrap_content"  
  43.         android:text="Layout 1"  
  44.         android:gravity="center"  
  45.         android:id="@+id/activity_main_content_title"  
  46.         android:layout_weight="1" />  
  47.               
  48.         </LinearLayout>         
  49.           
  50.         <!-- This is where fragment will show up -->  
  51.         <FrameLayout  
  52.       android:id="@+id/activity_main_content_fragment"  
  53.       android:layout_width="match_parent"  
  54.       android:layout_height="match_parent" >  
  55.           
  56.      </FrameLayout>  
  57.           
  58.     </LinearLayout>  
  59.   
  60. </com.android.slidingmenuexample.MainLayout>  


fragment_layout1.xml
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="@android:color/holo_blue_dark" >  
  6.   
  7.     <TextView  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:layout_centerInParent="true"  
  11.         android:gravity="center"  
  12.         android:text="Layout 1"  
  13.         android:textColor="@android:color/black"  
  14.         android:textSize="20sp" />  
  15.   
  16. </RelativeLayout>  


fragment_layout2.xml
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:background="@android:color/holo_green_dark"  
  6.     android:orientation="vertical" >  
  7.   
  8.     <TextView  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:layout_centerInParent="true"  
  12.         android:text="Layout2"  
  13.         android:textColor="@android:color/black"  
  14.         android:textSize="20sp" />  
  15.   
  16. </RelativeLayout>  


rec/values/menu_items.xml
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string-array name="menu_items">  
  4.         <item >Layout 1</item>  
  5.         <item >Layout 2</item>  
  6.     </string-array>  
  7. </resources>  





Complete project on GITHUB

No comments:

Post a Comment