Wednesday, April 24, 2013

Android-SemiOpened Top Sliding Drawer

By customising the multi direction sliding Drawer(Previous post) we can get semi opened top menu sliding drawer. For this one we have to customise the multi direction drwer class like this:--


package com.infinum.groundlink.custom;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.LinearLayout;

import com.infinum.groundlink.R;


/**
 * TODO Class description goes here.
 * @version
 */
public class TopMenuSlidingDrawer extends ViewGroup {

/**
*
*/
public static final int ORIENTATION_RTL        = 0;
/**
*
*/
public static final int ORIENTATION_BTT        = 1;
/**
*
*/
public static final int ORIENTATION_LTR        = 2;
/**
*
*/
public static final int ORIENTATION_TTB        = 3;
/**
*
*/
private static final int TAP_THRESHOLD = 6;
/**
*
*/
private static final float MAXIMUM_TAP_VELOCITY = 100.0f;
/**
*
*/
private static final float MAXIMUM_MINOR_VELOCITY = 150.0f;
/**
*
*/
private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;
/**
*
*/
private static final float MAXIMUM_ACCELERATION = 2000.0f;
/**
*
*/
private static final int VELOCITY_UNITS = 1000;
/**
*
*/
private static final int MSG_ANIMATE = 1000;
/**
*
*/
private static final int ANIMATION_FRAME_DURATION = 1000 / 60;
/**
*
*/
private static final int EXPANDED_FULL_OPEN = -10001;
/**
*
*/
private static final int            COLLAPSED_SEMI_CLOSED       = -10003;
private final int mHandleId;
private final int mContentId;
private View mHandle;
private View mContent;
private final Rect mFrame = new Rect();
private final Rect mInvalidate = new Rect();
private boolean mTracking;
private boolean mLocked;
private VelocityTracker mVelocityTracker;
private boolean mInvert;
private boolean mVertical;
private boolean mExpanded;
private int mBottomOffset;
private int mTopOffset;
private int mHandleHeight;
private int mHandleWidth;
private int                         mSemiClosedContentSize;
private OnTopDrawerOpenListener mOnDrawerOpenListener;
private OnTopDrawerCloseListener mOnDrawerCloseListener;
private OnDrawerScrollListener    mOnDrawerScrollListener;
private final Handler mHandler = new SlidingHandler();
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 int mMaximumMinorVelocity;
private int mMaximumMajorVelocity;
private int mMaximumAcceleration;
private final int mVelocityUnits;


/**
* Callback invoked when the drawer is opened.
*/
public static interface OnTopDrawerOpenListener {

/**
* Invoked when the drawer becomes fully open.
*/
public void onDrawerOpened();
}

/**
* Callback invoked when the drawer is closed.
*/
public static interface OnTopDrawerCloseListener {

/**
* Invoked when the drawer becomes fully closed.
*/
public void onDrawerClosed();
}

/**
* Callback invoked when the drawer is scrolled.
*/
public static interface OnDrawerScrollListener {

/**
* Invoked when the user starts dragging/flinging the drawer's handle.
*/
public void onScrollStarted();

/**
* Invoked when the user stops dragging/flinging the drawer's handle.
*/
public void onScrollEnded();

/**
* Invoked when the user is about to close the drawer.
*/
public void onScrollBeforeClose();

}

/**
* 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.
*/
public TopMenuSlidingDrawer( Context context, AttributeSet attrs )
{
this( context, attrs, 0 );

}

/**
* 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 defStyle
*           The style to apply to this widget.
*/
public TopMenuSlidingDrawer( Context context, AttributeSet attrs, int defStyle )
{
super( context, attrs, defStyle );
TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.TopMenuSlidingDrawer, defStyle, 0 );

int orientation = a.getInt( R.styleable.TopMenuSlidingDrawer_direction, ORIENTATION_BTT );
mVertical = ( orientation == ORIENTATION_BTT || orientation == ORIENTATION_TTB );
mBottomOffset = (int)a.getDimension( R.styleable.TopMenuSlidingDrawer_bottomOffset, 0.0f );
mTopOffset = (int)a.getDimension( R.styleable.TopMenuSlidingDrawer_topOffset, 0.0f );
mAllowSingleTap = a.getBoolean( R.styleable.TopMenuSlidingDrawer_allowSingleTap, true );
mAnimateOnClick = a.getBoolean( R.styleable.TopMenuSlidingDrawer_animateOnClick, true );
mInvert = ( orientation == ORIENTATION_TTB || orientation == ORIENTATION_LTR );
mSemiClosedContentSize = (int) a.getDimension(R.styleable.TopMenuSlidingDrawer_semiClosedContentSize, 0.0f);

int handleId = a.getResourceId( R.styleable.TopMenuSlidingDrawer_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.TopMenuSlidingDrawer_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 );

if( mInvert ) {
mMaximumAcceleration = -mMaximumAcceleration;
mMaximumMajorVelocity = -mMaximumMajorVelocity;
mMaximumMinorVelocity = -mMaximumMinorVelocity;
}

a.recycle();
setAlwaysDrawnWithCacheEnabled( false );
}

