分享一个自定义的SlidingDrawer,可控大小

来源:互联网 发布:one note for mac 编辑:程序博客网 时间:2024/05/24 04:47

我在做这方面的应用的时候,就曾用到SlidingDrawer来做一个下拉的广告Bener,但是直接使用SlidingDrawer却有很多不好的地方,比如说会占用主页面,经过网上查找的很多资料,才找到这么一个好用的自定义的SlidingDrawer灯,做出来效果不错,特意分享一下,忘了是从哪位高人那里找到的,但是在此表明本人不是原创作者,只是在别人的成果上做一点小修改,废话少说,直接进入正题:

首先定义好一个SlidingDrawer类:

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.DisplayMetrics;
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 com.application.activity.R;

public class MultiDirectionSlidingDrawer 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_FULL_CLOSED = -10002;

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;
//一开始时不隐藏mExpanded=true
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 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;

public static interface OnDrawerOpenListener {

public void onDrawerOpened();
}

public static interface OnDrawerCloseListener {

public void onDrawerClosed();
}

public static interface OnDrawerScrollListener {

public void onScrollStarted();

public void onScrollEnded();
}

public MultiDirectionSlidingDrawer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public MultiDirectionSlidingDrawer(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.MultiDirectionSlidingDrawer, defStyle, 0);

int orientation = a.getInt(
R.styleable.MultiDirectionSlidingDrawer_direction,
ORIENTATION_BTT);
mVertical = (orientation == ORIENTATION_BTT || orientation == ORIENTATION_TTB);
mBottomOffset = (int) a.getDimension(
R.styleable.MultiDirectionSlidingDrawer_bottomOffset, 0.0f);
mTopOffset = (int) a.getDimension(
R.styleable.MultiDirectionSlidingDrawer_topOffset, 0.0f);
mAllowSingleTap = a.getBoolean(
R.styleable.MultiDirectionSlidingDrawer_allowSingleTap, true);
mAnimateOnClick = a.getBoolean(
R.styleable.MultiDirectionSlidingDrawer_animateOnClick, true);
mInvert = (orientation == ORIENTATION_TTB || orientation == ORIENTATION_LTR);

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

@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.GONE);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
//TODO 2013.6.15

DisplayMetrics dm = new DisplayMetrics();
dm = getResources().getDisplayMetrics();
//int heightSpecSize = 608;
int heightSpecSize = 2*dm.heightPixels/3;

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) {
//TODO 213
//int height = 440;
int height = 2*dm.heightPixels/3 - 158;
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);
}

@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) {
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";

@Override
protected void onLayout(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;

//getMeasuredWidth 得到的是某个View想要在parent view里面占的大小
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;
if (mInvert) {
Log.d(LOG_TAG, "content.layout(1)");
handleTop = mExpanded ? height - mBottomOffset - handleHeight
: mTopOffset;
content.layout(0, mTopOffset, content.getMeasuredWidth(),
mTopOffset + content.getMeasuredHeight());
} else {
handleTop = mExpanded ? mTopOffset : height - handleHeight
+ mBottomOffset;
content.layout(0, mTopOffset + handleHeight,
content.getMeasuredWidth(), mTopOffset + handleHeight
+ content.getMeasuredHeight());
}
} else {
handleTop = (height - handleHeight) / 2;
if (mInvert) {
handleLeft = mExpanded ? width - mBottomOffset - handleWidth
: mTopOffset;
content.layout(mTopOffset, 0,
mTopOffset + content.getMeasuredWidth(),
content.getMeasuredHeight());
} else {
handleLeft = mExpanded ? mTopOffset : width - handleWidth
+ mBottomOffset;
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();
}

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

@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;
}
// fix by Maciej Ciemięga.
if ((!mInvert && xVelocity > mMaximumMinorVelocity)
|| (mInvert && xVelocity < mMaximumMinorVelocity)) {
xVelocity = mMaximumMinorVelocity;
}
} else {
negative = xVelocity < 0;
if (yVelocity < 0) {
yVelocity = -yVelocity;
}
// fix by Maciej Ciemięga.
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);
c3 = (mExpanded && (getRight() - handleRight) < mTapThreshold
+ mBottomOffset);
c4 = (!mExpanded && handleLeft > mTopOffset
+ mHandleWidth + mTapThreshold);
} else {
c1 = (mExpanded && handleTop < mTapThreshold
+ mTopOffset);
c2 = (!mExpanded && handleTop > mBottomOffset
+ getBottom() - getTop() - mHandleHeight
- mTapThreshold);
c3 = (mExpanded && handleLeft < mTapThreshold
+ mTopOffset);
c4 = (!mExpanded && handleLeft > mBottomOffset
+ getRight() - getLeft() - mHandleWidth
- 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);
}

private void animateClose(int position) {
prepareTracking(position);
performFling(position, mMaximumAcceleration, true);
}

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

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

