FileDocCategorySizeDatePackage
SlidingDrawer.javaAPI DocAndroid 5.1 API35283Thu Mar 12 22:22:10 GMT 2015android.widget

SlidingDrawer

public class SlidingDrawer extends android.view.ViewGroup
SlidingDrawer hides content out of the screen and allows the user to drag a handle to bring the content on screen. SlidingDrawer can be used vertically or horizontally. A special widget composed of two children views: the handle, that the users drags, and the content, attached to the handle and dragged with it. SlidingDrawer should be used as an overlay inside layouts. This means SlidingDrawer should only be used inside of a FrameLayout or a RelativeLayout for instance. The size of the SlidingDrawer defines how much space the content will occupy once slid out so SlidingDrawer should usually use match_parent for both its dimensions. Inside an XML layout, SlidingDrawer must define the id of the handle and of the content:
<SlidingDrawer
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"

android:handle="@+id/handle"
android:content="@+id/content">

<ImageView
android:id="@id/handle"
android:layout_width="88dip"
android:layout_height="44dip" />

<GridView
android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</SlidingDrawer>
attr
ref android.R.styleable#SlidingDrawer_content
attr
ref android.R.styleable#SlidingDrawer_handle
attr
ref android.R.styleable#SlidingDrawer_topOffset
attr
ref android.R.styleable#SlidingDrawer_bottomOffset
attr
ref android.R.styleable#SlidingDrawer_orientation
attr
ref android.R.styleable#SlidingDrawer_allowSingleTap
attr
ref android.R.styleable#SlidingDrawer_animateOnClick
deprecated
This class is not supported anymore. It is recommended you base your own implementation on the source code for the Android Open Source Project if you must use it in your application.

Fields Summary
public static final int
ORIENTATION_HORIZONTAL
public static final int
ORIENTATION_VERTICAL
private static final int
TAP_THRESHOLD
private static final float
MAXIMUM_TAP_VELOCITY
private static final float
MAXIMUM_MINOR_VELOCITY
private static final float
MAXIMUM_MAJOR_VELOCITY
private static final float
MAXIMUM_ACCELERATION
private static final int
VELOCITY_UNITS
private static final int
MSG_ANIMATE
private static final int
ANIMATION_FRAME_DURATION
private static final int
EXPANDED_FULL_OPEN
private static final int
COLLAPSED_FULL_CLOSED
private final int
mHandleId
private final int
mContentId
private android.view.View
mHandle
private android.view.View
mContent
private final android.graphics.Rect
mFrame
private final android.graphics.Rect
mInvalidate
private boolean
mTracking
private boolean
mLocked
private android.view.VelocityTracker
mVelocityTracker
private boolean
mVertical
private boolean
mExpanded
private int
mBottomOffset
private int
mTopOffset
private int
mHandleHeight
private int
mHandleWidth
private OnDrawerOpenListener
mOnDrawerOpenListener
private OnDrawerCloseListener
mOnDrawerCloseListener
private OnDrawerScrollListener
mOnDrawerScrollListener
private final android.os.Handler
mHandler
private float
mAnimatedAcceleration
private float
mAnimatedVelocity
private float
mAnimationPosition
private long
mAnimationLastTime
private long
mCurrentAnimationTime
private int
mTouchDelta
private boolean
mAnimating
private boolean
mAllowSingleTap
private boolean
mAnimateOnClick
private final int
mTapThreshold
private final int
mMaximumTapVelocity
private final int
mMaximumMinorVelocity
private final int
mMaximumMajorVelocity
private final int
mMaximumAcceleration
private final int
mVelocityUnits
Constructors Summary
public SlidingDrawer(android.content.Context context, android.util.AttributeSet attrs)
Creates a new SlidingDrawer from a specified set of attributes defined in XML.

param
context The application's environment.
param
attrs The attributes defined in XML.


                
        
                        
          
    

                
        
                        
          
    

                
        
                          
          

                          
          
    

                                  
         
        this(context, attrs, 0);
    
public SlidingDrawer(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)
Creates a new SlidingDrawer from a specified set of attributes defined in XML.

param
context The application's environment.
param
attrs The attributes defined in XML.
param
defStyleAttr An attribute in the current theme that contains a reference to a style resource that supplies default values for the view. Can be 0 to not look for defaults.

        this(context, attrs, defStyleAttr, 0);
    