/**
* TODO Method Description as a Single Sentence.
*
*/
@Override
protected void onFinishInflate()
{
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.VISIBLE );
}

/**
* TODO Method Description as a Single Sentence.
*
* @param widthMeasureSpec int
* @param heightMeasureSpec int
*/
@Override
protected void onMeasure( 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;
final View content =mContent;
measureChild( handle, widthMeasureSpec, heightMeasureSpec );


if ( mVertical ) {

int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset;
content.measure(widthMeasureSpec,MeasureSpec.makeMeasureSpec(height,heightSpecMode));
heightSpecSize = handle.getMeasuredHeight() + mTopOffset + content.getMeasuredHeight();
widthSpecSize = content.getMeasuredWidth();
if (handle.getMeasuredWidth() > widthSpecSize){widthSpecSize = handle.getMeasuredWidth();}

} else {
int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset;
content.measure(MeasureSpec.makeMeasureSpec(width, widthSpecMode), heightMeasureSpec);
widthSpecSize = handle.getMeasuredWidth() + mTopOffset + content.getMeasuredWidth();
heightSpecSize = content.getMeasuredHeight();
if (handle.getMeasuredHeight() > heightSpecSize) {heightSpecSize = handle.getMeasuredHeight();}
        }

setMeasuredDimension( widthSpecSize, heightSpecSize );
}

/**
* TODO Method Description as a Single Sentence.
*
* @param canvas Canvas
*/
@Override
protected void dispatchDraw( Canvas canvas )
{
final long drawingTime = getDrawingTime();
final View handle = mHandle;
final boolean isVertical = mVertical;

drawChild( canvas, handle, drawingTime );

if ( mTracking || mAnimating || !mExpanded) {
final Bitmap cache = mContent.getDrawingCache();
if ( cache != null ) {

if ( isVertical ) {
if( mInvert ) {
canvas.drawBitmap( cache, 0, handle.getTop() - (getBottom() - getTop()) + mHandleHeight, null );
} else {
canvas.drawBitmap( cache, 0, handle.getBottom(), null );
}
} else {
canvas.drawBitmap( cache, mInvert ? handle.getLeft() - cache.getWidth() : handle.getRight(), 0, null );
}
} else {
canvas.save();

if( mInvert ) {

canvas.translate( isVertical ? 0 : handle.getLeft() - mTopOffset - mContent.getMeasuredWidth(), isVertical ? handle.getTop() - mTopOffset - mContent.getMeasuredHeight(): 0 );
} else {

canvas.translate( isVertical ? 0 : handle.getLeft() - mTopOffset, isVertical ? handle.getTop() - mTopOffset : 0 );
}
drawChild( canvas, mContent, drawingTime );
canvas.restore();
}
invalidate();
} else if ( mExpanded ) {
drawChild( canvas, mContent, drawingTime );
}
}

/**
*
*/
public static final String LOG_TAG = "Sliding";