private void prepareTracking(int position) {
mTracking = true;
mVelocityTracker = VelocityTracker.obtain();
boolean opening = !mExpanded;

if (opening) {
mAnimatedAcceleration = mMaximumAcceleration;
mAnimatedVelocity = mMaximumMajorVelocity;
if (mInvert)
mAnimationPosition = mTopOffset;
else
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);
}
}

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_FULL_CLOSED) {
if (mInvert) {
handle.offsetTopAndBottom(mTopOffset - handle.getTop());
} else {
handle.offsetTopAndBottom(mBottomOffset + getBottom()
- getTop() - mHandleHeight - handle.getTop());
}
invalidate();
} else {
final int top = handle.getTop();
int deltaY = position - top;
if (position < mTopOffset) {
deltaY = mTopOffset - top;
} else if (deltaY > mBottomOffset + getBottom() - getTop()
- mHandleHeight - top) {
deltaY = mBottomOffset + getBottom() - getTop()
- 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) {
if (mInvert)
handle.offsetLeftAndRight(mBottomOffset + getRight()
- getLeft() - mHandleWidth);
else
handle.offsetLeftAndRight(mTopOffset - handle.getLeft());
invalidate();
} else if (position == COLLAPSED_FULL_CLOSED) {
if (mInvert)
handle.offsetLeftAndRight(mTopOffset - handle.getLeft());
else
handle.offsetLeftAndRight(mBottomOffset + getRight()
- getLeft() - mHandleWidth - handle.getLeft());
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;
}
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);
}
}
}

private 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.EXACTLY));

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(MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(
getBottom() - getTop(), MeasureSpec.EXACTLY));

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

private void stopTracking() {
mHandle.setPressed(false);
mTracking = false;

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

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

private void doAnimation() {
if (mAnimating) {
incrementAnimation();

if (mInvert) {
if (mAnimationPosition < mTopOffset) {
mAnimating = false;
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);
}
}
}
}

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
}

public void toggle() {
if (!mExpanded) {
openDrawer();
} else {
closeDrawer();
}
invalidate();
requestLayout();
}

public void animateToggle() {
if (!mExpanded) {
animateOpen();
} else {
animateClose();
}
}

public void open() {
openDrawer();
invalidate();
requestLayout();

sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}

public void close() {
closeDrawer();
invalidate();
requestLayout();
}

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

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

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

private void closeDrawer() {
moveHandle(COLLAPSED_FULL_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();
}
}

public void setOnDrawerOpenListener(
OnDrawerOpenListener onDrawerOpenListener) {
mOnDrawerOpenListener = onDrawerOpenListener;
}

public void setOnDrawerCloseListener(
OnDrawerCloseListener onDrawerCloseListener) {
mOnDrawerCloseListener = onDrawerCloseListener;
}

public void setOnDrawerScrollListener(
OnDrawerScrollListener onDrawerScrollListener) {
mOnDrawerScrollListener = onDrawerScrollListener;
}

public View getHandle() {
return mHandle;
}

public View getContent() {
return mContent;
}

public void unlock() {
mLocked = false;
}

public void lock() {
mLocked = true;
}

public boolean isOpened() {
return mExpanded;
}

public boolean isMoving() {
return mTracking || mAnimating;
}

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

private class SlidingHandler extends Handler {

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

然后在布局里直接像Android的Buttom控件等一样的使用方法:

<com.application.wetgit.MultiDirectionSlidingDrawer
            android:id="@+id/drawer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="0dp"
            my:content="@+id/content"
            my:direction="topToBottom"
            my:handle="@+id/handle" >

            <include
                android:id="@id/content"
                layout="@layout/pen_content" />

            <ImageView
                android:id="@id/handle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/vss_flying_32x" />
        </com.application.wetgit.MultiDirectionSlidingDrawer>

最后还有在values文件下建一个attrs.xml,里面的内容为:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="direction">
        <enum name="rightToLeft" value="0" />
        <enum name="bottomToTop" value="1" />
        <enum name="leftToRight" value="2" />
        <enum name="topToBottom" value="3" />
    </attr>

    <declare-styleable name="MultiDirectionSlidingDrawer">
        <attr name="handle" format="reference" />
        <attr name="content" format="reference" />
        <attr name="direction" />
        <attr name="bottomOffset" format="dimension" />
        <attr name="topOffset" format="dimension" />
        <attr name="allowSingleTap" format="boolean" />
        <attr name="animateOnClick" format="boolean" />
    </declare-styleable>

</resources>

注意在使用这个控件的布局主页面上要加上: xmlns:my="http://schemas.android.com/apk/res/com.application.activity"这里是这个控件所显示的Activity所在的包名。

有什么不对的地方,请大家多多指教。最后发两张我所做出来的效果截图:

 

本文转载,原文:http://blog.csdn.net/highning0007/article/details/9355963

原创粉丝点击