Files
EhViewer/src/com/hippo/ehviewer/widget/SlidingDrawerLayout.java
T
2014-11-10 21:59:18 +08:00

1030 lines
35 KiB
Java

/*
* 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)) * 1.5));
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 > (2 * ady))
interceptSlide = true;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mDragHelper.cancel();
break;
}
if (++mInterceptPointNum > 8)
return false;
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) {
final int action = MotionEventCompat.getActionMasked(ev);
final float x = ev.getX();
final float y = ev.getY();
if (action == MotionEvent.ACTION_DOWN) {
mInterceptPointNum = 1;
mIntercepted = true;
}
if (!mIntercepted)
return false;
// Cancel animate
cancelAnimation();
mDragHelper.processTouchEvent(ev);
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:
mInterceptPointNum = 0;
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;
}
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;
}
}
}