public SlidingDrawer(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)
Creates a new SlidingDrawer from a specified set of attributes defined in XML.

param
context The application's environment.
param
attrs The attributes defined in XML.
param
defStyleAttr An attribute in the current theme that contains a reference to a style resource that supplies default values for the view. Can be 0 to not look for defaults.
param
defStyleRes A resource identifier of a style resource that supplies default values for the view, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults.

        super(context, attrs, defStyleAttr, defStyleRes);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, R.styleable.SlidingDrawer, defStyleAttr, defStyleRes);

        int orientation = a.getInt(R.styleable.SlidingDrawer_orientation, ORIENTATION_VERTICAL);
        mVertical = orientation == ORIENTATION_VERTICAL;
        mBottomOffset = (int) a.getDimension(R.styleable.SlidingDrawer_bottomOffset, 0.0f);
        mTopOffset = (int) a.getDimension(R.styleable.SlidingDrawer_topOffset, 0.0f);
        mAllowSingleTap = a.getBoolean(R.styleable.SlidingDrawer_allowSingleTap, true);
        mAnimateOnClick = a.getBoolean(R.styleable.SlidingDrawer_animateOnClick, true);

        int handleId = a.getResourceId(R.styleable.SlidingDrawer_handle, 0);
        if (handleId == 0) {
            throw new IllegalArgumentException("The handle attribute is required and must refer "
                    + "to a valid child.");
        }

        int contentId = a.getResourceId(R.styleable.SlidingDrawer_content, 0);
        if (contentId == 0) {
            throw new IllegalArgumentException("The content attribute is required and must refer "
                    + "to a valid child.");
        }

        if (handleId == contentId) {
            throw new IllegalArgumentException("The content and handle attributes must refer "
                    + "to different children.");
        }

        mHandleId = handleId;
        mContentId = contentId;

        final float density = getResources().getDisplayMetrics().density;
        mTapThreshold = (int) (TAP_THRESHOLD * density + 0.5f);
        mMaximumTapVelocity = (int) (MAXIMUM_TAP_VELOCITY * density + 0.5f);
        mMaximumMinorVelocity = (int) (MAXIMUM_MINOR_VELOCITY * density + 0.5f);
        mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f);
        mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f);
        mVelocityUnits = (int) (VELOCITY_UNITS * density + 0.5f);

        a.recycle();

        setAlwaysDrawnWithCacheEnabled(false);
    
Methods Summary
private voidanimateClose(int position)

        prepareTracking(position);
        performFling(position, mMaximumAcceleration, true);
    
public voidanimateClose()
Closes the drawer with an animation.

see
#close()
see
#open()
see
#animateOpen()
see
#animateToggle()
see
#toggle()

        prepareContent();
        final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;
        if (scrollListener != null) {
            scrollListener.onScrollStarted();
        }
        animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft());

        if (scrollListener != null) {
            scrollListener.onScrollEnded();
        }
    
private voidanimateOpen(int position)

        prepareTracking(position);
        performFling(position, -mMaximumAcceleration, true);
    
public voidanimateOpen()
Opens the drawer with an animation.

see
#close()
see
#open()
see
#animateClose()
see
#animateToggle()
see
#toggle()

        prepareContent();
        final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;
        if (scrollListener != null) {
            scrollListener.onScrollStarted();
        }
        animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft());

        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);

        if (scrollListener != null) {
            scrollListener.onScrollEnded();
        }
    
public voidanimateToggle()
Toggles the drawer open and close with an animation.

see
#open()
see
#close()
see
#animateClose()
see
#animateOpen()
see
#toggle()

        if (!mExpanded) {
            animateOpen();
        } else {
            animateClose();
        }
    
public voidclose()
Closes the drawer immediately.

see
#toggle()
see
#open()
see
#animateClose()

        closeDrawer();
        invalidate();
        requestLayout();
    
private voidcloseDrawer()

        moveHandle(COLLAPSED_FULL_CLOSED);
        mContent.setVisibility(View.GONE);
        mContent.destroyDrawingCache();

        if (!mExpanded) {
            return;
        }

        mExpanded = false;
        if (mOnDrawerCloseListener != null) {
            mOnDrawerCloseListener.onDrawerClosed();
        }
    
