Saturday, April 26, 2014

Android Slide Menu like Facebook With Effect Like Navigation Drawer

Recently in one our project we have to build slide out menu like Facebook but with some difference. In Facebook app when slide menu opens the main content screen also goes left or right. While in our case main content should stat and menu should come over the content like Navigation Drawer. For certain reasons we could not use Navigation Drawer class as we have the fragments in the application.

So we accomplished it with some different manner. In this blog I will explain you how to do this. First lets see our Main Layout XML file.

&ltcom.myapp.layout.MainLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#221010" >

<!-- This holds our content -->
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >

         <FrameLayout
                android:id="@+id/activity_main_content_fragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/white" >
         </FrameLayout>
         <RelativeLayout
                android:layout_width="fill_parent"
                android:layout_height="70dip"
                android:alpha="0.9"
                android:background="#d84047"
                android:orientation="horizontal" >

                <Button
                    android:id="@+id/activity_main_content_button_menu"
                    android:layout_width="30dip"
                    android:layout_height="30dip"
                    android:layout_marginBottom="10dip"
                    android:layout_marginTop="18dip"
                    android:background="@drawable/menu"
                    android:onClick="toggleMenu" />
         </RelativeLayout>
    </FrameLayout>

 <!-- This holds our menu -->

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal" >
    </LinearLayout>

</com. myapp.layout.MainLayout>

So in our layout we have FrameLayout with top toolbar with menu button which will open our menu. and a content layout which holds out main content. Now our menu should occupy 70% of available space when it opens so for that we have to set width and height for it. Lets create MainLayout class in app and configure this.

package com. myapp.layout;

import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Interpolator;
import android.view.animation.TranslateAnimation;
import android.widget.LinearLayout;
import android.widget.Scroller;


public class MainLayout extends LinearLayout {

int mainLayoutWidth;
private View menu;
private View content;
private int contentXOffset;
private MenuState currentMenuState = MenuState.HIDDEN;
private enum MenuState {
        HIDING,
        HIDDEN,
        SHOWING,
        SHOWN,
};
public MainLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
}

public MainLayout(Context context) {
        super(context);
}

// Overriding LinearLayout core methods
   
   
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);      
        mainLayoutWidth = MeasureSpec.getSize(widthMeasureSpec);
    }
   
    // This is called when MainLayout is attached to window
    // At this point it has a Surface and will start drawing.
    // Note that this function is guaranteed to be called before onDraw
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
       
        // Get our 2 child View
        menu = this.getChildAt(1);
        content = this.getChildAt(0);  
       
        // Initially hide the menu
        menu.setVisibility(View.GONE);

    }
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
     
        if(changed) {
            LayoutParams contentLayoutParams = (LayoutParams)content.getLayoutParams();
            contentLayoutParams.height = this.getHeight();
            contentLayoutParams.width = this.getWidth();

            // menu View occupies the full height, but certain width
            LayoutParams menuLayoutParams = (LayoutParams)menu.getLayoutParams();
            menuLayoutParams.height = this.getHeight();
            menuLayoutParams.width = mainLayoutWidth;
        }
        // Layout the child views  
       
        menu.layout(left, top, mainLayoutWidth*0.7 , bottom);
        content.layout(left, top, right , bottom);
       
    }

    public void toggleMenu() {
    this.calledFromToggleMenu = true;
        // Do nothing if sliding is in progress
        if(currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
            return;
       
        switch(currentMenuState) {
        case HIDDEN:
        //show menu here
        Log.v("log","current state is hidden");
       
        TranslateAnimation moveLefttoRight = new TranslateAnimation(menu.getWidth()*-1, 0, 0, 0);
       
        moveLefttoRight.setAnimationListener(new Animation.AnimationListener(){
           @Override
           public void onAnimationStart(Animation arg0) {
            menu.setVisibility(View.VISIBLE);
            menu.bringToFront();
           }          
           @Override
           public void onAnimationRepeat(Animation arg0) {
           }          
           @Override
           public void onAnimationEnd(Animation arg0) {
           
           }
        });
       
            moveLefttoRight.setDuration(300);
            moveLefttoRight.setFillAfter(true);
            menu.startAnimation(moveLefttoRight);
            currentMenuState = MenuState.SHOWN;
            break;
        case SHOWN:
        //hide menu here
        Log.v("log","current state is visible");
        TranslateAnimation moveRightoLeft = new TranslateAnimation(0, menu.getWidth()*-1, 0, 0);
        moveRightoLeft.setDuration(300);
        moveRightoLeft.setFillAfter(true);
            menu.startAnimation(moveRightoLeft);
            currentMenuState = MenuState.HIDDEN;
            moveRightoLeft.setAnimationListener(new Animation.AnimationListener(){
           @Override
           public void onAnimationStart(Animation arg0) {
           
           }          
           @Override
           public void onAnimationRepeat(Animation arg0) {
           }          
           @Override
           public void onAnimationEnd(Animation arg0) {
            Log.v("log","hiding menu");
            content.bringToFront();
           }
        });
            break;
        default:
            break;
        }
        // Begin querying
        //menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
       
        // Invalite this whole MainLayout, causing onLayout() to be called
        //this.invalidate();
    }

}

As you can see in above class we have extended LinearLayout and it's core method and created an extended class. Where onLayout method we are hiding the menu and setting it's width to 70% width of the screen. Key function here is toggleMenu. It identifies the state of the menu and based on that show animation and show/hide the menu. You just have to call toggleMenu function when users taps on button. To close the menu user has to swipe left on the screen and it will close the menu. See the below code.

package com.myapp;
import com.myapp.layout.MainLayout;

public class MainActivity extends FragmentActivity implements
OnTouchListener{
public static MainLayout mainLayout;
      protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_market_place);
mainLayout = (MainLayout) this.getLayoutInflater().inflate(
R.layout.activity_main, null);
setContentView(mainLayout);
                btMenu = (Button) findViewById(R.id.activity_main_content_button_menu);
btMenu.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Show/hide the menu
mainLayout .toggleMenu(v);
}
});
      }

      @Override
       public boolean dispatchTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
super.dispatchTouchEvent(ev);

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN: {
// store the X value when the user's finger was pressed down
downXValue = ev.getX();
downYValue = ev.getY();
Log.v("", "= " + downYValue);
break;
}

case MotionEvent.ACTION_UP: {
// Get the X value when the user released his/her finger
float currentX = ev.getX();
float currentY = ev.getY();
// check if horizontal or vertical movement was bigger

if (Math.abs(downXValue - currentX) > Math.abs(downYValue
- currentY)) {
Log.v("", "x");
// going backwards: pushing stuff to the right

Log.v("log", " downXvalue - currentX "
+ (downXValue - currentX));

if (downXValue < currentX) {
Log.v("", "right");
if (-150 > (downXValue - currentX)) {
Log.v("log", "right if");
mainLayout.toggleMenu("OPEN");
} else {
Log.v("log", "right else");
}
}

// going forwards: pushing stuff to the left
if (downXValue > currentX) {
mainLayout. toggleMenu("CLOSE");

}

} else {
Log.v("", "y ");

if (downYValue < currentY) {
Log.v("", "down");

}
if (downYValue > currentY) {
Log.v("", "up");

}
}
break;
}

}
return true;
}
}

As you can see in above code we are setting MainLayout as content layout for the activity added on click listener for menu button. Which invokes toggleMenu function of mainLayout and toggles the menu. Also we have added dispatchTouchEvent method where we tracking user motion and invoke toggle menu method.

Hope this helps.

No comments:

Post a Comment