diff --git a/res/layout/favorite.xml b/res/layout/favorite.xml
index a153a147a..6e20a31d3 100644
--- a/res/layout/favorite.xml
+++ b/res/layout/favorite.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
+
diff --git a/res/layout/gallery_list.xml b/res/layout/gallery_list.xml
index dc912c427..e29d58cd1 100644
--- a/res/layout/gallery_list.xml
+++ b/res/layout/gallery_list.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-
+
diff --git a/src/com/hippo/ehviewer/ui/FavouriteActivity.java b/src/com/hippo/ehviewer/ui/FavouriteActivity.java
index 2842a3863..71a7cd1e9 100644
--- a/src/com/hippo/ehviewer/ui/FavouriteActivity.java
+++ b/src/com/hippo/ehviewer/ui/FavouriteActivity.java
@@ -35,7 +35,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.os.Bundle;
-import android.support.v4.widget.DrawerLayout;
import android.view.ActionMode;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -80,6 +79,7 @@ import com.hippo.ehviewer.widget.LoadImageView;
import com.hippo.ehviewer.widget.MaterialToast;
import com.hippo.ehviewer.widget.PullViewGroup;
import com.hippo.ehviewer.widget.RatingView;
+import com.hippo.ehviewer.widget.SlidingDrawerLayout;
public class FavouriteActivity extends AbsActivity
implements ListView.MultiChoiceModeListener,
@@ -96,7 +96,7 @@ public class FavouriteActivity extends AbsActivity
private ActionBar mActionBar;
private int mThemeColor;
- private DrawerLayout mDrawerLayout;
+ private SlidingDrawerLayout mDrawerLayout;
private ListView mMenu;
private FrameLayout mContentView;
private FitWindowView mStandard;
@@ -173,7 +173,7 @@ public class FavouriteActivity extends AbsActivity
materialMenu.setState(MaterialMenuDrawable.IconState.ARROW);
// Get View
- mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout);
+ mDrawerLayout = (SlidingDrawerLayout) findViewById(R.id.drawerlayout);
mMenu = (ListView) mDrawerLayout.findViewById(R.id.favorite_menu_list);
mContentView = (FrameLayout) mDrawerLayout.findViewById(R.id.content);
mStandard = (FitWindowView) mDrawerLayout.findViewById(R.id.standard);
@@ -303,7 +303,7 @@ public class FavouriteActivity extends AbsActivity
lp.rightMargin = magicSpacing;
mActionableToastBar.setLayoutParams(lp);
mList.setPadding(mList.getPaddingLeft(), t, mList.getPaddingRight(), b);
- ((DrawerLayout.LayoutParams) mMenu.getLayoutParams()).topMargin = t;
+ ((SlidingDrawerLayout.LayoutParams) mMenu.getLayoutParams()).topMargin = t;
mMenu.setPadding(mMenu.getPaddingLeft(), mMenu.getPaddingTop(), mMenu.getPaddingRight(), b);
Ui.translucent(this, mThemeColor, t - Ui.ACTION_BAR_HEIGHT);
diff --git a/src/com/hippo/ehviewer/ui/GalleryListActivity.java b/src/com/hippo/ehviewer/ui/GalleryListActivity.java
index 1d17e6482..0adfd9414 100644
--- a/src/com/hippo/ehviewer/ui/GalleryListActivity.java
+++ b/src/com/hippo/ehviewer/ui/GalleryListActivity.java
@@ -41,7 +41,6 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.provider.MediaStore;
-import android.support.v4.widget.DrawerLayout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ImageSpan;
@@ -101,6 +100,7 @@ import com.hippo.ehviewer.widget.GalleryListView.OnGetListListener;
import com.hippo.ehviewer.widget.LoadImageView;
import com.hippo.ehviewer.widget.MaterialToast;
import com.hippo.ehviewer.widget.RatingView;
+import com.hippo.ehviewer.widget.SlidingDrawerLayout;
import com.hippo.ehviewer.widget.SuggestionHelper;
import com.hippo.ehviewer.widget.SuggestionTextView;
import com.hippo.ehviewer.widget.TagListView;
@@ -132,7 +132,7 @@ import com.hippo.ehviewer.windowsanimate.WindowsAnimate;
*/
public class GalleryListActivity extends AbsActivity implements View.OnClickListener,
SearchView.OnQueryTextListener, SearchView.OnFocusChangeListener,
- DrawerLayout.DrawerListener, EhClient.OnLoginListener,
+ SlidingDrawerLayout.DrawerListener, EhClient.OnLoginListener,
GalleryListView.GalleryListViewHelper, FitWindowView.OnFitSystemWindowsListener {
@SuppressWarnings("unused")
@@ -162,7 +162,7 @@ public class GalleryListActivity extends AbsActivity implements View.OnClickList
private MaterialMenuIcon mMaterialMenu;
private boolean mDirection;
- private DrawerLayout mDrawerLayout;
+ private SlidingDrawerLayout mDrawerLayout;
private View mLeftMenu;
private View mRightMenu;
private FitWindowView mStandard;
@@ -938,7 +938,7 @@ public class GalleryListActivity extends AbsActivity implements View.OnClickList
}
@Override
- public void onDrawerStateChanged(int i) {
+ public void onDrawerStateChanged(View drawerView, int newState) {
// Empty
}
@@ -946,8 +946,8 @@ public class GalleryListActivity extends AbsActivity implements View.OnClickList
public void onFitSystemWindows(int l, int t, int r, int b) {
mStaggeredGridView.setPadding(mStaggeredGridView.getPaddingLeft(), t,
mStaggeredGridView.getPaddingRight(), b);
- ((DrawerLayout.LayoutParams) mLeftMenu.getLayoutParams()).topMargin = t;
- ((DrawerLayout.LayoutParams) mRightMenu.getLayoutParams()).topMargin = t;
+ ((SlidingDrawerLayout.LayoutParams) mLeftMenu.getLayoutParams()).topMargin = t;
+ ((SlidingDrawerLayout.LayoutParams) mRightMenu.getLayoutParams()).topMargin = t;
mMenuList.setPadding(mMenuList.getPaddingLeft(), mMenuList.getPaddingTop(),
mMenuList.getPaddingRight(), b);
mQuickSearchList.setPadding(mQuickSearchList.getPaddingLeft(), mQuickSearchList.getPaddingTop(),
@@ -1017,7 +1017,7 @@ public class GalleryListActivity extends AbsActivity implements View.OnClickList
mMaterialMenu.setState(MaterialMenuDrawable.IconState.ARROW);
// Get View
- mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout);
+ mDrawerLayout = (SlidingDrawerLayout) findViewById(R.id.drawerlayout);
mLeftMenu = mDrawerLayout.findViewById(R.id.list_menu_left);
mRightMenu = mDrawerLayout.findViewById(R.id.list_menu_right);
@@ -1050,7 +1050,7 @@ public class GalleryListActivity extends AbsActivity implements View.OnClickList
mActionBar.setHomeAsUpIndicator(mResources.getDrawable(R.drawable.ic_navigation_drawer));
mDrawerLayout.setDrawerListener(this);
if (!mShowDrawer)
- mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mLeftMenu);
+ ; //mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, mLeftMenu);
// Content
mGalleryListView.setGalleryListViewHelper(this);
diff --git a/src/com/hippo/ehviewer/util/ViewUtils.java b/src/com/hippo/ehviewer/util/ViewUtils.java
index d8789528f..2349b953e 100644
--- a/src/com/hippo/ehviewer/util/ViewUtils.java
+++ b/src/com/hippo/ehviewer/util/ViewUtils.java
@@ -149,4 +149,23 @@ public final class ViewUtils {
if (oldLp != null)
v.setLayoutParams(oldLp);
}
+
+ /**
+ * Determine if the supplied view is under the given point in the
+ * parent view's coordinate system.
+ *
+ * @param view Child view of the parent to hit test
+ * @param x X position to test in the parent's coordinate system
+ * @param y Y position to test in the parent's coordinate system
+ * @return true if the supplied view is under the given point, false otherwise
+ */
+ public static boolean isViewUnder(View view, int x, int y) {
+ if (view == null) {
+ return false;
+ }
+ return x >= view.getLeft() &&
+ x < view.getRight() &&
+ y >= view.getTop() &&
+ y < view.getBottom();
+ }
}
diff --git a/src/com/hippo/ehviewer/widget/SlidingDrawerLayout.java b/src/com/hippo/ehviewer/widget/SlidingDrawerLayout.java
new file mode 100644
index 000000000..1d91a5b30
--- /dev/null
+++ b/src/com/hippo/ehviewer/widget/SlidingDrawerLayout.java
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 2014 Hippo Seven
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.hippo.ehviewer.widget;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
+
+import com.hippo.ehviewer.util.MathUtils;
+import com.hippo.ehviewer.util.Ui;
+import com.hippo.ehviewer.util.ViewUtils;
+
+@SuppressLint("RtlHardcoded")
+public class SlidingDrawerLayout extends ViewGroup implements ValueAnimator.AnimatorUpdateListener,
+ Animator.AnimatorListener {
+
+ private static final String TAG = SlidingDrawerLayout.class.getSimpleName();
+
+ private static final int[] LAYOUT_ATTRS = new int[] {
+ android.R.attr.layout_gravity
+ };
+
+ private static final int STATE_CLOSED = 0x0;
+ private static final int STATE_SLIDING = 0x1;
+ private static final int STATE_OPENED = 0x2;
+
+ private static final float OPEN_SENSITIVITY = 0.1f;
+ private static final float CLOSE_SENSITIVITY = 0.9f;
+
+ private int mMinDrawerMargin;
+ private boolean mInLayout;
+
+ private View mContentView;
+ private View mLeftDrawer;
+ private View mRightDrawer;
+
+ private ViewDragHelper mDragHelper;
+ private float mInitialMotionX;
+ private float mInitialMotionY;
+
+ private boolean mLeftOpened;
+ private boolean mRightOpened;
+ private int mLeftState;
+ private int mRightState;
+ private float mLeftPercent;
+ private float mRightPercent;
+
+ private ValueAnimator mAnimator;
+ private View mTargetView;
+ private int mStartLeft;
+ private int mEndLeft;
+ private boolean mToOpen;
+ private boolean mCancelAnimation;
+ private View mOpenTask;
+
+ private DrawerListener mListener;
+
+ private Drawable mStatusBarBackground;
+
+ private boolean mIntercepted;
+ private int mInterceptPointNum;
+
+ private Object mLastInsets;
+ private boolean mDrawStatusBarBackground;
+
+ private static int sDefaultMinDrawerMargin = 56;
+
+ private static final SlidingDrawerLayoutCompatImpl IMPL;
+
+ static {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ IMPL = new SlidingDrawerLayoutCompatImplApi21();
+ } else {
+ IMPL = new SlidingDrawerLayoutCompatImplBase();
+ }
+ }
+
+ interface SlidingDrawerLayoutCompatImpl {
+ void configureApplyInsets(View drawerLayout);
+ void dispatchChildInsets(View child, Object insets, int drawerGravity);
+ void applyMarginInsets(MarginLayoutParams lp, Object insets, int drawerGravity);
+ int getTopInset(Object lastInsets);
+ }
+
+ static class SlidingDrawerLayoutCompatImplBase implements SlidingDrawerLayoutCompatImpl {
+ @Override
+ public void configureApplyInsets(View drawerLayout) {
+ // This space for rent
+ }
+
+ @Override
+ public void dispatchChildInsets(View child, Object insets, int drawerGravity) {
+ // This space for rent
+ }
+
+ @Override
+ public void applyMarginInsets(MarginLayoutParams lp, Object insets, int drawerGravity) {
+ // This space for rent
+ }
+
+ @Override
+ public int getTopInset(Object insets) {
+ return 0;
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ static class SlidingDrawerLayoutCompatImplApi21 implements SlidingDrawerLayoutCompatImpl {
+ @Override
+ public void configureApplyInsets(View drawerLayout) {
+ if (drawerLayout instanceof SlidingDrawerLayout) {
+ drawerLayout.setOnApplyWindowInsetsListener(new InsetsListener());
+ drawerLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+ }
+
+ @Override
+ public void dispatchChildInsets(View child, Object insets, int gravity) {
+ WindowInsets wi = (WindowInsets) insets;
+ if (gravity == Gravity.LEFT) {
+ wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(),
+ wi.getSystemWindowInsetTop(), 0, wi.getSystemWindowInsetBottom());
+ } else if (gravity == Gravity.RIGHT) {
+ wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(),
+ wi.getSystemWindowInsetRight(), wi.getSystemWindowInsetBottom());
+ }
+ child.dispatchApplyWindowInsets(wi);
+ }
+
+ @Override
+ public void applyMarginInsets(MarginLayoutParams lp, Object insets, int gravity) {
+ WindowInsets wi = (WindowInsets) insets;
+ if (gravity == Gravity.LEFT) {
+ wi = wi.replaceSystemWindowInsets(wi.getSystemWindowInsetLeft(),
+ wi.getSystemWindowInsetTop(), 0, wi.getSystemWindowInsetBottom());
+ } else if (gravity == Gravity.RIGHT) {
+ wi = wi.replaceSystemWindowInsets(0, wi.getSystemWindowInsetTop(),
+ wi.getSystemWindowInsetRight(), wi.getSystemWindowInsetBottom());
+ }
+ lp.leftMargin = wi.getSystemWindowInsetLeft();
+ lp.topMargin = wi.getSystemWindowInsetTop();
+ lp.rightMargin = wi.getSystemWindowInsetRight();
+ lp.bottomMargin = wi.getSystemWindowInsetBottom();
+ }
+
+ @Override
+ public int getTopInset(Object insets) {
+ return insets != null ? ((WindowInsets) insets).getSystemWindowInsetTop() : 0;
+ }
+
+ static class InsetsListener implements View.OnApplyWindowInsetsListener {
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ final SlidingDrawerLayout drawerLayout = (SlidingDrawerLayout) v;
+ drawerLayout.setChildInsets(insets, insets.getSystemWindowInsetTop() > 0);
+ return insets.consumeSystemWindowInsets();
+ }
+ }
+ }
+
+ /**
+ * Listener for monitoring events about drawers.
+ */
+ public interface DrawerListener {
+ /**
+ * Called when a drawer's position changes.
+ * @param drawerView The child view that was moved
+ * @param slideOffset The new offset of this drawer within its range, from 0-1
+ */
+ public void onDrawerSlide(View drawerView, float percent);
+
+ /**
+ * Called when a drawer has settled in a completely open state.
+ * The drawer is interactive at this point.
+ *
+ * @param drawerView Drawer view that is now open
+ */
+ public void onDrawerOpened(View drawerView);
+
+ /**
+ * Called when a drawer has settled in a completely closed state.
+ *
+ * @param drawerView Drawer view that is now closed
+ */
+ public void onDrawerClosed(View drawerView);
+
+ /**
+ * Called when the drawer motion state changes. The new state will
+ * be one of {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.
+ *
+ * @param newState The new drawer motion state
+ */
+ public void onDrawerStateChanged(View drawerView, int newState);
+ }
+
+ /**
+ * minMargin should be app bar height in Material design
+ *
+ * @param minMargin
+ */
+ public static final void setDefaultMinDrawerMargin(int minMargin) {
+ sDefaultMinDrawerMargin = minMargin;
+ }
+
+ public SlidingDrawerLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public SlidingDrawerLayout(Context context, AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public SlidingDrawerLayout(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ mLeftOpened = false;
+ mRightOpened = false;
+ mLeftState = STATE_CLOSED;
+ mRightState = STATE_CLOSED;
+ mLeftPercent = 0.0f;
+ mRightPercent = 0.0f;
+ mMinDrawerMargin = sDefaultMinDrawerMargin;
+ mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
+ mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mAnimator.addUpdateListener(this);
+ mAnimator.addListener(this);
+ mCancelAnimation = false;
+
+ setWillNotDraw(false);
+ }
+
+ public void setDrawerListener(DrawerListener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Close all currently open drawer views by animating them out of view.
+ */
+ public void closeDrawers() {
+ closeDrawer(mLeftDrawer);
+ closeDrawer(mRightDrawer);
+ }
+
+ /**
+ * Open the specified drawer view by animating it into view.
+ *
+ * @param drawerView
+ * Drawer view to open
+ */
+ public void openDrawer(View drawerView) {
+ if (drawerView == null || (drawerView != mLeftDrawer && drawerView != mRightDrawer))
+ return;
+
+ boolean isLeft = drawerView == mLeftDrawer;
+ switch (isLeft ? mLeftState : mRightState) {
+ case STATE_CLOSED:
+ // Check other side first
+ switch (isLeft ? mRightState : mLeftState) {
+ case STATE_CLOSED:
+ startAnimation(isLeft, true);
+ break;
+ case STATE_SLIDING:
+ if (mAnimator.isRunning()) {
+ if (mToOpen) {
+ cancelAnimation();
+ mOpenTask = isLeft ? mLeftDrawer : mRightDrawer;
+ startAnimation(!isLeft, false);
+ } else {
+ mOpenTask = isLeft ? mLeftDrawer : mRightDrawer;
+ }
+ } else {
+ // You finger is on Screen!
+ }
+ break;
+ case STATE_OPENED:
+ // Close other side first
+ mOpenTask = isLeft ? mLeftDrawer : mRightDrawer;
+ startAnimation(!isLeft, false);
+ break;
+ }
+ break;
+
+ case STATE_SLIDING:
+ if (mAnimator.isRunning()) {
+ if (mToOpen) {
+ // Same purpose
+ } else {
+ cancelAnimation();
+ startAnimation(isLeft, true);
+ }
+ } else {
+ // You finger is on Screen!
+ }
+ break;
+
+ case STATE_OPENED:
+ // Ok it is opened
+ break;
+ }
+ }
+
+ public void openDrawer(int gravity) {
+ if (gravity == Gravity.LEFT)
+ openDrawer(mLeftDrawer);
+ else if (gravity == Gravity.RIGHT)
+ openDrawer(mRightDrawer);
+ }
+
+ /**
+ * Close the specified drawer view by animating it into view.
+ *
+ * @param drawerView
+ * Drawer view to close
+ */
+ public void closeDrawer(View drawerView) {
+ if (drawerView == null || (drawerView != mLeftDrawer && drawerView != mRightDrawer))
+ return;
+
+ boolean isLeft = drawerView == mLeftDrawer;
+ switch (isLeft ? mLeftState : mRightState) {
+ case STATE_CLOSED:
+ // Ok it is closed
+ break;
+ case STATE_SLIDING:
+ if (mAnimator.isRunning()) {
+ if (mToOpen) {
+ cancelAnimation();
+ startAnimation(isLeft, false);
+ } else {
+ // Same purpose
+ }
+ } else {
+ // You finger is on Screen!
+ }
+ break;
+ case STATE_OPENED:
+ startAnimation(isLeft, false);
+ break;
+ }
+ }
+
+ public void closeDrawer(int gravity) {
+ if (gravity == Gravity.LEFT)
+ closeDrawer(mLeftDrawer);
+ else if (gravity == Gravity.RIGHT)
+ closeDrawer(mRightDrawer);
+ }
+
+ public boolean isDrawerOpen(View drawer) {
+ if (drawer == mLeftDrawer)
+ return mLeftOpened;
+ else if (drawer == mRightDrawer)
+ return mRightOpened;
+ else
+ throw new IllegalArgumentException();
+ }
+
+ public boolean isDrawerOpen(int gravity) {
+ if (gravity == Gravity.LEFT)
+ return isDrawerOpen(mLeftDrawer);
+ else if (gravity == Gravity.RIGHT)
+ return isDrawerOpen(mRightDrawer);
+ else
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final int absGravity = GravityCompat.getAbsoluteGravity(
+ ((LayoutParams) child.getLayoutParams()).gravity,
+ ViewCompat.getLayoutDirection(child));
+
+ if (absGravity == Gravity.NO_GRAVITY) {
+ if (mContentView != null)
+ throw new IllegalStateException("There is more than one content view");
+ mContentView = child;
+ } else if ((absGravity & Gravity.LEFT) == Gravity.LEFT) {
+ if (mLeftDrawer != null)
+ throw new IllegalStateException("There is more than one left menu");
+ mLeftDrawer = child;
+ } else if ((absGravity & Gravity.RIGHT) == Gravity.RIGHT) {
+ if (mRightDrawer != null)
+ throw new IllegalStateException("There is more than one right menu");
+ mRightDrawer = child;
+ } else {
+ throw new IllegalStateException("Child " + child + " at index " + i +
+ " does not have a valid layout_gravity - must be Gravity.LEFT, " +
+ "Gravity.RIGHT or Gravity.NO_GRAVITY");
+ }
+ }
+
+ if (mContentView == null)
+ throw new IllegalStateException("There is no content view");
+ // Material is solid.
+ // Input events cannot pass through material.
+ if (mLeftDrawer != null)
+ mLeftDrawer.setClickable(true);
+ if (mRightDrawer != null)
+ mRightDrawer.setClickable(true);
+ }
+
+ /**
+ * @hide Internal use only; called to apply window insets when configured
+ * with fitsSystemWindows="true"
+ */
+ public void setChildInsets(Object insets, boolean draw) {
+ mLastInsets = insets;
+ mDrawStatusBarBackground = draw;
+ setWillNotDraw(!draw && getBackground() == null);
+ requestLayout();
+ }
+
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY)
+ throw new IllegalArgumentException(
+ "SlidingDrawerLayout must be measured with MeasureSpec.EXACTLY.");
+
+ setMeasuredDimension(widthSize, heightSize);
+
+ final boolean applyInsets = mLastInsets != null && ViewCompat.getFitsSystemWindows(this);
+ final int layoutDirection = ViewCompat.getLayoutDirection(this);
+
+ // Gravity value for each drawer we've seen. Only one of each permitted.
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ if (applyInsets) {
+ final int cgrav = GravityCompat.getAbsoluteGravity(lp.gravity, layoutDirection);
+ if (ViewCompat.getFitsSystemWindows(child)) {
+ IMPL.dispatchChildInsets(child, mLastInsets, cgrav);
+ } else {
+ IMPL.applyMarginInsets(lp, mLastInsets, cgrav);
+ }
+ }
+
+ if (child == mContentView) {
+ // Content views get measured at exactly the layout's size.
+ final int contentWidthSpec = MeasureSpec.makeMeasureSpec(
+ widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
+ final int contentHeightSpec = MeasureSpec.makeMeasureSpec(
+ heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
+ child.measure(contentWidthSpec, contentHeightSpec);
+ } else if (child == mLeftDrawer || child == mRightDrawer) {
+ final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
+ mMinDrawerMargin + lp.leftMargin + lp.rightMargin,
+ lp.width);
+ final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
+ lp.topMargin + lp.bottomMargin,
+ lp.height);
+ child.measure(drawerWidthSpec, drawerHeightSpec);
+ } else {
+ throw new IllegalStateException("Don't call addView");
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ mInLayout = true;
+ final int width = r - l;
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (child == mContentView) {
+ child.layout(lp.leftMargin, lp.topMargin,
+ lp.leftMargin + child.getMeasuredWidth(),
+ lp.topMargin + child.getMeasuredHeight());
+ } else { // Drawer, if it wasn't onMeasure would have thrown an exception.
+ final int childWidth = child.getMeasuredWidth();
+ final int childHeight = child.getMeasuredHeight();
+ int childLeft;
+ float percent;
+
+ if (child == mLeftDrawer) {
+ percent = mLeftPercent;
+ childLeft = -childWidth + (int) (childWidth * percent);
+ } else { // Right; onMeasure checked for us.
+ percent = mRightPercent;
+ childLeft = width - (int) (childWidth * percent);
+ }
+
+ final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
+
+ switch (vgrav) {
+ default:
+ case Gravity.TOP: {
+ child.layout(childLeft, lp.topMargin, childLeft + childWidth,
+ lp.topMargin + childHeight);
+ break;
+ }
+
+ case Gravity.BOTTOM: {
+ final int height = b - t;
+ child.layout(childLeft,
+ height - lp.bottomMargin - child.getMeasuredHeight(),
+ childLeft + childWidth,
+ height - lp.bottomMargin);
+ break;
+ }
+
+ case Gravity.CENTER_VERTICAL: {
+ final int height = b - t;
+ int childTop = (height - childHeight) / 2;
+
+ // Offset for margins. If things don't fit right because of
+ // bad measurement before, oh well.
+ if (childTop < lp.topMargin) {
+ childTop = lp.topMargin;
+ } else if (childTop + childHeight > height - lp.bottomMargin) {
+ childTop = height - lp.bottomMargin - childHeight;
+ }
+ child.layout(childLeft, childTop, childLeft + childWidth,
+ childTop + childHeight);
+ break;
+ }
+ }
+
+ final int newVisibility = percent > 0 ? VISIBLE : INVISIBLE;
+ if (child.getVisibility() != newVisibility) {
+ child.setVisibility(newVisibility);
+ }
+ }
+ }
+ mInLayout = false;
+ }
+
+ @Override
+ public void requestLayout() {
+ if (!mInLayout) {
+ super.requestLayout();
+ }
+ }
+
+ private boolean isDrawerUnder(int x, int y) {
+ return ViewUtils.isViewUnder(mLeftDrawer, x, y) || ViewUtils.isViewUnder(mRightDrawer, x, y);
+ }
+
+ private boolean hasDrawer() {
+ return mLeftDrawer != null || mRightDrawer != null;
+ }
+
+ private void slideLeftDrawer(int dx) {
+ int leftWidth = mLeftDrawer.getWidth();
+ int oldLeft = mLeftDrawer.getLeft();
+ int newLeft = MathUtils.clamp(oldLeft + dx, -leftWidth, 0);
+ int newVisible = newLeft == -leftWidth ? View.INVISIBLE : View.VISIBLE;
+ if (mLeftDrawer.getVisibility() != newVisible)
+ mLeftDrawer.setVisibility(newVisible);
+ mLeftDrawer.offsetLeftAndRight(newLeft - oldLeft);
+
+ updateDrawerSlide(mLeftDrawer, (newLeft + leftWidth) / (float) leftWidth);
+ }
+
+ private void slideRightDrawer(int dx) {
+ int width = getWidth();
+ int rightWidth = mRightDrawer.getWidth();
+ int oldLeft = mRightDrawer.getLeft();
+ int newLeft = MathUtils.clamp(oldLeft + dx, width - rightWidth, width);
+ int newVisible = newLeft == width ? View.INVISIBLE : View.VISIBLE;
+ if (mRightDrawer.getVisibility() != newVisible)
+ mRightDrawer.setVisibility(newVisible);
+ mRightDrawer.offsetLeftAndRight(newLeft - oldLeft);
+
+ updateDrawerSlide(mRightDrawer, (width - newLeft) / (float) rightWidth);
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float value = (Float) animation.getAnimatedValue();
+ int oldLeft = mTargetView.getLeft();
+ int newLeft = (int) MathUtils.lerp(mStartLeft, mEndLeft, value);
+ if (mTargetView == mLeftDrawer)
+ slideLeftDrawer(newLeft - oldLeft);
+ else
+ slideRightDrawer(newLeft - oldLeft);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ updateDrawerState(mTargetView, STATE_SLIDING);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelAnimation) {
+ updateDrawerSlide(mTargetView, mToOpen ? 1.0f : 0.0f);
+ updateDrawerState(mTargetView, mToOpen ? STATE_OPENED : STATE_CLOSED);
+ if (mToOpen)
+ dispatchOnDrawerOpened(mTargetView);
+ else
+ dispatchOnDrawerClosed(mTargetView);
+
+ if (mOpenTask == null || mToOpen)
+ ;
+ else if (mOpenTask == mLeftDrawer)
+ startAnimation(true, true);
+ else if (mOpenTask == mRightDrawer)
+ startAnimation(true, false);
+
+ } else {
+ mCancelAnimation = false;
+ }
+ mOpenTask = null;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ // Empty
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // Empty
+ }
+
+ private void startAnimation(final boolean isLeft, final boolean isOpen) {
+ mToOpen = isOpen;
+ if (isLeft) {
+ mTargetView = mLeftDrawer;
+ mStartLeft = mLeftDrawer.getLeft();
+ mEndLeft = mToOpen ? 0 : -mLeftDrawer.getWidth();
+ } else {
+ mTargetView = mRightDrawer;
+ mStartLeft = mRightDrawer.getLeft();
+ mEndLeft = mToOpen ? getWidth() - mRightDrawer.getWidth() : getWidth();
+ }
+
+ if (mStartLeft == mEndLeft) {
+ // No need to animate
+ updateDrawerSlide(mTargetView, mToOpen ? 1.0f : 0.0f);
+ updateDrawerState(mTargetView, mToOpen ? STATE_OPENED : STATE_CLOSED);
+ if (mToOpen)
+ dispatchOnDrawerOpened(mTargetView);
+ else
+ dispatchOnDrawerClosed(mTargetView);
+ return;
+ }
+
+ mAnimator.setDuration((long) (Ui.pix2dp(Math.abs(mEndLeft - mStartLeft)) * 2));
+ mAnimator.start();
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+
+ final int action = MotionEventCompat.getActionMasked(ev);
+ final float x = ev.getX();
+ final float y = ev.getY();
+ boolean interceptSlide = false;
+
+ if (!hasDrawer() || isDrawerUnder((int) x, (int) y)) {
+ return false;
+ }
+
+ if ((mLeftState != STATE_CLOSED || mRightState != STATE_CLOSED))
+ return true;
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mIntercepted = false;
+ mInterceptPointNum = 0;
+ mInitialMotionX = x;
+ mInitialMotionY = y;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ final float adx = Math.abs(x - mInitialMotionX);
+ final float ady = Math.abs(y - mInitialMotionY);
+ final int slop = mDragHelper.getTouchSlop();
+ if (adx > slop && adx > (3 * ady))
+ interceptSlide = true;
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mDragHelper.cancel();
+ break;
+ }
+
+ if (mInterceptPointNum > 8)
+ return false;
+ else
+ mInterceptPointNum++;
+
+ mIntercepted = mDragHelper.shouldInterceptTouchEvent(ev) || interceptSlide;
+ return mIntercepted;
+ }
+
+ private void cancelAnimation() {
+ if (mAnimator.isRunning()) {
+ mCancelAnimation = true;
+ mAnimator.cancel();
+ }
+ }
+
+ @Override
+ @SuppressLint("ClickableViewAccessibility")
+ public boolean onTouchEvent(MotionEvent ev) {
+
+ if (!mIntercepted)
+ return false;
+
+ // Cancel animate
+ cancelAnimation();
+
+ final int action = MotionEventCompat.getActionMasked(ev);
+ final float x = ev.getX();
+ final float y = ev.getY();
+
+ if (!hasDrawer() || isDrawerUnder((int) x, (int) y))
+ return false;
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mInitialMotionX = x;
+ mInitialMotionY = y;
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+
+ if (mLeftState == STATE_SLIDING) {
+ if ((mLeftOpened && mLeftPercent < CLOSE_SENSITIVITY) ||
+ (!mLeftOpened && mLeftPercent < OPEN_SENSITIVITY))
+ startAnimation(true, false);
+ else if ((mLeftOpened && mLeftPercent >= CLOSE_SENSITIVITY) ||
+ (!mLeftOpened && mLeftPercent >= OPEN_SENSITIVITY))
+ startAnimation(true, true);
+ } else if (mRightState == STATE_SLIDING) {
+ if ((mRightOpened && mRightPercent < CLOSE_SENSITIVITY) ||
+ (!mRightOpened && mRightPercent < OPEN_SENSITIVITY))
+ startAnimation(false, false);
+ else if ((mRightOpened && mRightPercent >= CLOSE_SENSITIVITY) ||
+ (!mRightOpened && mRightPercent >= OPEN_SENSITIVITY))
+ startAnimation(false, true);
+ }
+ break;
+ }
+
+ mDragHelper.processTouchEvent(ev);
+
+ return true;
+ }
+
+ @Override
+ protected int getChildDrawingOrder(int childCount, int i) {
+ return i;
+ }
+
+ /**
+ * Set a drawable to draw in the insets area for the status bar.
+ * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
+ *
+ * @param bg Background drawable to draw behind the status bar
+ */
+ public void setStatusBarBackground(Drawable bg) {
+ mStatusBarBackground = bg;
+ }
+
+ /**
+ * Set a drawable to draw in the insets area for the status bar.
+ * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
+ *
+ * @param resId Resource id of a background drawable to draw behind the status bar
+ */
+ public void setStatusBarBackground(int resId) {
+ mStatusBarBackground = resId != 0 ? ContextCompat.getDrawable(getContext(), resId) : null;
+ }
+
+ /**
+ * Set a drawable to draw in the insets area for the status bar.
+ * Note that this will only be activated if this DrawerLayout fitsSystemWindows.
+ *
+ * @param color Color to use as a background drawable to draw behind the status bar
+ * in 0xAARRGGBB format.
+ */
+ public void setStatusBarBackgroundColor(int color) {
+ mStatusBarBackground = new ColorDrawable(color);
+ }
+
+ @Override
+ public void onDraw(Canvas c) {
+ super.onDraw(c);
+ if (mDrawStatusBarBackground && mStatusBarBackground != null) {
+ final int inset = IMPL.getTopInset(mLastInsets);
+ if (inset > 0) {
+ mStatusBarBackground.setBounds(0, 0, getWidth(), inset);
+ mStatusBarBackground.draw(c);
+ }
+ }
+ }
+
+ private void updateDrawerSlide(View drawerView, float percent) {
+ boolean update = false;
+ // Update percent
+ if (drawerView == mLeftDrawer) {
+ update = mLeftPercent != percent;
+ mLeftPercent = percent;
+ } else if (drawerView == mRightDrawer) {
+ update = mRightPercent != percent;
+ mRightPercent = percent;
+ }
+
+ // Callback
+ if (update && mListener != null) {
+ mListener.onDrawerSlide(drawerView, percent);
+ }
+ }
+
+ private void updateDrawerState(View drawerView, int state) {
+ boolean update = false;
+ // Update state
+ if (drawerView == mLeftDrawer) {
+ update = mLeftState != state;
+ mLeftState = state;
+ } else if (drawerView == mRightDrawer) {
+ update = mRightState != state;
+ mRightState = state;
+ }
+
+ // Callback
+ if (update && mListener != null) {
+ mListener.onDrawerStateChanged(drawerView, state);
+ }
+ }
+
+ private void dispatchOnDrawerClosed(View drawerView) {
+ boolean update = false;
+ // Update
+ if (drawerView == mLeftDrawer) {
+ update = mLeftOpened;
+ mLeftOpened = false;
+ } else if (drawerView == mRightDrawer) {
+ update = mRightOpened;
+ mRightOpened = false;
+ }
+
+ // Callback
+ if (update && mListener != null) {
+ mListener.onDrawerOpened(drawerView);
+ }
+ }
+
+ private void dispatchOnDrawerOpened(View drawerView) {
+ boolean update = false;
+ // Update
+ if (drawerView == mLeftDrawer) {
+ update = !mLeftOpened;
+ mLeftOpened = true;
+ } else if (drawerView == mRightDrawer) {
+ update = !mRightOpened;
+ mRightOpened = true;
+ }
+
+ // Callback
+ if (update && mListener != null) {
+ mListener.onDrawerOpened(drawerView);
+ }
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams
+ ? new LayoutParams((LayoutParams) p)
+ : p instanceof ViewGroup.MarginLayoutParams
+ ? new LayoutParams((MarginLayoutParams) p)
+ : new LayoutParams(p);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams && super.checkLayoutParams(p);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ public static class LayoutParams extends ViewGroup.MarginLayoutParams {
+
+ public int gravity = Gravity.NO_GRAVITY;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
+ this.gravity = a.getInt(0, Gravity.NO_GRAVITY);
+ a.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(int width, int height, int gravity) {
+ this(width, height);
+ this.gravity = gravity;
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+ this.gravity = source.gravity;
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(ViewGroup.MarginLayoutParams source) {
+ super(source);
+ }
+ }
+
+ private class DragHelperCallback extends ViewDragHelper.Callback {
+
+ @Override
+ public boolean tryCaptureView(View child, int pointerId) {
+ return child == mContentView;
+ }
+
+ @Override
+ public int clampViewPositionVertical(View child, int top, int dy) {
+ return child.getTop();
+ }
+
+ @Override
+ public int clampViewPositionHorizontal(View child, int left, int dx) {
+
+ if (!mIntercepted)
+ return child.getLeft();
+
+ if (mLeftState == STATE_CLOSED && mRightState == STATE_CLOSED) {
+ if (dx > 0 && mLeftDrawer != null) {
+ slideLeftDrawer(dx);
+ updateDrawerState(mLeftDrawer, STATE_SLIDING);
+ } else if (dx < 0 && mRightDrawer != null) {
+ slideRightDrawer(dx);
+ updateDrawerState(mRightDrawer, STATE_SLIDING);
+ }
+ } else if (mLeftState != STATE_CLOSED) {
+ slideLeftDrawer(dx);
+ updateDrawerState(mLeftDrawer, STATE_SLIDING);
+ } else if (mRightState != STATE_CLOSED) {
+ slideRightDrawer(dx);
+ updateDrawerState(mRightDrawer, STATE_SLIDING);
+ }
+
+ return child.getLeft();
+ }
+
+ @Override
+ public int getViewHorizontalDragRange(View child) {
+ return Integer.MAX_VALUE;
+ }
+ }
+}