protected voiddispatchDraw(android.graphics.Canvas canvas)

        final long drawingTime = getDrawingTime();
        final View handle = mHandle;
        final boolean isVertical = mVertical;

        drawChild(canvas, handle, drawingTime);

        if (mTracking || mAnimating) {
            final Bitmap cache = mContent.getDrawingCache();
            if (cache != null) {
                if (isVertical) {
                    canvas.drawBitmap(cache, 0, handle.getBottom(), null);
                } else {
                    canvas.drawBitmap(cache, handle.getRight(), 0, null);                    
                }
            } else {
                canvas.save();
                canvas.translate(isVertical ? 0 : handle.getLeft() - mTopOffset,
                        isVertical ? handle.getTop() - mTopOffset : 0);
                drawChild(canvas, mContent, drawingTime);
                canvas.restore();
            }
        } else if (mExpanded) {
            drawChild(canvas, mContent, drawingTime);
        }
    
private voiddoAnimation()

        if (mAnimating) {
            incrementAnimation();
            if (mAnimationPosition >= mBottomOffset + (mVertical ? getHeight() : getWidth()) - 1) {
                mAnimating = false;
                closeDrawer();
            } else if (mAnimationPosition < mTopOffset) {
                mAnimating = false;
                openDrawer();
            } else {
                moveHandle((int) mAnimationPosition);
                mCurrentAnimationTime += ANIMATION_FRAME_DURATION;
                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),
                        mCurrentAnimationTime);
            }
        }
    
public android.view.ViewgetContent()
Returns the content of the drawer.

return
The View reprenseting the content of the drawer, identified by the "content" id in XML.

        return mContent;
    
public android.view.ViewgetHandle()
Returns the handle of the drawer.

return
The View reprenseting the handle of the drawer, identified by the "handle" id in XML.

        return mHandle;
    
private voidincrementAnimation()

        long now = SystemClock.uptimeMillis();
        float t = (now - mAnimationLastTime) / 1000.0f;                   // ms -> s
        final float position = mAnimationPosition;
        final float v = mAnimatedVelocity;                                // px/s
        final float a = mAnimatedAcceleration;                            // px/s/s
        mAnimationPosition = position + (v * t) + (0.5f * a * t * t);     // px
        mAnimatedVelocity = v + (a * t);                                  // px/s
        mAnimationLastTime = now;                                         // ms
    
public booleanisMoving()
Indicates whether the drawer is scrolling or flinging.

return
True if the drawer is scroller or flinging, false otherwise.

        return mTracking || mAnimating;
    
public booleanisOpened()
Indicates whether the drawer is currently fully opened.

return
True if the drawer is opened, false otherwise.

        return mExpanded;
    
public voidlock()
Locks the SlidingDrawer so that touch events are ignores.

see
#unlock()

        mLocked = true;
    
private voidmoveHandle(int position)

        final View handle = mHandle;

        if (mVertical) {
            if (position == EXPANDED_FULL_OPEN) {
                handle.offsetTopAndBottom(mTopOffset - handle.getTop());
                invalidate();
            } else if (position == COLLAPSED_FULL_CLOSED) {
                handle.offsetTopAndBottom(mBottomOffset + mBottom - mTop -
                        mHandleHeight - handle.getTop());
                invalidate();
            } else {
                final int top = handle.getTop();
                int deltaY = position - top;
                if (position < mTopOffset) {
                    deltaY = mTopOffset - top;
                } else if (deltaY > mBottomOffset + mBottom - mTop - mHandleHeight - top) {
                    deltaY = mBottomOffset + mBottom - mTop - mHandleHeight - top;
                }
                handle.offsetTopAndBottom(deltaY);

                final Rect frame = mFrame;
                final Rect region = mInvalidate;

                handle.getHitRect(frame);
                region.set(frame);

                region.union(frame.left, frame.top - deltaY, frame.right, frame.bottom - deltaY);
                region.union(0, frame.bottom - deltaY, getWidth(),
                        frame.bottom - deltaY + mContent.getHeight());

                invalidate(region);
            }
        } else {
            if (position == EXPANDED_FULL_OPEN) {
                handle.offsetLeftAndRight(mTopOffset - handle.getLeft());
                invalidate();
            } else if (position == COLLAPSED_FULL_CLOSED) {
                handle.offsetLeftAndRight(mBottomOffset + mRight - mLeft -
                        mHandleWidth - handle.getLeft());
                invalidate();
            } else {
                final int left = handle.getLeft();
                int deltaX = position - left;
                if (position < mTopOffset) {
                    deltaX = mTopOffset - left;
                } else if (deltaX > mBottomOffset + mRight - mLeft - mHandleWidth - left) {
                    deltaX = mBottomOffset + mRight - mLeft - mHandleWidth - left;
                }
                handle.offsetLeftAndRight(deltaX);

                final Rect frame = mFrame;
                final Rect region = mInvalidate;

                handle.getHitRect(frame);
                region.set(frame);

                region.union(frame.left - deltaX, frame.top, frame.right - deltaX, frame.bottom);
                region.union(frame.right - deltaX, 0,
                        frame.right - deltaX + mContent.getWidth(), getHeight());

                invalidate(region);
            }
        }
    
