Android Open Source - mobilib Mbl Side Menu Enabled Layout






From Project

Back to project page mobilib.

License

The source code is released under:

MIT License

If you think the Android project mobilib listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.datdo.mobilib.widget;
// w  ww. j a v  a2s  .c o  m
import com.datdo.mobilib.util.MblUtils;

import junit.framework.Assert;
import android.content.Context;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.FrameLayout;
import android.widget.Scroller;

public class MblSideMenuEnabledLayout extends FrameLayout {
    private static final int DEFAULT_HIDDEN_VIEW_MARGIN_IN_DP = 50;
    private static final int SCROLL_X_PER_Y_RATE_THRESHHOLD = 4;

    private static final int LEFT_VIEW_ID = 1;
    private static final int MID_VIEW_ID = 2;
    private static final int RIGHT_VIEW_ID = 3;

    private static final int DIRECTION_LEFT_TO_RIGHT = -1;
    private static final int DIRECTION_RIGHT_TO_LEFT = 1;

    public static enum SidePosition {
        LEFT, MID, RIGHT;
    }

    // views
    private ViewGroup mLeftView;
    private ViewGroup mMidView;
    private ViewGroup mRightView;
    private boolean mHasLeftContent;
    private boolean mHasRightContent;
    private int mHiddenViewMargin;
    private int mShadowPadding;

    // gestures
    private SidePosition mSidePos = SidePosition.MID;
    private Scroller mScroller;
    private GestureDetector mGestureDetector;
    private float mCurrentX;
    private boolean mDraggingHorizontally;
    private boolean mFlingDetected;
    private int mFlingDirection;
    private boolean mTapMidViewToCloseSideViewDetected;
    private Handler mMainThread = new Handler();
    private MblSideMenuEnabledLayoutDelegate mDelegate;

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