/**
* TODO Method Description as a Single Sentence.
*
* @param changed boolean
* @param l int
* @param t int
* @param r int
* @param b int
*/
@Override
protected void onLayout( boolean changed, int l, int t, int r, int b )
{
Log.d(LOG_TAG, "topmenu::onlayout");
if ( mTracking ) { return; }

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

final View handle = mHandle;

int handleWidth = handle.getMeasuredWidth();
int handleHeight = handle.getMeasuredHeight();

Log.d( LOG_TAG, "handleHeight: " + handleHeight );

int handleLeft;
int handleTop;

final View content = mContent;

if ( mVertical ) {

handleLeft = ( width - handleWidth ) / 2;
if ( mInvert ) {
handleTop = mExpanded ? height - mBottomOffset - handleHeight : mTopOffset+ mSemiClosedContentSize ;
Log.d( LOG_TAG, "content.layout(1)" +handleTop+handleHeight);
content.layout( 0, mTopOffset, content.getMeasuredWidth(), mTopOffset + content.getMeasuredHeight() );

} else {
handleTop = mExpanded ? mTopOffset : height - handleHeight + mBottomOffset+ mSemiClosedContentSize;
content.layout( 0, mTopOffset + handleHeight, content.getMeasuredWidth(), mTopOffset + handleHeight + content.getMeasuredHeight() );
}
} else {
handleTop = ( height - handleHeight ) / 2;
if( mInvert ) {
handleLeft = mExpanded ? width - mBottomOffset - handleWidth : mTopOffset+ mSemiClosedContentSize;
content.layout( mTopOffset, 0, mTopOffset + content.getMeasuredWidth(), content.getMeasuredHeight() );
} else {
handleLeft = mExpanded ? mTopOffset : width - handleWidth + mBottomOffset + mSemiClosedContentSize;
content.layout( mTopOffset + handleWidth, 0, mTopOffset + handleWidth + content.getMeasuredWidth(), content.getMeasuredHeight() );
}
}

handle.layout( handleLeft, handleTop, handleLeft + handleWidth, handleTop + handleHeight );
mHandleHeight = handle.getHeight();
mHandleWidth = handle.getWidth();

}

/**
* TODO Method Description as a Single Sentence.
*
* @param event MotionEvent
* @return boolean boolean
*/
@Override
public boolean onInterceptTouchEvent( 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 after prepareContent()
if ( mOnDrawerScrollListener != null ) {
mOnDrawerScrollListener.onScrollStarted();
}
// Must be called before prepareTracking()
prepareContent();



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;
}

/**
* TODO Method Description as a Single Sentence.
*
*/
public void hideHandle(){
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0,0);
mHandle.setLayoutParams(params);
}

/**
* TODO Method Description as a Single Sentence.
*
* @param event MotionEvent
* @return boolean boolean
*/
@Override
public boolean onTouchEvent( 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 ( (!mInvert && xVelocity > mMaximumMinorVelocity) || (mInvert && xVelocity < mMaximumMinorVelocity) ) {
xVelocity = mMaximumMinorVelocity;
}
} else {
negative = xVelocity < 0;
if ( yVelocity < 0 ) {
yVelocity = -yVelocity;
}

if ( (!mInvert && yVelocity > mMaximumMinorVelocity) || (mInvert && yVelocity < mMaximumMinorVelocity) ) {
yVelocity = mMaximumMinorVelocity;
}
}

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

final int handleTop = mHandle.getTop();
final int handleLeft = mHandle.getLeft();
final int handleBottom = mHandle.getBottom();
final int handleRight = mHandle.getRight();