protected voidonFinishInflate()

        mHandle = findViewById(mHandleId);
        if (mHandle == null) {
            throw new IllegalArgumentException("The handle attribute is must refer to an"
                    + " existing child.");
        }
        mHandle.setOnClickListener(new DrawerToggler());

        mContent = findViewById(mContentId);
        if (mContent == null) {
            throw new IllegalArgumentException("The content attribute is must refer to an" 
                    + " existing child.");
        }
        mContent.setVisibility(View.GONE);
    
public voidonInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)

        super.onInitializeAccessibilityEvent(event);
        event.setClassName(SlidingDrawer.class.getName());
    
public voidonInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo info)

        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(SlidingDrawer.class.getName());
    
public booleanonInterceptTouchEvent(android.view.MotionEvent event)

        if (mLocked) {
            return false;
        }

        final int action = event.getAction();

        float x = event.getX();
        float y = event.getY();

        final Rect frame = mFrame;
        final View handle = mHandle;

        handle.getHitRect(frame);
        if (!mTracking && !frame.contains((int) x, (int) y)) {
            return false;
        }

        if (action == MotionEvent.ACTION_DOWN) {
            mTracking = true;

            handle.setPressed(true);
            // Must be called before prepareTracking()
            prepareContent();

            // Must be called after prepareContent()
            if (mOnDrawerScrollListener != null) {
                mOnDrawerScrollListener.onScrollStarted();
            }

            if (mVertical) {
                final int top = mHandle.getTop();
                mTouchDelta = (int) y - top;
                prepareTracking(top);
            } else {
                final int left = mHandle.getLeft();
                mTouchDelta = (int) x - left;
                prepareTracking(left);
            }
            mVelocityTracker.addMovement(event);
        }

        return true;
    
protected voidonLayout(boolean changed, int l, int t, int r, int b)

        if (mTracking) {
            return;
        }

        final int width = r - l;
        final int height = b - t;

        final View handle = mHandle;

        int childWidth = handle.getMeasuredWidth();
        int childHeight = handle.getMeasuredHeight();

        int childLeft;
        int childTop;

        final View content = mContent;

        if (mVertical) {
            childLeft = (width - childWidth) / 2;
            childTop = mExpanded ? mTopOffset : height - childHeight + mBottomOffset;

            content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(),
                    mTopOffset + childHeight + content.getMeasuredHeight());
        } else {
            childLeft = mExpanded ? mTopOffset : width - childWidth + mBottomOffset;
            childTop = (height - childHeight) / 2;

            content.layout(mTopOffset + childWidth, 0,
                    mTopOffset + childWidth + content.getMeasuredWidth(),
                    content.getMeasuredHeight());            
        }

        handle.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        mHandleHeight = handle.getHeight();
        mHandleWidth = handle.getWidth();
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);

        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);

        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
            throw new RuntimeException("SlidingDrawer cannot have UNSPECIFIED dimensions");
        }

        final View handle = mHandle;
        measureChild(handle, widthMeasureSpec, heightMeasureSpec);

        if (mVertical) {
            int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset;
            mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
        } else {
            int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset;
            mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(heightSpecSize, MeasureSpec.EXACTLY));
        }

        setMeasuredDimension(widthSpecSize, heightSpecSize);
    