    public MblSideMenuEnabledLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MblSideMenuEnabledLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void init(
            final Context context,
            final Object leftContent,
            final Object midContent,
            final Object rightContent,
            final int hiddenViewMargin,
            final int shadowPadding,
            final int shadowDrawableResId,
            final MblSideMenuEnabledLayoutDelegate delegate) {

        // assertions
        Assert.assertTrue(midContent != null);

        // options
        mHiddenViewMargin = hiddenViewMargin < 0 ? MblUtils.pxFromDp(DEFAULT_HIDDEN_VIEW_MARGIN_IN_DP) : hiddenViewMargin;
        mShadowPadding = shadowPadding > 0 && shadowDrawableResId > 0 ? shadowPadding : 0;
        mHasLeftContent = leftContent != null;
        mHasRightContent = rightContent != null;
        mDelegate = delegate;

        // scroller && gesture detector
        mScroller = new Scroller(context);
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            private boolean mShouldHandle;
            @Override
            public boolean onDown(MotionEvent e) {
                mFlingDetected = false;
                mTapMidViewToCloseSideViewDetected = false;
                mShouldHandle = MblUtils.motionEventOnView(e, mMidView);
                return mShouldHandle;
            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                if (!mShouldHandle) return false;

                if (mSidePos != SidePosition.MID && MblUtils.motionEventOnView(e, mMidView)) {
                    mTapMidViewToCloseSideViewDetected = true;
                    return true;
                }
                return false;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                if (!mShouldHandle) return false;

                // check if this dragging should be considered as horizontal dragging
                float rate = distanceY != 0 ? distanceX/distanceY : SCROLL_X_PER_Y_RATE_THRESHHOLD;
                if (Math.abs(rate) >= SCROLL_X_PER_Y_RATE_THRESHHOLD) {
                    mDraggingHorizontally = true;
                }
                if (mDraggingHorizontally) {
                    float toX = mCurrentX - distanceX;
                    toX = Math.max(toX, getMostLeftX());
                    toX = Math.min(toX, getMostRightX());
                    scrollTo(toX);
                }

                // return true if a horizontal dragging is detected
                return mDraggingHorizontally;
            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                if (!mShouldHandle) return false;

                // if left content or right content is empty, forbid flinging to that direction
                int flingDirection = velocityX > 0 ? DIRECTION_LEFT_TO_RIGHT : DIRECTION_RIGHT_TO_LEFT;
                boolean isWrongDirection = 
                        (!mHasLeftContent && flingDirection == DIRECTION_LEFT_TO_RIGHT) ||
                        (!mHasRightContent && flingDirection == DIRECTION_RIGHT_TO_LEFT);
                if (mSidePos == SidePosition.MID && isWrongDirection) return false;

                // check if velocity is big enough to be considered a fling
                if (Math.abs(velocityX) >= getFlingThreshHold()) {
                    mFlingDetected = true;
                    mFlingDirection = flingDirection;
                }

                // fling the scroll so that mid view will fly together with the finger
                mScroller.fling(
                        (int) mCurrentX, 0,
                        (int) -velocityX, 0,
                        getMostLeftX(), getMostRightX(), // TODO: should not use getMostLeftX(), getMostRightX() because left view or right view may be empty
                        0, 0);
                updateTranslationX();

                // return true if a fling is detected
                return mFlingDetected;
            }
        });

        // left view
        mLeftView = generateSubView(context, LEFT_VIEW_ID, 0, mHiddenViewMargin);
        addView(mLeftView);

        // right view
        mRightView = generateSubView(context, RIGHT_VIEW_ID, mHiddenViewMargin, 0);
        addView(mRightView);

        // mid view
        mMidView = generateSubView(context, MID_VIEW_ID, 0, 0);
        mMidView.setOnTouchListener( new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return true;
            }
        });

        if (shadowDrawableResId > 0) mMidView.setBackgroundResource(shadowDrawableResId);
        addView(mMidView);
        getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                MblUtils.removeOnGlobalLayoutListener(MblSideMenuEnabledLayout.this, this);
                mMidView.getLayoutParams().width = getWidth() + 2 * mShadowPadding;
                mMidView.setPadding(mShadowPadding, 0, mShadowPadding, 0);
                setMidViewMargin(getOriginX());
                mCurrentX = getOriginX();
            }
        });

        // add content to views
        addContent(context, leftContent, mLeftView, LEFT_VIEW_ID);
        addContent(context, midContent, mMidView, MID_VIEW_ID);
        addContent(context, rightContent, mRightView, RIGHT_VIEW_ID);
    }

    private ViewGroup generateSubView(Context context, int id, int leftMargin, int rightMargin) {
        FrameLayout ret = new FrameLayout(context);
        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        lp.leftMargin = leftMargin;
        lp.rightMargin = rightMargin;
        lp.gravity = Gravity.TOP | Gravity.LEFT;
        ret.setLayoutParams(lp);
        ret.setId(id);
        return ret;
    }

    private void addContent(Context context, Object content, ViewGroup view, int viewId) {
        if (content == null) return;
        if (content instanceof View) {
            view.addView((View) content);
        } else if (content instanceof Fragment) {
            FragmentActivity activity = (FragmentActivity) context;
            activity.getSupportFragmentManager()
            .beginTransaction()
            .replace(viewId, (Fragment) content)
            .commit();
        }
    }

    public SidePosition getSidePosition() {
        return mSidePos;
    }

    private void setMidViewMargin(int margin) {
        MarginLayoutParams lp = (MarginLayoutParams) mMidView.getLayoutParams();
        lp.leftMargin = margin;
        mMidView.setLayoutParams(lp);
    }

    private void updateTranslationX() {
        if (mScroller.computeScrollOffset()) {
            setMidViewMargin(mScroller.getCurrX());
        }
        if (!mScroller.isFinished()) {
            mMainThread.post(new Runnable() {
                @Override
                public void run() {
                    updateTranslationX();
                }
            });
        } else {
            mCurrentX = mScroller.getCurrX();
        }
    }

    private void scrollTo(float toX) {
        mScroller.startScroll((int) mCurrentX, 0, (int)(toX - mCurrentX), 0);
        mCurrentX = toX;
        updateTranslationX();
    }

    public void scrollTo(final SidePosition newPos) {
        if (newPos == SidePosition.LEFT) {
            scrollTo(getMostRightX());
        } else if (newPos == SidePosition.MID) {
            scrollTo(getOriginX());
        } else if (newPos == SidePosition.RIGHT) {
            scrollTo(getMostLeftX());
        }

        boolean isChanged = mSidePos != newPos;
        mSidePos = newPos;
        if (mDelegate != null && isChanged) {
            mMainThread.post(new Runnable() {
                @Override
                public void run() {
                    mDelegate.handleCurrentSideChange(newPos);
                }
            });
        }
    }

    private void handleFling() {
        if (mSidePos == SidePosition.LEFT) {
            if (mFlingDirection == DIRECTION_RIGHT_TO_LEFT) {
                scrollTo(SidePosition.MID);
                return;
            }
        } else if (mSidePos == SidePosition.MID) {
            if (mFlingDirection == DIRECTION_LEFT_TO_RIGHT) {
                scrollTo(SidePosition.LEFT);
                return;
            } else if (mFlingDirection == DIRECTION_RIGHT_TO_LEFT) {
                scrollTo(SidePosition.RIGHT);
                return;
            }
        } else if (mSidePos == SidePosition.RIGHT) {
            if (mFlingDirection == DIRECTION_LEFT_TO_RIGHT) {
                scrollTo(SidePosition.MID);
                return;
            }
        }
        scrollTo(mSidePos); // scroll back to original position 
    }

    private void handleFinishDragging() {
        float relativeX = mCurrentX - getOriginX();
        if (mSidePos == SidePosition.LEFT) {
            if (relativeX >= 0 && relativeX <= getWidth() / 2) {
                scrollTo(SidePosition.MID);
                return;
            }
        } else if (mSidePos == SidePosition.MID) {
            if (relativeX > getWidth()/2) {
                scrollTo(SidePosition.LEFT);
                return;
            } else if (relativeX < -getWidth()/2) {
                scrollTo(SidePosition.RIGHT);
                return;
            }
        } else if (mSidePos == SidePosition.RIGHT) {
            if (relativeX >= -getWidth()/2 && relativeX <= 0) {
                scrollTo(SidePosition.MID);
                return;
            }
        }
        scrollTo(mSidePos); // scroll back to original position
    }

    private int getFlingThreshHold() {
        return getWidth();
    }

    private boolean shouldHandleTouchEvent(MotionEvent event) {
        if (mDelegate != null && !mDelegate.shouldAllowSwipeToOpenSideView(event)) {
            return false;
        }
        return true;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (!shouldHandleTouchEvent(event)) return false;

        boolean handled = mGestureDetector.onTouchEvent(event);
        boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
        if (isUpOrCancel(event) && mTapMidViewToCloseSideViewDetected) {
            scrollTo(SidePosition.MID);
        }
        return handled && !isDown;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!shouldHandleTouchEvent(event)) return false;

        mGestureDetector.onTouchEvent(event);
        if (isUpOrCancel(event)) {
            if (mFlingDetected) {
                handleFling();
            } else {
                handleFinishDragging();
            }
            mDraggingHorizontally = false;
        }
        return true;
    }

    private boolean isUpOrCancel(MotionEvent event) {
        return  event.getAction() == MotionEvent.ACTION_UP ||
                event.getAction() == MotionEvent.ACTION_CANCEL;
    }

    private int getMidViewWidth() {
        return mMidView.getWidth();
    }

    private int getOriginX() {
        return -mShadowPadding;
    }

    private int getMostLeftX() {
        if (!mHasRightContent) {
            return getOriginX();
        }
        return (mHiddenViewMargin + mShadowPadding) - getMidViewWidth();
    }

    private int getMostRightX() {
        if (!mHasLeftContent) {
            return getOriginX();
        }
        return getWidth() - (mHiddenViewMargin + mShadowPadding);
    }

    public static interface MblSideMenuEnabledLayoutDelegate {
        public boolean shouldAllowSwipeToOpenSideView(MotionEvent event);
        public void handleCurrentSideChange(SidePosition pos);
    }
}