if ( Math.abs( velocity ) < mMaximumTapVelocity ) {
boolean c1;
boolean c2;
boolean c3;
boolean c4;

if( mInvert ) {

c1 = ( mExpanded && (getBottom() - handleBottom ) < mTapThreshold + mBottomOffset );
c2 = ( !mExpanded && handleTop < mTopOffset + mHandleHeight - mTapThreshold + mSemiClosedContentSize );
c3 = ( mExpanded && (getRight() - handleRight ) < mTapThreshold + mBottomOffset );
c4 = ( !mExpanded && handleLeft > mTopOffset + mHandleWidth + mTapThreshold + mSemiClosedContentSize );
} else {

c1 = ( mExpanded && handleTop < mTapThreshold + mTopOffset );
c2 = ( !mExpanded && handleTop > mBottomOffset + getBottom() - getTop() - mHandleHeight + mSemiClosedContentSize-mTapThreshold );
c3 = ( mExpanded && handleLeft < mTapThreshold + mTopOffset );
c4 = ( !mExpanded && handleLeft > mBottomOffset + getRight() - getLeft() - mHandleWidth + mSemiClosedContentSize-mTapThreshold );
}

Log.d( LOG_TAG, "ACTION_UP: " + "c1: " + c1 + ", c2: " + c2 + ", c3: " + c3 + ", c4: " + c4 );

if ( vertical ? c1 || c2 : c3 || c4 ) {

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

if ( mExpanded ) {
animateClose( vertical ? handleTop : handleLeft );
} else {
animateOpen( vertical ? handleTop : handleLeft );
}
} else {
performFling( vertical ? handleTop : handleLeft, velocity, false );
}
} else {
performFling( vertical ? handleTop : handleLeft, velocity, false );
}
} else {
performFling( vertical ? handleTop : handleLeft, velocity, false );
}
}
break;
}
}

return mTracking || mAnimating || super.onTouchEvent( event );
}

/**
* @param position int
*/
private void animateClose( int position )
{
prepareTracking( position );
performFling( position, mMaximumAcceleration, true );
}

/**
* @param position int
*/
private void animateOpen( int position )
{
prepareTracking( position );
performFling( position, -mMaximumAcceleration, true );
}

/**
* TODO Method Description as a Single Sentence.
*
* @param position int
* @param velocity float
* @param always boolean
*/
private void performFling( int position, float velocity, boolean always )
{
mAnimationPosition = position;
mAnimatedVelocity = velocity;

boolean c1;
boolean c2;
boolean c3;

if ( mExpanded )
{
int bottom = mVertical ? getBottom() : getRight();
int handleHeight = mVertical ? mHandleHeight : mHandleWidth;

Log.d( LOG_TAG, "position: " + position + ", velocity: " + velocity + ", mMaximumMajorVelocity: " + mMaximumMajorVelocity );
c1 = mInvert ? velocity < mMaximumMajorVelocity : velocity > mMaximumMajorVelocity;
c2 = mInvert ? ( bottom - (position + handleHeight) ) + mBottomOffset > handleHeight : position > mTopOffset + ( mVertical ? mHandleHeight : mHandleWidth );
c3 = mInvert ? velocity < -mMaximumMajorVelocity : velocity > -mMaximumMajorVelocity;
Log.d( LOG_TAG, "EXPANDED. c1: " + c1 + ", c2: " + c2 + ", c3: " + c3 );
if ( always || ( c1 || ( c2 && c3 ) ) ) {
// We are expanded, So animate to CLOSE!
mAnimatedAcceleration = mMaximumAcceleration;
if( mInvert )
{
if ( velocity > 0 ) {
mAnimatedVelocity = 0;
}
} else {
if ( velocity < 0 ) {
mAnimatedVelocity = 0;
}
}
} else {
// We are expanded, but they didn't move sufficiently to cause
// us to retract. Animate back to the expanded position. so animate BACK to expanded!
mAnimatedAcceleration = -mMaximumAcceleration;

if( mInvert ) {
if ( velocity < 0 ) {
mAnimatedVelocity = 0;
}
} else {
if ( velocity > 0 ) {
mAnimatedVelocity = 0;
}
}
}
} else {

// WE'RE COLLAPSED

c1 = mInvert ? velocity < mMaximumMajorVelocity : velocity > mMaximumMajorVelocity;
c2 = mInvert ? ( position < ( mVertical ? getHeight() : getWidth() ) / 2 ) : ( position > ( mVertical ? getHeight() : getWidth() ) / 2 );
c3 = mInvert ? velocity < -mMaximumMajorVelocity : velocity > -mMaximumMajorVelocity;

Log.d( LOG_TAG, "COLLAPSED. position: " + position + ", velocity: " + velocity + ", mMaximumMajorVelocity: " + mMaximumMajorVelocity );
Log.d( LOG_TAG, "COLLAPSED. always: " + always + ", c1: " + c1 + ", c2: " + c2 + ", c3: " + c3 );

if ( !always && ( c1 || ( c2 && c3 ) ) ) {
mAnimatedAcceleration = mMaximumAcceleration;

if( mInvert ) {
if ( velocity > 0 ) {
mAnimatedVelocity = 0;
}
} else {
if ( velocity < 0 ) {
mAnimatedVelocity = 0;
}
}
} else {
mAnimatedAcceleration = -mMaximumAcceleration;

if( mInvert ) {
if ( velocity < 0 ) {
mAnimatedVelocity = 0;
}
} else {
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();
}

/**
* TODO Method Description as a Single Sentence.
*
* @param position int
*/
private void prepareTracking( int position )
{
mTracking = true;
mVelocityTracker = VelocityTracker.obtain();
boolean opening = !mExpanded;

if ( opening ) {
mAnimatedAcceleration = mMaximumAcceleration;
mAnimatedVelocity = mMaximumMajorVelocity;
if( mInvert ){
mAnimationPosition = mTopOffset + mSemiClosedContentSize;}
else{
mAnimationPosition = mBottomOffset + ( mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth ) + mSemiClosedContentSize;
}
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 );
}
}

/**
* TODO Method Description as a Single Sentence.
*
* @param position int
*/
private void moveHandle( int position )
{
final View handle = mHandle;

if ( mVertical ) {
if ( position == EXPANDED_FULL_OPEN ) {
if( mInvert ){
handle.offsetTopAndBottom( mBottomOffset + getBottom() - getTop() - mHandleHeight );}
else{
handle.offsetTopAndBottom( mTopOffset - handle.getTop() );}
invalidate();
} else if (position == COLLAPSED_SEMI_CLOSED)
{
if( mInvert ) {
handle.offsetTopAndBottom( mTopOffset - handle.getTop()+ mSemiClosedContentSize );
}else{
handle.offsetTopAndBottom(mBottomOffset + getBottom() - getTop() - mHandleHeight
- handle.getTop()+ mSemiClosedContentSize);
}
invalidate();
}

else
{
final int top = handle.getTop();
int deltaY = position - top;
if ( position < mTopOffset ) {
deltaY = mTopOffset - top + mSemiClosedContentSize;
} else if ( deltaY > mBottomOffset + getBottom() - getTop() - mHandleHeight - top ) {
deltaY = mBottomOffset + getBottom() - getTop() - mHandleHeight - top;
}

// [SEMI-CLOSED] Remove width of visible content part here ...
else if (deltaY < mTopOffset - handle.getTop()+ mSemiClosedContentSize)
{
// [SEMI_CLOSED] ... and here.
deltaY=mTopOffset - handle.getTop()+ mSemiClosedContentSize;
}
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 ) {
if( mInvert ){
handle.offsetLeftAndRight( mBottomOffset + getRight() - getLeft() - mHandleWidth );}
else{
handle.offsetLeftAndRight( mTopOffset - handle.getLeft() );}
invalidate();
} // [SEMI-CLOSED] Remove width of visible content part here.
else if (position == COLLAPSED_SEMI_CLOSED)
{
if( mInvert ){
handle.offsetLeftAndRight( mTopOffset - handle.getLeft() + mSemiClosedContentSize);}
else{
handle.offsetLeftAndRight(mBottomOffset + getRight() - getLeft() - mHandleWidth
- handle.getLeft()+ mSemiClosedContentSize);}
invalidate();
}

else {
final int left = handle.getLeft();
int deltaX = position - left;
if ( position < mTopOffset ) {
deltaX = mTopOffset - left;
} else if ( deltaX > mBottomOffset + getRight() - getLeft() - mHandleWidth - left ) {
deltaX = mBottomOffset + getRight() - getLeft() - mHandleWidth - left;
}
// [SEMI-CLOSED] Remove width of visible content part here ...
else if (deltaX > mBottomOffset + getRight() - getLeft() - mHandleWidth + mSemiClosedContentSize - left)
{
// [SEMI-CLOSED] ... and here.
deltaX = mBottomOffset + getRight() - getLeft() - mHandleWidth + mSemiClosedContentSize - 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 );
}
}
}

/**
* TODO Method Description as a Single Sentence.
*
*/
public void prepareContent()
{
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 handleHeight = mHandleHeight;
int height = getBottom() - getTop() - handleHeight - mTopOffset;

content.measure(MeasureSpec.makeMeasureSpec(getRight() - getLeft(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.UNSPECIFIED));


Log.d( LOG_TAG, "content.layout(2)" );

if ( mInvert ) {
content.layout( 0, mTopOffset,content.getMeasuredWidth(), mTopOffset + content.getMeasuredHeight() );}
else {
content.layout( 0, mTopOffset + handleHeight, content.getMeasuredWidth(), mTopOffset + handleHeight + content.getMeasuredHeight() );}

} else {

final int handleWidth = mHandle.getWidth();
int width = getRight() - getLeft() - handleWidth - mTopOffset;
content.measure( width,( getBottom() - getTop()));

if( mInvert ){
content.layout( mTopOffset, 0, mTopOffset + content.getMeasuredWidth(), content.getMeasuredHeight() );}
else{
content.layout( handleWidth + mTopOffset, 0, mTopOffset + handleWidth + 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();
content.buildDrawingCache();

content.setVisibility( View.GONE );
}


/**
* TODO Method Description as a Single Sentence.
*
*/
private void stopTracking()
{
mHandle.setPressed( false );
mTracking = false;

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

if ( mVelocityTracker != null ) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}

/**
* TODO Method Description as a Single Sentence.
*
*/
private void doAnimation()
{
if ( mAnimating ) {
incrementAnimation();

if( mInvert )
{
if ( mAnimationPosition < mTopOffset ) {
mAnimating = false;
if ( mOnDrawerScrollListener != null ) {
mOnDrawerScrollListener.onScrollBeforeClose();
}
closeDrawer();
} else if ( mAnimationPosition >= mTopOffset + ( mVertical ? getHeight() : getWidth() ) - 1 ) {
mAnimating = false;
openDrawer();
} else {
moveHandle( (int)mAnimationPosition );
mCurrentAnimationTime += ANIMATION_FRAME_DURATION;
mHandler.sendMessageAtTime( mHandler.obtainMessage( MSG_ANIMATE ), mCurrentAnimationTime );
}
} else {
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 );
}
}
}
}

/**
* TODO Method Description as a Single Sentence.
*
*/
private void incrementAnimation()
{
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 = mInvert ? mAnimatedAcceleration : mAnimatedAcceleration; // px/s/s
mAnimationPosition = position + ( v * t ) + ( 0.5f * a * t * t ); // px
mAnimatedVelocity = v + ( a * t ); // px/s
mAnimationLastTime = now; // ms
}

/**
* Toggles the drawer open and close. Takes effect immediately.
*
* @see #open()
* @see #close()
* @see #animateClose()
* @see #animateOpen()
* @see #animateToggle()
*/
public void toggle()
{
if ( !mExpanded ) {
openDrawer();
} else {
closeDrawer();
}
invalidate();
requestLayout();
}

/**
* Toggles the drawer open and close with an animation.
*
* @see #open()
* @see #close()
* @see #animateClose()
* @see #animateOpen()
* @see #toggle()
*/
public void animateToggle()
{
if ( !mExpanded ) {
animateOpen();
} else {
animateClose();
}
}

/**
* Opens the drawer immediately.
*
* @see #toggle()
* @see #close()
* @see #animateOpen()
*/
public void open()
{
openDrawer();
invalidate();
requestLayout();

sendAccessibilityEvent( AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED );
}

/**
* Closes the drawer immediately.
*
* @see #toggle()
* @see #open()
* @see #animateClose()
*/
public void close()
{
if ( mOnDrawerScrollListener != null ) {
mOnDrawerScrollListener.onScrollBeforeClose();
}
closeDrawer();
invalidate();
requestLayout();
}


/**
* Closes the drawer with an animation.
*
* @see #close()
* @see #open()
* @see #animateOpen()
* @see #animateToggle()
* @see #toggle()
*/
public void animateClose()
{
prepareContent();
final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;
if ( scrollListener != null ) {
scrollListener.onScrollStarted();
}
animateClose( mVertical ? mHandle.getTop() : mHandle.getLeft() );

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

/**
* Opens the drawer with an animation.
*
* @see #close()
* @see #open()
* @see #animateClose()
* @see #animateToggle()
* @see #toggle()
*/
public void animateOpen()
{
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 void closeDrawer()
{
moveHandle( COLLAPSED_SEMI_CLOSED );
mContent.setVisibility( View.GONE );
mContent.destroyDrawingCache();

if ( !mExpanded ) { return; }

mExpanded = false;
if ( mOnDrawerCloseListener != null ) {
mOnDrawerCloseListener.onDrawerClosed();
}

}

/**
*
*/
private void openDrawer()
{
moveHandle( EXPANDED_FULL_OPEN );
mContent.setVisibility( View.VISIBLE );

if ( mExpanded ) { return; }

mExpanded = true;

if ( mOnDrawerOpenListener != null ) {
mOnDrawerOpenListener.onDrawerOpened();
}
}

/**
* Sets the listener that receives a notification when the drawer becomes
* open.
*
* @param onDrawerOpenListener
*           The listener to be notified when the drawer is opened.
*/
public void setOnTopDrawerOpenListener( OnTopDrawerOpenListener onDrawerOpenListener )
{
mOnDrawerOpenListener = onDrawerOpenListener;
}

/**
* Sets the listener that receives a notification when the drawer becomes
* close.
*
* @param onDrawerCloseListener
*           The listener to be notified when the drawer is closed.
*/
public void setOnTopDrawerCloseListener( OnTopDrawerCloseListener onDrawerCloseListener )
{
mOnDrawerCloseListener = onDrawerCloseListener;
}

/**
* 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.
*/
public void setOnDrawerScrollListener( OnDrawerScrollListener onDrawerScrollListener )
{
mOnDrawerScrollListener = onDrawerScrollListener;
}

/**
* Returns the handle of the drawer.
*
* @return The View reprenseting the handle of the drawer, identified by the
*         "handle" id in XML.
*/
public View getHandle()
{
return mHandle;
}

/**
* Returns the content of the drawer.
*
* @return The View reprenseting the content of the drawer, identified by the
*         "content" id in XML.
*/
public View getContent()
{
return mContent;
}

/**
* Unlocks the SlidingDrawer so that touch events are processed.
*
* @see #lock()
*/
public void unlock()
{
mLocked = false;
}

/**
* Locks the SlidingDrawer so that touch events are ignores.
*
* @see #unlock()
*/
public void lock()
{
mLocked = true;
}

/**
* Indicates whether the drawer is currently fully opened.
*
* @return True if the drawer is opened, false otherwise.
*/
public boolean isOpened()
{
return mExpanded;
}

/**
* Indicates whether the drawer is scrolling or flinging.
*
* @return True if the drawer is scroller or flinging, false otherwise.
*/
public boolean isMoving()
{
return mTracking || mAnimating;
}

/**
* TODO Class description goes here.
* @version
*/
private class DrawerToggler implements OnClickListener {

public void onClick( View v )
{
if ( mLocked ) { return; }
// mAllowSingleTap isn't relevant here; you're *always*
// allowed to open/close the drawer by clicking with the
// trackball.

if ( mAnimateOnClick ) {
animateToggle();
} else {
toggle();
}
}
}

/**
* TODO Class description goes here.
* @version
*/
private class SlidingHandler extends Handler {

public void handleMessage( Message m )
{
switch ( m.what ) {
case MSG_ANIMATE:
doAnimation();
break;
}
}
}
}



No comments:

Post a Comment