public booleanonTouchEvent(android.view.MotionEvent event)

        if (mLocked) {
            return true;
        }

        if (mTracking) {
            mVelocityTracker.addMovement(event);
            final int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_MOVE:
                    moveHandle((int) (mVertical ? event.getY() : event.getX()) - mTouchDelta);
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL: {
                    final VelocityTracker velocityTracker = mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(mVelocityUnits);

                    float yVelocity = velocityTracker.getYVelocity();
                    float xVelocity = velocityTracker.getXVelocity();
                    boolean negative;

                    final boolean vertical = mVertical;
                    if (vertical) {
                        negative = yVelocity < 0;
                        if (xVelocity < 0) {
                            xVelocity = -xVelocity;
                        }
                        if (xVelocity > mMaximumMinorVelocity) {
                            xVelocity = mMaximumMinorVelocity;
                        }
                    } else {
                        negative = xVelocity < 0;
                        if (yVelocity < 0) {
                            yVelocity = -yVelocity;
                        }
                        if (yVelocity > mMaximumMinorVelocity) {
                            yVelocity = mMaximumMinorVelocity;
                        }
                    }

                    float velocity = (float) Math.hypot(xVelocity, yVelocity);
                    if (negative) {
                        velocity = -velocity;
                    }

                    final int top = mHandle.getTop();
                    final int left = mHandle.getLeft();

                    if (Math.abs(velocity) < mMaximumTapVelocity) {
                        if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset) ||
                                (!mExpanded && top > mBottomOffset + mBottom - mTop -
                                        mHandleHeight - mTapThreshold) :
                                (mExpanded && left < mTapThreshold + mTopOffset) ||
                                (!mExpanded && left > mBottomOffset + mRight - mLeft -
                                        mHandleWidth - mTapThreshold)) {

                            if (mAllowSingleTap) {
                                playSoundEffect(SoundEffectConstants.CLICK);

                                if (mExpanded) {
                                    animateClose(vertical ? top : left);
                                } else {
                                    animateOpen(vertical ? top : left);
                                }
                            } else {
                                performFling(vertical ? top : left, velocity, false);
                            }

                        } else {
                            performFling(vertical ? top : left, velocity, false);
                        }
                    } else {
                        performFling(vertical ? top : left, velocity, false);
                    }
                }
                break;
            }
        }

        return mTracking || mAnimating || super.onTouchEvent(event);
    
public voidopen()
Opens the drawer immediately.

see
#toggle()
see
#close()
see
#animateOpen()

        openDrawer();
        invalidate();
        requestLayout();

        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    
private voidopenDrawer()

        moveHandle(EXPANDED_FULL_OPEN);
        mContent.setVisibility(View.VISIBLE);

        if (mExpanded) {
            return;
        }

        mExpanded = true;

        if (mOnDrawerOpenListener != null) {
            mOnDrawerOpenListener.onDrawerOpened();
        }
    
private voidperformFling(int position, float velocity, boolean always)

        mAnimationPosition = position;
        mAnimatedVelocity = velocity;

        if (mExpanded) {
            if (always || (velocity > mMaximumMajorVelocity ||
                    (position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth) &&
                            velocity > -mMaximumMajorVelocity))) {
                // We are expanded, but they didn't move sufficiently to cause
                // us to retract.  Animate back to the expanded position.
                mAnimatedAcceleration = mMaximumAcceleration;
                if (velocity < 0) {
                    mAnimatedVelocity = 0;
                }
            } else {
                // We are expanded and are now going to animate away.
                mAnimatedAcceleration = -mMaximumAcceleration;
                if (velocity > 0) {
                    mAnimatedVelocity = 0;
                }
            }
        } else {
            if (!always && (velocity > mMaximumMajorVelocity ||
                    (position > (mVertical ? getHeight() : getWidth()) / 2 &&
                            velocity > -mMaximumMajorVelocity))) {
                // We are collapsed, and they moved enough to allow us to expand.
                mAnimatedAcceleration = mMaximumAcceleration;
                if (velocity < 0) {
                    mAnimatedVelocity = 0;
                }
            } else {
                // We are collapsed, but they didn't move sufficiently to cause
                // us to retract.  Animate back to the collapsed position.
                mAnimatedAcceleration = -mMaximumAcceleration;
                if (velocity > 0) {
                    mAnimatedVelocity = 0;
                }
            }
        }

        long now = SystemClock.uptimeMillis();
        mAnimationLastTime = now;
        mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
        mAnimating = true;
        mHandler.removeMessages(MSG_ANIMATE);
        mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);
        stopTracking();
    
private voidprepareContent()

        if (mAnimating) {
            return;
        }

        // Something changed in the content, we need to honor the layout request
        // before creating the cached bitmap
        final View content = mContent;
        if (content.isLayoutRequested()) {
            if (mVertical) {
                final int childHeight = mHandleHeight;
                int height = mBottom - mTop - childHeight - mTopOffset;
                content.measure(MeasureSpec.makeMeasureSpec(mRight - mLeft, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
                content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(),
                        mTopOffset + childHeight + content.getMeasuredHeight());
            } else {
                final int childWidth = mHandle.getWidth();
                int width = mRight - mLeft - childWidth - mTopOffset;
                content.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
                        MeasureSpec.makeMeasureSpec(mBottom - mTop, MeasureSpec.EXACTLY));
                content.layout(childWidth + mTopOffset, 0,
                        mTopOffset + childWidth + content.getMeasuredWidth(),
                        content.getMeasuredHeight());
            }
        }
        // Try only once... we should really loop but it's not a big deal
        // if the draw was cancelled, it will only be temporary anyway
        content.getViewTreeObserver().dispatchOnPreDraw();
        if (!content.isHardwareAccelerated()) content.buildDrawingCache();

        content.setVisibility(View.GONE);        
    
private voidprepareTracking(int position)

        mTracking = true;
        mVelocityTracker = VelocityTracker.obtain();
        boolean opening = !mExpanded;
        if (opening) {
            mAnimatedAcceleration = mMaximumAcceleration;
            mAnimatedVelocity = mMaximumMajorVelocity;
            mAnimationPosition = mBottomOffset +
                    (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth);
            moveHandle((int) mAnimationPosition);
            mAnimating = true;
            mHandler.removeMessages(MSG_ANIMATE);
            long now = SystemClock.uptimeMillis();
            mAnimationLastTime = now;
            mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
            mAnimating = true;
        } else {
            if (mAnimating) {
                mAnimating = false;
                mHandler.removeMessages(MSG_ANIMATE);
            }
            moveHandle(position);
        }
    
public voidsetOnDrawerCloseListener(android.widget.SlidingDrawer$OnDrawerCloseListener onDrawerCloseListener)
Sets the listener that receives a notification when the drawer becomes close.

param
onDrawerCloseListener The listener to be notified when the drawer is closed.

        mOnDrawerCloseListener = onDrawerCloseListener;
    
public voidsetOnDrawerOpenListener(android.widget.SlidingDrawer$OnDrawerOpenListener onDrawerOpenListener)
Sets the listener that receives a notification when the drawer becomes open.

param
onDrawerOpenListener The listener to be notified when the drawer is opened.

        mOnDrawerOpenListener = onDrawerOpenListener;
    
public voidsetOnDrawerScrollListener(android.widget.SlidingDrawer$OnDrawerScrollListener onDrawerScrollListener)
Sets the listener that receives a notification when the drawer starts or ends a scroll. A fling is considered as a scroll. A fling will also trigger a drawer opened or drawer closed event.

param
onDrawerScrollListener The listener to be notified when scrolling starts or stops.

        mOnDrawerScrollListener = onDrawerScrollListener;
    
private voidstopTracking()

        mHandle.setPressed(false);
        mTracking = false;

        if (mOnDrawerScrollListener != null) {
            mOnDrawerScrollListener.onScrollEnded();
        }

        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    
public voidtoggle()
Toggles the drawer open and close. Takes effect immediately.

see
#open()
see
#close()
see
#animateClose()
see
#animateOpen()
see
#animateToggle()

        if (!mExpanded) {
            openDrawer();
        } else {
            closeDrawer();
        }
        invalidate();
        requestLayout();
    
public voidunlock()
Unlocks the SlidingDrawer so that touch events are processed.

see
#lock()

        mLocked = false;