Java Source Code List

com.datdo.mobilib.api.DBBase.java
com.datdo.mobilib.api.DBHelper.java
com.datdo.mobilib.api.MblApi.java
com.datdo.mobilib.api.MblCache.java
com.datdo.mobilib.api.MblException.java
com.datdo.mobilib.api.MblSSLCertificateUtils.java
com.datdo.mobilib.base.MblActivityPlugin.java
com.datdo.mobilib.base.MblBaseActionBarActivity.java
com.datdo.mobilib.base.MblBaseActivity.java
com.datdo.mobilib.base.MblBaseAdapter.java
com.datdo.mobilib.base.MblBaseApplication.java
com.datdo.mobilib.base.MblBaseFragmentActivity.java
com.datdo.mobilib.base.MblDecorView.java
com.datdo.mobilib.base.MblNetworkStatusChangedReceiver.java
com.datdo.mobilib.event.MblCommonEvents.java
com.datdo.mobilib.event.MblEventCenter.java
com.datdo.mobilib.event.MblEventListener.java
com.datdo.mobilib.event.MblStrongEventListener.java
com.datdo.mobilib.event.MblWeakArrayList.java
com.datdo.mobilib.imageinput.MblAutoResizeSquareImageView.java
com.datdo.mobilib.imageinput.MblDataInputActivity.java
com.datdo.mobilib.imageinput.MblImageFolderScanner.java
com.datdo.mobilib.imageinput.MblImageInput.java
com.datdo.mobilib.imageinput.MblImagePickingScanEngine.java
com.datdo.mobilib.imageinput.MblPickImageActivity.java
com.datdo.mobilib.imageinput.MblPickImageGridViewAdapter.java
com.datdo.mobilib.imageinput.MblTakeImageActivity.java
com.datdo.mobilib.util.MblAsyncTask.java
com.datdo.mobilib.util.MblImageLoader.java
com.datdo.mobilib.util.MblLinkMovementMethod.java
com.datdo.mobilib.util.MblUrlRecognizer.java
com.datdo.mobilib.util.MblUtils.java
com.datdo.mobilib.widget.MblHorizontalViewPager.java
com.datdo.mobilib.widget.MblListViewWithScrollableItems.java
com.datdo.mobilib.widget.MblSequenceImage.java
com.datdo.mobilib.widget.MblSideMenuEnabledLayout.java
com.datdo.mobilib.widget.MblTouchImageView.java