Skip to content

Instantly share code, notes, and snippets.

@markus2610
Forked from EddieRingle/gist:3179361
Created July 27, 2012 04:52
Show Gist options
  • Save markus2610/3186235 to your computer and use it in GitHub Desktop.
Save markus2610/3186235 to your computer and use it in GitHub Desktop.
A Slide-out Navigation Implementation for Android
/*
* Copyright (c) 2012 Eddie Ringle <[email protected]>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.Scroller;
/**
* DecorDrawerWrapper
*
* An implementation of the slide-out navigation pattern.
*
* @author Eddie Ringle ([email protected])
*/
public class DecorDrawerWrapper extends FrameLayout {
private static final int SCROLL_DURATION = 250;
private static final float TOUCH_TARGET_WIDTH_DIP = 48.0f;
private boolean mDrawerOpened = false;
private boolean mDrawerMoving = false;
private int mDecorOffsetX = 0;
private int mTouchTargetWidth;
private Drawable mShadowDrawable;
private Handler mScrollerHandler;
private Scroller mScroller;
private ViewGroup mDecorView;
private ViewGroup mDecorContent;
private ViewGroup mDrawerContent;
private IDrawerCallbacks mDrawerCallbacks;
public static interface IDrawerCallbacks {
public void onDrawerOpened();
public void onDrawerClosed();
}
public DecorDrawerWrapper(Activity activity, int drawerLayout) {
super(activity);
final DisplayMetrics dm = activity.getResources().getDisplayMetrics();
mTouchTargetWidth = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
TOUCH_TARGET_WIDTH_DIP, dm));
mShadowDrawable = getResources().getDrawable(R.drawable.decor_shadow);
mShadowDrawable.setBounds(-mTouchTargetWidth / 6, 0, 0, dm.heightPixels);
mScrollerHandler = new Handler();
mScroller = new Scroller(activity, new AccelerateDecelerateInterpolator());
mDecorView = (ViewGroup) activity.getWindow().getDecorView();
mDecorContent = (ViewGroup) mDecorView.getChildAt(0);
mDrawerContent = (ViewGroup) LayoutInflater.from(activity).inflate(drawerLayout, null);
/* TODO: Make this a configurable attribute */
mDecorContent.setBackgroundColor(Color.WHITE);
mDrawerContent.setPadding(0, 0, mTouchTargetWidth, 0);
/*
* Remove DecorView's children and re-appropriate them under this ViewGroup,
* along with the drawer contents.
*/
mDecorView.removeAllViews();
addView(mDrawerContent);
addView(mDecorContent);
mDecorView.addView(this);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mDrawerContent.layout(left, top, right, bottom);
mDecorContent.layout(mDecorContent.getLeft(), 0, mDecorContent.getLeft() + right, bottom);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/* We always return false from this method */
return false;
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mDrawerOpened || mDrawerMoving) {
canvas.save();
canvas.translate(mDecorContent.getLeft(), 0);
mShadowDrawable.draw(canvas);
canvas.restore();
}
}
public void toggleDrawer(final boolean animate) {
if (!mDrawerOpened) {
openDrawer(animate);
} else {
closeDrawer(animate);
}
}
public void toggleDrawer() {
toggleDrawer(true);
}
public void openDrawer(final boolean animate) {
if (mDrawerOpened || mDrawerMoving) return;
mDrawerMoving = true;
final int widthPixels = getResources().getDisplayMetrics().widthPixels;
mScroller.startScroll(0, 0, (widthPixels - mTouchTargetWidth), 0, SCROLL_DURATION);
mDecorOffsetX = 0;
mScrollerHandler.postDelayed(new Runnable() {
@Override
public void run() {
final boolean scrolling = mScroller.computeScrollOffset();
mDecorContent.offsetLeftAndRight(mScroller.getCurrX() - mDecorOffsetX);
mDecorOffsetX = mScroller.getCurrX();
postInvalidate();
if (!scrolling) {
mDrawerMoving = false;
mDrawerOpened = true;
if (mDrawerCallbacks != null) {
mScrollerHandler.post(new Runnable() {
@Override
public void run() {
enableDisableViewGroup((ViewGroup) mDecorContent
.findViewById(android.R.id.content), false);
mDrawerCallbacks.onDrawerOpened();
}
});
}
} else {
mScrollerHandler.postDelayed(this, 16);
}
}
}, 16);
}
public void openDrawer() {
openDrawer(true);
}
public void closeDrawer(final boolean animate) {
if (!mDrawerOpened || mDrawerMoving) return;
mDrawerMoving = true;
final int widthPixels = getResources().getDisplayMetrics().widthPixels;
mScroller.startScroll(0, 0, -(widthPixels - mTouchTargetWidth), 0, SCROLL_DURATION);
mDecorOffsetX = 0;
mScrollerHandler.postDelayed(new Runnable() {
@Override
public void run() {
final boolean scrolling = mScroller.computeScrollOffset();
mDecorContent.offsetLeftAndRight(mScroller.getCurrX() - mDecorOffsetX);
mDecorOffsetX = mScroller.getCurrX();
postInvalidate();
if (!scrolling) {
mDrawerMoving = false;
mDrawerOpened = false;
if (mDrawerCallbacks != null) {
mScrollerHandler.post(new Runnable() {
@Override
public void run() {
enableDisableViewGroup((ViewGroup) mDecorContent
.findViewById(android.R.id.content), true);
mDrawerCallbacks.onDrawerClosed();
}
});
}
} else {
mScrollerHandler.postDelayed(this, 16);
}
}
}, 16);
}
public void closeDrawer() {
closeDrawer(true);
}
public boolean isDrawerOpened() {
return mDrawerOpened;
}
public boolean isDrawerMoving() {
return mDrawerMoving;
}
public void setDrawerCallbacks(final IDrawerCallbacks callbacks) {
mDrawerCallbacks = callbacks;
}
public IDrawerCallbacks getDrawerCallbacks() {
return mDrawerCallbacks;
}
public static void enableDisableViewGroup(ViewGroup viewGroup, boolean enabled) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = viewGroup.getChildAt(i);
if (view.isFocusable()) view.setEnabled(enabled);
if (view instanceof ViewGroup) {
enableDisableViewGroup((ViewGroup) view, enabled);
} else if (view instanceof ListView) {
if (view.isFocusable()) view.setEnabled(enabled);
ListView listView = (ListView) view;
int listChildCount = listView.getChildCount();
for (int j = 0; j < listChildCount; j++) {
if (view.isFocusable()) listView.getChildAt(j).setEnabled(false);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment