Want more features on Pastebin? Sign Up, it's FREE!
Guest

SemiClosedSlidingDrawer.java

By: a guest on Apr 2nd, 2012  |  syntax: Java  |  size: 30.76 KB  |  views: 1,562  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*
  2.  * Copyright (C) 2008 The Android Open Source Project
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16.  
  17. package se.smartrefill.view;
  18.  
  19. import java.lang.reflect.Method;
  20.  
  21. import se.smartrefill.app.x.R;
  22. import android.content.Context;
  23. import android.content.res.TypedArray;
  24. import android.graphics.Bitmap;
  25. import android.graphics.Canvas;
  26. import android.graphics.Rect;
  27. import android.os.Handler;
  28. import android.os.Message;
  29. import android.os.SystemClock;
  30. import android.util.AttributeSet;
  31. import android.view.MotionEvent;
  32. import android.view.SoundEffectConstants;
  33. import android.view.VelocityTracker;
  34. import android.view.View;
  35. import android.view.ViewGroup;
  36. import android.view.accessibility.AccessibilityEvent;
  37.  
  38. /**
  39.  * This is a modified version of {@link SlidingDrawer} that supports the use
  40.  * case to show a part of the content also when the drawer is closed/collapsed.
  41.  * Use the 'semiClosedContentSize' attribute to specify how much of the content
  42.  * (preferably in dips) that should be visible in closed/collapsed mode.
  43.  *
  44.  * SlidingDrawer hides content out of the screen and allows the user to drag a
  45.  * handle to bring the content on screen. SlidingDrawer can be used vertically
  46.  * or horizontally.
  47.  *
  48.  * A special widget composed of two children views: the handle, that the users
  49.  * drags, and the content, attached to the handle and dragged with it.
  50.  *
  51.  * SlidingDrawer should be used as an overlay inside layouts. This means
  52.  * SlidingDrawer should only be used inside of a FrameLayout or a RelativeLayout
  53.  * for instance. The size of the SlidingDrawer defines how much space the
  54.  * content will occupy once slid out so SlidingDrawer should usually use
  55.  * fill_parent for both its dimensions.
  56.  *
  57.  * Inside an XML layout, SlidingDrawer must define the id of the handle and of
  58.  * the content:
  59.  *
  60.  * <pre class="prettyprint">
  61.  * &lt;SlidingDrawer
  62.  *     android:id="@+id/drawer"
  63.  *     android:layout_width="fill_parent"
  64.  *     android:layout_height="fill_parent"
  65.  *
  66.  *     android:handle="@+id/handle"
  67.  *     android:content="@+id/content"&gt;
  68.  *
  69.  *     &lt;ImageView
  70.  *         android:id="@id/handle"
  71.  *         android:layout_width="88dip"
  72.  *         android:layout_height="44dip" /&gt;
  73.  *
  74.  *     &lt;GridView
  75.  *         android:id="@id/content"
  76.  *         android:layout_width="fill_parent"
  77.  *         android:layout_height="fill_parent" /&gt;
  78.  *
  79.  * &lt;/SlidingDrawer&gt;
  80.  * </pre>
  81.  *
  82.  * @attr ref android.R.styleable#SlidingDrawer_content
  83.  * @attr ref android.R.styleable#SlidingDrawer_handle
  84.  * @attr ref android.R.styleable#SlidingDrawer_topOffset
  85.  * @attr ref android.R.styleable#SlidingDrawer_bottomOffset
  86.  * @attr ref android.R.styleable#SlidingDrawer_orientation
  87.  * @attr ref android.R.styleable#SlidingDrawer_allowSingleTap
  88.  * @attr ref android.R.styleable#SlidingDrawer_animateOnClick
  89.  */
  90. public class SemiClosedSlidingDrawer
  91.         extends ViewGroup
  92. {
  93.         public static final int ORIENTATION_HORIZONTAL = 0;
  94.         public static final int ORIENTATION_VERTICAL = 1;
  95.  
  96.         private static final int TAP_THRESHOLD = 6;
  97.         private static final float MAXIMUM_TAP_VELOCITY = 100.0f;
  98.         private static final float MAXIMUM_MINOR_VELOCITY = 150.0f;
  99.         private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;
  100.         private static final float MAXIMUM_ACCELERATION = 2000.0f;
  101.         private static final int VELOCITY_UNITS = 1000;
  102.         private static final int MSG_ANIMATE = 1000;
  103.         private static final int ANIMATION_FRAME_DURATION = 1000 / 60;
  104.  
  105.         private static final int EXPANDED_FULL_OPEN = -10001;
  106.         private static final int COLLAPSED_SEMI_CLOSED = -10003;
  107.  
  108.         private final int mHandleId;
  109.         private final int mContentId;
  110.  
  111.         private View mHandle;
  112.         private View mContent;
  113.  
  114.         private final Rect mFrame = new Rect();
  115.         private final Rect mInvalidate = new Rect();
  116.         private boolean mTracking;
  117.         private boolean mLocked;
  118.  
  119.         private VelocityTracker mVelocityTracker;
  120.  
  121.         private boolean mVertical;
  122.         private boolean mExpanded;
  123.         private int mBottomOffset;
  124.         private int mTopOffset;
  125.         private int mHandleHeight;
  126.         private int mHandleWidth;
  127.  
  128.         private int mSemiClosedContentSize;
  129.  
  130.         private OnDrawerOpenListener mOnDrawerOpenListener;
  131.         private OnDrawerCloseListener mOnDrawerCloseListener;
  132.         private OnDrawerScrollListener mOnDrawerScrollListener;
  133.  
  134.         private final Handler mHandler = new SlidingHandler();
  135.         private float mAnimatedAcceleration;
  136.         private float mAnimatedVelocity;
  137.         private float mAnimationPosition;
  138.         private long mAnimationLastTime;
  139.         private long mCurrentAnimationTime;
  140.         private int mTouchDelta;
  141.         private boolean mAnimating;
  142.         private boolean mAllowSingleTap;
  143.         private boolean mAnimateOnClick;
  144.  
  145.         private final int mTapThreshold;
  146.         private final int mMaximumTapVelocity;
  147.         private final int mMaximumMinorVelocity;
  148.         private final int mMaximumMajorVelocity;
  149.         private final int mMaximumAcceleration;
  150.         private final int mVelocityUnits;
  151.  
  152.         /**
  153.          * Callback invoked when the drawer is opened.
  154.          */
  155.         public static interface OnDrawerOpenListener
  156.         {
  157.                 /**
  158.                  * Invoked when the drawer becomes fully open.
  159.                  */
  160.                 public void onDrawerOpened();
  161.         }
  162.  
  163.         /**
  164.          * Callback invoked when the drawer is closed.
  165.          */
  166.         public static interface OnDrawerCloseListener
  167.         {
  168.                 /**
  169.                  * Invoked when the drawer becomes fully closed.
  170.                  */
  171.                 public void onDrawerClosed();
  172.         }
  173.  
  174.         /**
  175.          * Callback invoked when the drawer is scrolled.
  176.          */
  177.         public static interface OnDrawerScrollListener
  178.         {
  179.                 /**
  180.                  * Invoked when the user starts dragging/flinging the drawer's handle.
  181.                  */
  182.                 public void onScrollStarted();
  183.  
  184.                 /**
  185.                  * Invoked when the user stops dragging/flinging the drawer's handle.
  186.                  */
  187.                 public void onScrollEnded();
  188.         }
  189.  
  190.         /**
  191.          * Creates a new SlidingDrawer from a specified set of attributes defined in
  192.          * XML.
  193.          *
  194.          * @param context
  195.          *            The application's environment.
  196.          * @param attrs
  197.          *            The attributes defined in XML.
  198.          */
  199.         public SemiClosedSlidingDrawer(Context context, AttributeSet attrs)
  200.         {
  201.                 this(context, attrs, 0);
  202.         }
  203.  
  204.         /**
  205.          * Creates a new SlidingDrawer from a specified set of attributes defined in
  206.          * XML.
  207.          *
  208.          * @param context
  209.          *            The application's environment.
  210.          * @param attrs
  211.          *            The attributes defined in XML.
  212.          * @param defStyle
  213.          *            The style to apply to this widget.
  214.          */
  215.         public SemiClosedSlidingDrawer(Context context, AttributeSet attrs, int defStyle)
  216.         {
  217.                 super(context, attrs, defStyle);
  218.                 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SemiClosedSlidingDrawer, defStyle, 0);
  219.  
  220.                 String orientation = a.getString(R.styleable.SemiClosedSlidingDrawer_orientation);
  221.                 mVertical = (orientation != null && orientation.equals("vertical"));
  222.                 mBottomOffset = (int) a.getDimension(R.styleable.SemiClosedSlidingDrawer_bottomOffset, 0.0f);
  223.                 mTopOffset = (int) a.getDimension(R.styleable.SemiClosedSlidingDrawer_topOffset, 0.0f);
  224.                 mAllowSingleTap = a.getBoolean(R.styleable.SemiClosedSlidingDrawer_allowSingleTap, true);
  225.                 mAnimateOnClick = a.getBoolean(R.styleable.SemiClosedSlidingDrawer_animateOnClick, true);
  226.                 mSemiClosedContentSize = (int) a.getDimension(R.styleable.SemiClosedSlidingDrawer_semiClosedContentSize, 0.0f);
  227.  
  228.                 int handleId = a.getResourceId(R.styleable.SemiClosedSlidingDrawer_handle, 0);
  229.                 if (handleId == 0)
  230.                 {
  231.                         throw new IllegalArgumentException("The handle attribute is required and must refer " + "to a valid child.");
  232.                 }
  233.  
  234.                 int contentId = a.getResourceId(R.styleable.SemiClosedSlidingDrawer_content, 0);
  235.                 if (contentId == 0)
  236.                 {
  237.                         throw new IllegalArgumentException("The content attribute is required and must refer "
  238.                                                         + "to a valid child.");
  239.                 }
  240.  
  241.                 if (handleId == contentId)
  242.                 {
  243.                         throw new IllegalArgumentException("The content and handle attributes must refer "
  244.                                                         + "to different children.");
  245.                 }
  246.  
  247.                 mHandleId = handleId;
  248.                 mContentId = contentId;
  249.  
  250.                 final float density = getResources().getDisplayMetrics().density;
  251.                 mTapThreshold = (int) (TAP_THRESHOLD * density + 0.5f);
  252.                 mMaximumTapVelocity = (int) (MAXIMUM_TAP_VELOCITY * density + 0.5f);
  253.                 mMaximumMinorVelocity = (int) (MAXIMUM_MINOR_VELOCITY * density + 0.5f);
  254.                 mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f);
  255.                 mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f);
  256.                 mVelocityUnits = (int) (VELOCITY_UNITS * density + 0.5f);
  257.  
  258.                 a.recycle();
  259.  
  260.                 setAlwaysDrawnWithCacheEnabled(false);
  261.         }
  262.  
  263.         @Override
  264.         protected void onFinishInflate()
  265.         {
  266.                 mHandle = findViewById(mHandleId);
  267.                 if (mHandle == null)
  268.                 {
  269.                         throw new IllegalArgumentException("The handle attribute is must refer to an" + " existing child.");
  270.                 }
  271.                 mHandle.setOnClickListener(new DrawerToggler());
  272.  
  273.                 mContent = findViewById(mContentId);
  274.                 if (mContent == null)
  275.                 {
  276.                         throw new IllegalArgumentException("The content attribute is must refer to an" + " existing child.");
  277.                 }
  278.                 mContent.setVisibility(View.VISIBLE);
  279.         }
  280.  
  281.         @Override
  282.         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
  283.         {
  284.                 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
  285.                 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
  286.  
  287.                 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
  288.                 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
  289.  
  290.                 if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED)
  291.                 {
  292.                         throw new RuntimeException("SlidingDrawer cannot have UNSPECIFIED dimensions");
  293.                 }
  294.  
  295.                 final View handle = mHandle;
  296.                 measureChild(handle, widthMeasureSpec, heightMeasureSpec);
  297.  
  298.                 if (mVertical)
  299.                 {
  300.                         int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset;
  301.                         mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY),
  302.                                                         MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
  303.                 }
  304.                 else
  305.                 {
  306.                         int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset;
  307.                         mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
  308.                                                         MeasureSpec.makeMeasureSpec(heightSpecSize, MeasureSpec.EXACTLY));
  309.                 }
  310.  
  311.                 setMeasuredDimension(widthSpecSize, heightSpecSize);
  312.         }
  313.  
  314.         @Override
  315.         protected void dispatchDraw(Canvas canvas)
  316.         {
  317.                 final long drawingTime = getDrawingTime();
  318.                 final View handle = mHandle;
  319.                 final boolean isVertical = mVertical;
  320.  
  321.                 drawChild(canvas, handle, drawingTime);
  322.  
  323.                 if (mTracking || mAnimating || !mExpanded)
  324.                 {
  325.                         final Bitmap cache = mContent.getDrawingCache();
  326.                         if (cache != null)
  327.                         {
  328.                                 if (isVertical)
  329.                                 {
  330.                                         canvas.drawBitmap(cache, 0, handle.getBottom(), null);
  331.                                 }
  332.                                 else
  333.                                 {
  334.                                         canvas.drawBitmap(cache, handle.getRight(), 0, null);
  335.                                 }
  336.                         }
  337.                         else
  338.                         {
  339.                                 canvas.save();
  340.                                 canvas.translate(isVertical ? 0 : handle.getLeft() - mTopOffset, isVertical ? handle.getTop()
  341.                                                                 - mTopOffset : 0);
  342.                                 drawChild(canvas, mContent, drawingTime);
  343.                                 canvas.restore();
  344.                         }
  345.                 }
  346.                 else if (mExpanded)
  347.                 {
  348.                         drawChild(canvas, mContent, drawingTime);
  349.                 }
  350.         }
  351.  
  352.         @Override
  353.         protected void onLayout(boolean changed, int l, int t, int r, int b)
  354.         {
  355.                 if (mTracking)
  356.                 {
  357.                         return;
  358.                 }
  359.  
  360.                 final int width = r - l;
  361.                 final int height = b - t;
  362.  
  363.                 final View handle = mHandle;
  364.  
  365.                 int childWidth = handle.getMeasuredWidth();
  366.                 int childHeight = handle.getMeasuredHeight();
  367.  
  368.                 int childLeft;
  369.                 int childTop;
  370.  
  371.                 final View content = mContent;
  372.  
  373.                 if (mVertical)
  374.                 {
  375.                         // [SEMI-CLOSED] Change so that drawer is laid out semi-closed in
  376.                         // collapsed mode.
  377.                         childLeft = (width - childWidth) / 2;
  378.                         childTop = mExpanded ? mTopOffset : height - childHeight + mBottomOffset - mSemiClosedContentSize;
  379.  
  380.                         content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(),
  381.                                                         mTopOffset + childHeight + content.getMeasuredHeight());
  382.                 }
  383.                 else
  384.                 {
  385.                         // [SEMI-CLOSED] Change so that drawer is laid out semi-closed in
  386.                         // collapsed mode.
  387.                         childLeft = mExpanded ? mTopOffset : width - childWidth + mBottomOffset - mSemiClosedContentSize;
  388.                         childTop = (height - childHeight) / 2;
  389.  
  390.                         content.layout(mTopOffset + childWidth, 0, mTopOffset + childWidth + content.getMeasuredWidth(),
  391.                                                         content.getMeasuredHeight());
  392.                 }
  393.  
  394.                 handle.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
  395.                 mHandleHeight = handle.getHeight();
  396.                 mHandleWidth = handle.getWidth();
  397.         }
  398.  
  399.         @Override
  400.         public boolean onInterceptTouchEvent(MotionEvent event)
  401.         {
  402.                 if (mLocked)
  403.                 {
  404.                         return false;
  405.                 }
  406.  
  407.                 final int action = event.getAction();
  408.  
  409.                 float x = event.getX();
  410.                 float y = event.getY();
  411.  
  412.                 final Rect frame = mFrame;
  413.                 final View handle = mHandle;
  414.                
  415.                 handle.getHitRect(frame);
  416.  
  417.                 if (!mTracking && !frame.contains((int) x, (int) y))
  418.                 {
  419.                         return false;
  420.                 }
  421.  
  422.                 if (action == MotionEvent.ACTION_DOWN)
  423.                 {
  424.                         mTracking = true;
  425.  
  426.                         handle.setPressed(true);
  427.                         // Must be called before prepareTracking()
  428.                         prepareContent();
  429.  
  430.                         // Must be called after prepareContent()
  431.                         if (mOnDrawerScrollListener != null)
  432.                         {
  433.                                 mOnDrawerScrollListener.onScrollStarted();
  434.                         }
  435.  
  436.                         if (mVertical)
  437.                         {
  438.                                 final int top = mHandle.getTop();
  439.                                 mTouchDelta = (int) y - top;
  440.                                 prepareTracking(top);
  441.                         }
  442.                         else
  443.                         {
  444.                                 final int left = mHandle.getLeft();
  445.                                 mTouchDelta = (int) x - left;
  446.                                 prepareTracking(left);
  447.                         }
  448.                         mVelocityTracker.addMovement(event);
  449.                 }
  450.  
  451.                 return true;
  452.         }
  453.  
  454.         @Override
  455.         public boolean onTouchEvent(MotionEvent event)
  456.         {
  457.                 if (mLocked)
  458.                 {
  459.                         return true;
  460.                 }
  461.  
  462.                 if (mTracking)
  463.                 {
  464.                         mVelocityTracker.addMovement(event);
  465.                         final int action = event.getAction();
  466.                         switch (action) {
  467.                         case MotionEvent.ACTION_MOVE:
  468.                                 moveHandle((int) (mVertical ? event.getY() : event.getX()) - mTouchDelta);
  469.                                 break;
  470.                         case MotionEvent.ACTION_UP:
  471.                         case MotionEvent.ACTION_CANCEL: {
  472.                                 final VelocityTracker velocityTracker = mVelocityTracker;
  473.                                 velocityTracker.computeCurrentVelocity(mVelocityUnits);
  474.  
  475.                                 float yVelocity = velocityTracker.getYVelocity();
  476.                                 float xVelocity = velocityTracker.getXVelocity();
  477.                                 boolean negative;
  478.  
  479.                                 final boolean vertical = mVertical;
  480.                                 if (vertical)
  481.                                 {
  482.                                         negative = yVelocity < 0;
  483.                                         if (xVelocity < 0)
  484.                                         {
  485.                                                 xVelocity = -xVelocity;
  486.                                         }
  487.                                         if (xVelocity > mMaximumMinorVelocity)
  488.                                         {
  489.                                                 xVelocity = mMaximumMinorVelocity;
  490.                                         }
  491.                                 }
  492.                                 else
  493.                                 {
  494.                                         negative = xVelocity < 0;
  495.                                         if (yVelocity < 0)
  496.                                         {
  497.                                                 yVelocity = -yVelocity;
  498.                                         }
  499.                                         if (yVelocity > mMaximumMinorVelocity)
  500.                                         {
  501.                                                 yVelocity = mMaximumMinorVelocity;
  502.                                         }
  503.                                 }
  504.  
  505.                                 float velocity = (float) Math.hypot(xVelocity, yVelocity);
  506.                                 if (negative)
  507.                                 {
  508.                                         velocity = -velocity;
  509.                                 }
  510.  
  511.                                 final int top = mHandle.getTop();
  512.                                 final int left = mHandle.getLeft();
  513.  
  514.                                 if (Math.abs(velocity) < mMaximumTapVelocity)
  515.                                 {
  516.                                         // [SEMI-CLOSED] Remove width of visible content part here.
  517.                                         if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset)
  518.                                                                         || (!mExpanded && top > mBottomOffset + getBottom() - getTop() - mHandleHeight
  519.                                                                                                         - -mSemiClosedContentSize - mTapThreshold)
  520.                                                                         : (mExpanded && left < mTapThreshold + mTopOffset)
  521.                                                                                                         || (!mExpanded && left > mBottomOffset + getRight() - getLeft()
  522.                                                                                                                                         - mHandleWidth - mSemiClosedContentSize
  523.                                                                                                                                         - mTapThreshold))
  524.                                         {
  525.  
  526.                                                 if (mAllowSingleTap)
  527.                                                 {
  528.                                                         playSoundEffect(SoundEffectConstants.CLICK);
  529.  
  530.                                                         if (mExpanded)
  531.                                                         {
  532.                                                                 animateClose(vertical ? top : left);
  533.                                                         }
  534.                                                         else
  535.                                                         {
  536.                                                                 animateOpen(vertical ? top : left);
  537.                                                         }
  538.                                                 }
  539.                                                 else
  540.                                                 {
  541.                                                         performFling(vertical ? top : left, velocity, false);
  542.                                                 }
  543.  
  544.                                         }
  545.                                         else
  546.                                         {
  547.                                                 performFling(vertical ? top : left, velocity, false);
  548.                                         }
  549.                                 }
  550.                                 else
  551.                                 {
  552.                                         performFling(vertical ? top : left, velocity, false);
  553.                                 }
  554.                         }
  555.                                 break;
  556.                         }
  557.                 }
  558.  
  559.                 return mTracking || mAnimating || super.onTouchEvent(event);
  560.         }
  561.  
  562.         private void animateClose(int position)
  563.         {
  564.                 prepareTracking(position);
  565.                 performFling(position, mMaximumAcceleration, true);
  566.         }
  567.  
  568.         private void animateOpen(int position)
  569.         {
  570.                 prepareTracking(position);
  571.                 performFling(position, -mMaximumAcceleration, true);
  572.         }
  573.  
  574.         private void performFling(int position, float velocity, boolean always)
  575.         {
  576.                 mAnimationPosition = position;
  577.                 mAnimatedVelocity = velocity;
  578.  
  579.                 if (mExpanded)
  580.                 {
  581.                         if (always
  582.                                                         || (velocity > mMaximumMajorVelocity || (position > mTopOffset
  583.                                                                                         + (mVertical ? mHandleHeight : mHandleWidth) && velocity > -mMaximumMajorVelocity)))
  584.                         {
  585.                                 // We are expanded, but they didn't move sufficiently to cause
  586.                                 // us to retract. Animate back to the expanded position.
  587.                                 mAnimatedAcceleration = mMaximumAcceleration;
  588.                                 if (velocity < 0)
  589.                                 {
  590.                                         mAnimatedVelocity = 0;
  591.                                 }
  592.                         }
  593.                         else
  594.                         {
  595.                                 // We are expanded and are now going to animate away.
  596.                                 mAnimatedAcceleration = -mMaximumAcceleration;
  597.                                 if (velocity > 0)
  598.                                 {
  599.                                         mAnimatedVelocity = 0;
  600.                                 }
  601.                         }
  602.                 }
  603.                 else
  604.                 {
  605.                         if (!always
  606.                                                         && (velocity > mMaximumMajorVelocity || (position > (mVertical ? getHeight() : getWidth()) / 2 && velocity > -mMaximumMajorVelocity)))
  607.                         {
  608.                                 // We are collapsed, and they moved enough to allow us to
  609.                                 // expand.
  610.                                 mAnimatedAcceleration = mMaximumAcceleration;
  611.                                 if (velocity < 0)
  612.                                 {
  613.                                         mAnimatedVelocity = 0;
  614.                                 }
  615.                         }
  616.                         else
  617.                         {
  618.                                 // We are collapsed, but they didn't move sufficiently to cause
  619.                                 // us to retract. Animate back to the collapsed position.
  620.                                 mAnimatedAcceleration = -mMaximumAcceleration;
  621.                                 if (velocity > 0)
  622.                                 {
  623.                                         mAnimatedVelocity = 0;
  624.                                 }
  625.                         }
  626.                 }
  627.  
  628.                 long now = SystemClock.uptimeMillis();
  629.                 mAnimationLastTime = now;
  630.                 mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
  631.                 mAnimating = true;
  632.                 mHandler.removeMessages(MSG_ANIMATE);
  633.                 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);
  634.                 stopTracking();
  635.         }
  636.  
  637.         private void prepareTracking(int position)
  638.         {
  639.                 mTracking = true;
  640.                 mVelocityTracker = VelocityTracker.obtain();
  641.                 boolean opening = !mExpanded;
  642.                 if (opening)
  643.                 {
  644.                         mAnimatedAcceleration = mMaximumAcceleration;
  645.                         mAnimatedVelocity = mMaximumMajorVelocity;
  646.                         // [SEMI-CLOSED] Remove width of visible content part here.
  647.                         mAnimationPosition = mBottomOffset + (mVertical ? getHeight() - mHandleHeight : getWidth() - mHandleWidth)
  648.                                                         - mSemiClosedContentSize;
  649.                         moveHandle((int) mAnimationPosition);
  650.                         mAnimating = true;
  651.                         mHandler.removeMessages(MSG_ANIMATE);
  652.                         long now = SystemClock.uptimeMillis();
  653.                         mAnimationLastTime = now;
  654.                         mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;
  655.                         mAnimating = true;
  656.                 }
  657.                 else
  658.                 {
  659.                         if (mAnimating)
  660.                         {
  661.                                 mAnimating = false;
  662.                                 mHandler.removeMessages(MSG_ANIMATE);
  663.                         }
  664.                         moveHandle(position);
  665.                 }
  666.         }
  667.  
  668.         private void moveHandle(int position)
  669.         {
  670.                 final View handle = mHandle;
  671.  
  672.                 if (mVertical)
  673.                 {
  674.                         if (position == EXPANDED_FULL_OPEN)
  675.                         {
  676.                                 handle.offsetTopAndBottom(mTopOffset - handle.getTop());
  677.                                 invalidate();
  678.                         }
  679.                         // [SEMI-CLOSED] Remove width of visible content part here.
  680.                         else if (position == COLLAPSED_SEMI_CLOSED)
  681.                         {
  682.                                 handle.offsetTopAndBottom(mBottomOffset + getBottom() - getTop() - mHandleHeight
  683.                                                                 - mSemiClosedContentSize - handle.getTop());
  684.                                 invalidate();
  685.                         }
  686.                         else
  687.                         {
  688.                                 final int top = handle.getTop();
  689.                                 int deltaY = position - top;
  690.                                 if (position < mTopOffset)
  691.                                 {
  692.                                         deltaY = mTopOffset - top;
  693.                                 }
  694.                                 // [SEMI-CLOSED] Remove width of visible content part here ...
  695.                                 else if (deltaY > mBottomOffset + getBottom() - getTop() - mHandleHeight - mSemiClosedContentSize - top)
  696.                                 {
  697.                                         // [SEMI_CLOSED] ... and here.
  698.                                         deltaY = mBottomOffset + getBottom() - getTop() - mHandleHeight - mSemiClosedContentSize - top;
  699.                                 }
  700.                                 handle.offsetTopAndBottom(deltaY);
  701.  
  702.                                 final Rect frame = mFrame;
  703.                                 final Rect region = mInvalidate;
  704.  
  705.                                 handle.getHitRect(frame);
  706.                                 region.set(frame);
  707.  
  708.                                 region.union(frame.left, frame.top - deltaY, frame.right, frame.bottom - deltaY);
  709.                                 region.union(0, frame.bottom - deltaY, getWidth(), frame.bottom - deltaY + mContent.getHeight());
  710.  
  711.                                 invalidate(region);
  712.                         }
  713.                 }
  714.                 else
  715.                 {
  716.                         if (position == EXPANDED_FULL_OPEN)
  717.                         {
  718.                                 handle.offsetLeftAndRight(mTopOffset - handle.getLeft());
  719.                                 invalidate();
  720.                         }
  721.                         // [SEMI-CLOSED] Remove width of visible content part here.
  722.                         else if (position == COLLAPSED_SEMI_CLOSED)
  723.                         {
  724.                                 handle.offsetLeftAndRight(mBottomOffset + getRight() - getLeft() - mHandleWidth
  725.                                                                 - mSemiClosedContentSize - handle.getLeft());
  726.                                 invalidate();
  727.                         }
  728.                         else
  729.                         {
  730.                                 final int left = handle.getLeft();
  731.                                 int deltaX = position - left;
  732.                                 if (position < mTopOffset)
  733.                                 {
  734.                                         deltaX = mTopOffset - left;
  735.                                 }
  736.                                 // [SEMI-CLOSED] Remove width of visible content part here ...
  737.                                 else if (deltaX > mBottomOffset + getRight() - getLeft() - mHandleWidth - mSemiClosedContentSize - left)
  738.                                 {
  739.                                         // [SEMI-CLOSED] ... and here.
  740.                                         deltaX = mBottomOffset + getRight() - getLeft() - mHandleWidth - mSemiClosedContentSize - left;
  741.                                 }
  742.                                 handle.offsetLeftAndRight(deltaX);
  743.  
  744.                                 final Rect frame = mFrame;
  745.                                 final Rect region = mInvalidate;
  746.  
  747.                                 handle.getHitRect(frame);
  748.                                 region.set(frame);
  749.  
  750.                                 region.union(frame.left - deltaX, frame.top, frame.right - deltaX, frame.bottom);
  751.                                 region.union(frame.right - deltaX, 0, frame.right - deltaX + mContent.getWidth(), getHeight());
  752.  
  753.                                 invalidate(region);
  754.                         }
  755.                 }
  756.         }
  757.  
  758.         private void prepareContent()
  759.         {
  760.                 if (mAnimating)
  761.                 {
  762.                         return;
  763.                 }
  764.  
  765.                 // Something changed in the content, we need to honor the layout request
  766.                 // before creating the cached bitmap
  767.                 final View content = mContent;
  768.                 if (content.isLayoutRequested())
  769.                 {
  770.                         if (mVertical)
  771.                         {
  772.                                 final int childHeight = mHandleHeight;
  773.                                 int height = getBottom() - getTop() - childHeight - mTopOffset;
  774.                                 content.measure(MeasureSpec.makeMeasureSpec(getRight() - getLeft(), MeasureSpec.EXACTLY),
  775.                                                                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
  776.                                 content.layout(0, mTopOffset + childHeight, content.getMeasuredWidth(), mTopOffset + childHeight
  777.                                                                 + content.getMeasuredHeight());
  778.                         }
  779.                         else
  780.                         {
  781.                                 final int childWidth = mHandle.getWidth();
  782.                                 int width = getRight() - getLeft() - childWidth - mTopOffset;
  783.                                 content.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
  784.                                                                 MeasureSpec.makeMeasureSpec(getBottom() - getTop(), MeasureSpec.EXACTLY));
  785.                                 content.layout(childWidth + mTopOffset, 0, mTopOffset + childWidth + content.getMeasuredWidth(),
  786.                                                                 content.getMeasuredHeight());
  787.                         }
  788.                 }
  789.                 // Try only once... we should really loop but it's not a big deal
  790.                 // if the draw was cancelled, it will only be temporary anyway
  791.                 content.getViewTreeObserver().dispatchOnPreDraw();
  792.                 if (!isViewHardwareAccelerated(content))
  793.                 {
  794.                         content.buildDrawingCache();
  795.                 }
  796.  
  797.                 content.setVisibility(View.GONE);
  798.         }
  799.        
  800.         private boolean isViewHardwareAccelerated(View view)
  801.         {
  802.                 // View.isHardwareAccelerated() is available from API level 11 (and up).
  803.                 try
  804.                 {
  805.                         Method isHardwareAcceleratedMethod = view.getClass().getDeclaredMethod("isHardwareAccelerated");
  806.                         if (isHardwareAcceleratedMethod != null)
  807.                         {
  808.                                 Boolean result = (Boolean) isHardwareAcceleratedMethod.invoke(view);
  809.                                 return result != null && result;
  810.                         }
  811.                 }
  812.                 catch (Exception e)
  813.                 {
  814.                         // Can't do anything here.
  815.                 }
  816.                 return false;
  817.         }
  818.  
  819.         private void stopTracking()
  820.         {
  821.                 mHandle.setPressed(false);
  822.                 mTracking = false;
  823.  
  824.                 if (mOnDrawerScrollListener != null)
  825.                 {
  826.                         mOnDrawerScrollListener.onScrollEnded();
  827.                 }
  828.  
  829.                 if (mVelocityTracker != null)
  830.                 {
  831.                         mVelocityTracker.recycle();
  832.                         mVelocityTracker = null;
  833.                 }
  834.         }
  835.  
  836.         private void doAnimation()
  837.         {
  838.                 if (mAnimating)
  839.                 {
  840.                         incrementAnimation();
  841.                         if (mAnimationPosition >= mBottomOffset + (mVertical ? getHeight() : getWidth()) - 1)
  842.                         {
  843.                                 mAnimating = false;
  844.                                 closeDrawer();
  845.                         }
  846.                         else if (mAnimationPosition < mTopOffset)
  847.                         {
  848.                                 mAnimating = false;
  849.                                 openDrawer();
  850.                         }
  851.                         else
  852.                         {
  853.                                 moveHandle((int) mAnimationPosition);
  854.                                 mCurrentAnimationTime += ANIMATION_FRAME_DURATION;
  855.                                 mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurrentAnimationTime);
  856.                         }
  857.                 }
  858.         }
  859.  
  860.         private void incrementAnimation()
  861.         {
  862.                 long now = SystemClock.uptimeMillis();
  863.                 float t = (now - mAnimationLastTime) / 1000.0f; // ms -> s
  864.                 final float position = mAnimationPosition;
  865.                 final float v = mAnimatedVelocity; // px/s
  866.                 final float a = mAnimatedAcceleration; // px/s/s
  867.                 mAnimationPosition = position + (v * t) + (0.5f * a * t * t); // px
  868.                 mAnimatedVelocity = v + (a * t); // px/s
  869.                 mAnimationLastTime = now; // ms
  870.         }
  871.  
  872.         /**
  873.          * Toggles the drawer open and close. Takes effect immediately.
  874.          *
  875.          * @see #open()
  876.          * @see #close()
  877.          * @see #animateClose()
  878.          * @see #animateOpen()
  879.          * @see #animateToggle()
  880.          */
  881.         public void toggle()
  882.         {
  883.                 if (!mExpanded)
  884.                 {
  885.                         openDrawer();
  886.                 }
  887.                 else
  888.                 {
  889.                         closeDrawer();
  890.                 }
  891.                 invalidate();
  892.                 requestLayout();
  893.         }
  894.  
  895.         /**
  896.          * Toggles the drawer open and close with an animation.
  897.          *
  898.          * @see #open()
  899.          * @see #close()
  900.          * @see #animateClose()
  901.          * @see #animateOpen()
  902.          * @see #toggle()
  903.          */
  904.         public void animateToggle()
  905.         {
  906.                 if (!mExpanded)
  907.                 {
  908.                         animateOpen();
  909.                 }
  910.                 else
  911.                 {
  912.                         animateClose();
  913.                 }
  914.         }
  915.  
  916.         /**
  917.          * Opens the drawer immediately.
  918.          *
  919.          * @see #toggle()
  920.          * @see #close()
  921.          * @see #animateOpen()
  922.          */
  923.         public void open()
  924.         {
  925.                 openDrawer();
  926.                 invalidate();
  927.                 requestLayout();
  928.  
  929.                 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
  930.         }
  931.  
  932.         /**
  933.          * Closes the drawer immediately.
  934.          *
  935.          * @see #toggle()
  936.          * @see #open()
  937.          * @see #animateClose()
  938.          */
  939.         public void close()
  940.         {
  941.                 closeDrawer();
  942.                 invalidate();
  943.                 requestLayout();
  944.         }
  945.  
  946.         /**
  947.          * Closes the drawer with an animation.
  948.          *
  949.          * @see #close()
  950.          * @see #open()
  951.          * @see #animateOpen()
  952.          * @see #animateToggle()
  953.          * @see #toggle()
  954.          */
  955.         public void animateClose()
  956.         {
  957.                 prepareContent();
  958.                 final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;
  959.                 if (scrollListener != null)
  960.                 {
  961.                         scrollListener.onScrollStarted();
  962.                 }
  963.                 animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft());
  964.  
  965.                 if (scrollListener != null)
  966.                 {
  967.                         scrollListener.onScrollEnded();
  968.                 }
  969.         }
  970.  
  971.         /**
  972.          * Opens the drawer with an animation.
  973.          *
  974.          * @see #close()
  975.          * @see #open()
  976.          * @see #animateClose()
  977.          * @see #animateToggle()
  978.          * @see #toggle()
  979.          */
  980.         public void animateOpen()
  981.         {
  982.                 prepareContent();
  983.                 final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener;
  984.                 if (scrollListener != null)
  985.                 {
  986.                         scrollListener.onScrollStarted();
  987.                 }
  988.                 animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft());
  989.  
  990.                 sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
  991.  
  992.                 if (scrollListener != null)
  993.                 {
  994.                         scrollListener.onScrollEnded();
  995.                 }
  996.         }
  997.  
  998.         private void closeDrawer()
  999.         {
  1000.                 moveHandle(COLLAPSED_SEMI_CLOSED);
  1001.                 mContent.setVisibility(View.GONE);
  1002.                 mContent.destroyDrawingCache();
  1003.  
  1004.                 if (!mExpanded)
  1005.                 {
  1006.                         return;
  1007.                 }
  1008.  
  1009.                 mExpanded = false;
  1010.                 if (mOnDrawerCloseListener != null)
  1011.                 {
  1012.                         mOnDrawerCloseListener.onDrawerClosed();
  1013.                 }
  1014.         }
  1015.  
  1016.         private void openDrawer()
  1017.         {
  1018.                 moveHandle(EXPANDED_FULL_OPEN);
  1019.                 mContent.setVisibility(View.VISIBLE);
  1020.  
  1021.                 if (mExpanded)
  1022.                 {
  1023.                         return;
  1024.                 }
  1025.  
  1026.                 mExpanded = true;
  1027.  
  1028.                 if (mOnDrawerOpenListener != null)
  1029.                 {
  1030.                         mOnDrawerOpenListener.onDrawerOpened();
  1031.                 }
  1032.         }
  1033.  
  1034.         /**
  1035.          * Sets the listener that receives a notification when the drawer becomes
  1036.          * open.
  1037.          *
  1038.          * @param onDrawerOpenListener
  1039.          *            The listener to be notified when the drawer is opened.
  1040.          */
  1041.         public void setOnDrawerOpenListener(OnDrawerOpenListener onDrawerOpenListener)
  1042.         {
  1043.                 mOnDrawerOpenListener = onDrawerOpenListener;
  1044.         }
  1045.  
  1046.         /**
  1047.          * Sets the listener that receives a notification when the drawer becomes
  1048.          * close.
  1049.          *
  1050.          * @param onDrawerCloseListener
  1051.          *            The listener to be notified when the drawer is closed.
  1052.          */
  1053.         public void setOnDrawerCloseListener(OnDrawerCloseListener onDrawerCloseListener)
  1054.         {
  1055.                 mOnDrawerCloseListener = onDrawerCloseListener;
  1056.         }
  1057.  
  1058.         /**
  1059.          * Sets the listener that receives a notification when the drawer starts or
  1060.          * ends a scroll. A fling is considered as a scroll. A fling will also
  1061.          * trigger a drawer opened or drawer closed event.
  1062.          *
  1063.          * @param onDrawerScrollListener
  1064.          *            The listener to be notified when scrolling starts or stops.
  1065.          */
  1066.         public void setOnDrawerScrollListener(OnDrawerScrollListener onDrawerScrollListener)
  1067.         {
  1068.                 mOnDrawerScrollListener = onDrawerScrollListener;
  1069.         }
  1070.  
  1071.         /**
  1072.          * Returns the handle of the drawer.
  1073.          *
  1074.          * @return The View reprenseting the handle of the drawer, identified by the
  1075.          *         "handle" id in XML.
  1076.          */
  1077.         public View getHandle()
  1078.         {
  1079.                 return mHandle;
  1080.         }
  1081.  
  1082.         /**
  1083.          * Returns the content of the drawer.
  1084.          *
  1085.          * @return The View reprenseting the content of the drawer, identified by
  1086.          *         the "content" id in XML.
  1087.          */
  1088.         public View getContent()
  1089.         {
  1090.                 return mContent;
  1091.         }
  1092.  
  1093.         /**
  1094.          * Unlocks the SlidingDrawer so that touch events are processed.
  1095.          *
  1096.          * @see #lock()
  1097.          */
  1098.         public void unlock()
  1099.         {
  1100.                 mLocked = false;
  1101.         }
  1102.  
  1103.         /**
  1104.          * Locks the SlidingDrawer so that touch events are ignores.
  1105.          *
  1106.          * @see #unlock()
  1107.          */
  1108.         public void lock()
  1109.         {
  1110.                 mLocked = true;
  1111.         }
  1112.  
  1113.         /**
  1114.          * Indicates whether the drawer is currently fully opened.
  1115.          *
  1116.          * @return True if the drawer is opened, false otherwise.
  1117.          */
  1118.         public boolean isOpened()
  1119.         {
  1120.                 return mExpanded;
  1121.         }
  1122.  
  1123.         /**
  1124.          * Indicates whether the drawer is scrolling or flinging.
  1125.          *
  1126.          * @return True if the drawer is scroller or flinging, false otherwise.
  1127.          */
  1128.         public boolean isMoving()
  1129.         {
  1130.                 return mTracking || mAnimating;
  1131.         }
  1132.  
  1133.         private class DrawerToggler
  1134.                 implements OnClickListener
  1135.         {
  1136.                 public void onClick(View v)
  1137.                 {
  1138.                         if (mLocked)
  1139.                         {
  1140.                                 return;
  1141.                         }
  1142.                         // mAllowSingleTap isn't relevant here; you're *always*
  1143.                         // allowed to open/close the drawer by clicking with the
  1144.                         // trackball.
  1145.  
  1146.                         if (mAnimateOnClick)
  1147.                         {
  1148.                                 animateToggle();
  1149.                         }
  1150.                         else
  1151.                         {
  1152.                                 toggle();
  1153.                         }
  1154.                 }
  1155.         }
  1156.  
  1157.         private class SlidingHandler
  1158.                 extends Handler
  1159.         {
  1160.                 public void handleMessage(Message m)
  1161.                 {
  1162.                         switch (m.what) {
  1163.                         case MSG_ANIMATE:
  1164.                                 doAnimation();
  1165.                                 break;
  1166.                         }
  1167.                 }
  1168.         }
  1169. }
clone this paste RAW Paste Data