开源项目之Android undergarment(导航)

来源:互联网 发布:木子软件 官网app 编辑:程序博客网 时间:2024/06/07 13:26

undergarment 是 Android 上实现滑盖导航(抽屉)效果的 UI 组件。 项目封装成了静态库,其它程序直接引用,项目如图:



界面效果很给力,58同城 android版主界面采用了类似的效果,只不过起点和方向不一样,笔者已经实现了该效果,有机会贴出源码来,看效果:





不知道效果能不能得到58同城的认可,呵呵!看源码

public class DrawerGarment extends FrameLayout { // FrameLayout称为层布局,将组件显示在屏幕的左上角,后面的组件覆盖前面的组public static final int SLIDE_TARGET_CONTENT = 0; //public static final int SLIDE_TARGET_WINDOW = 1;private static final int SCROLL_DURATION = 400; // 滑动时间间距private static final float TOUCH_TARGET_WIDTH_DIP = 48.0f;private boolean mAdded = false; // 是否添加private boolean mDrawerEnabled = true; // 是否自绘private boolean mDrawerOpened = false; // 是否开打private boolean mDrawerMoving = false; // 是否移动private boolean mGestureStarted = false; // 是否支持手势private int mDecorContentBackgroundColor = Color.TRANSPARENT; // 背景色private int mDecorOffsetX = 0; //private int mDrawerMaxWidth = WRAP_CONTENT;private int mDrawerWidth;private int mGestureStartX;private int mGestureCurrentX;private int mGestureStartY;private int mGestureCurrentY;private int mSlideTarget;private int mTouchTargetWidth;private Drawable mShadowDrawable;private Handler mScrollerHandler;private Scroller mScroller; // 滚动条private ViewGroup mDecorView;private ViewGroup mContentTarget;private ViewGroup mContentTargetParent;private ViewGroup mWindowTarget;private ViewGroup mWindowTargetParent;private ViewGroup mDecorContent;private ViewGroup mDecorContentParent;private ViewGroup mDrawerContent;private Runnable mDrawOpenRunnable, mDrawCloseRunnable;private VelocityTracker mVelocityTracker;private IDrawerCallbacks mDrawerCallbacks;public static interface IDrawerCallbacks { // 回调打开/关闭public void onDrawerOpened();public void onDrawerClosed();}// interpolator被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果可以accelerated(加速),decelerated(减速),repeated(重复),bounced(弹跳)等。public static class SmoothInterpolator implements Interpolator {@Overridepublic float getInterpolation(float v) {return (float) (Math.pow((double) v - 1.0, 5.0) + 1.0f);}}public void reconfigureViewHierarchy() { // 重新配置视图层次final DisplayMetrics dm = getResources().getDisplayMetrics();final int widthPixels = dm.widthPixels;if (mDecorView == null) {return;}if (mDrawerContent != null) {removeView(mDrawerContent);}if (mDecorContent != null) {// 添加窗口/内容removeView(mDecorContent);mDecorContentParent.addView(mDecorContent);// 取消单击监听 背景透明mDecorContent.setOnClickListener(null);mDecorContent.setBackgroundColor(Color.TRANSPARENT);}if (mAdded) {mDecorContentParent.removeView(this);}if (mSlideTarget == SLIDE_TARGET_CONTENT) {mDecorContent = mContentTarget;mDecorContentParent = mContentTargetParent;} else if (mSlideTarget == SLIDE_TARGET_WINDOW) {mDecorContent = mWindowTarget;mDecorContentParent = mWindowTargetParent;} else {throw new IllegalArgumentException("Slide target must be one of SLIDE_TARGET_CONTENT or SLIDE_TARGET_WINDOW.");}((ViewGroup) mDecorContent.getParent()).removeView(mDecorContent);addView(mDrawerContent, new ViewGroup.LayoutParams(mDrawerMaxWidth,MATCH_PARENT));addView(mDecorContent, new ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));mDecorContentParent.addView(this);mAdded = true;mDecorContent.setBackgroundColor(mDecorContentBackgroundColor); // 设置背景mShadowDrawable.setBounds(-mTouchTargetWidth / 6, 0, 0, dm.heightPixels); // 重设大小// 防止关闭mDecorContent.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View view) {}});}public DrawerGarment(Activity activity, int drawerLayout) {super(activity);final DisplayMetrics dm = activity.getResources().getDisplayMetrics();mTouchTargetWidth = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, TOUCH_TARGET_WIDTH_DIP, dm));mShadowDrawable = getResources().getDrawable(R.drawable.decor_shadow);mScrollerHandler = new Handler();mScroller = new Scroller(activity, new SmoothInterpolator());// 默认针对整个窗口mSlideTarget = SLIDE_TARGET_WINDOW;mDecorView = (ViewGroup) activity.getWindow().getDecorView();mWindowTarget = (ViewGroup) mDecorView.getChildAt(0);mWindowTargetParent = (ViewGroup) mWindowTarget.getParent();mContentTarget = (ViewGroup) mDecorView.findViewById(android.R.id.content);mContentTargetParent = (ViewGroup) mContentTarget.getParent();mDrawerContent = (ViewGroup) LayoutInflater.from(activity).inflate(drawerLayout, null);mDrawerContent.setVisibility(INVISIBLE);// 重新配置视图层次reconfigureViewHierarchy();/* * This currently causes lock-ups on 10" tablets (e.g., Xoom & * Transformer), should probably look into why this is happening. *  * if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { * setLayerType(LAYER_TYPE_HARDWARE, null); } */}@Overrideprotected void onLayout(boolean changed, int left, int top, int right,int bottom) { // 用于指定所有子视图的位置Rect windowRect = new Rect();mDecorView.getWindowVisibleDisplayFrame(windowRect);if (mSlideTarget == SLIDE_TARGET_WINDOW) {mDrawerContent.layout(left, top + windowRect.top, right, bottom);mDecorContent.layout(mDecorContent.getLeft(),mDecorContent.getTop(), mDecorContent.getLeft() + right,bottom);} else {mDrawerContent.layout(left, 0, right, bottom);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {mDecorContent.layout(mDecorContent.getLeft(), 0,mDecorContent.getLeft() + right, bottom);} else {mDecorContent.layout(mDecorContent.getLeft(), top,mDecorContent.getLeft() + right, bottom);}}mDrawerWidth = mDrawerContent.getMeasuredWidth();if (mDrawerWidth > right - mTouchTargetWidth) {mDrawerContent.setPadding(0, 0, mTouchTargetWidth, 0);mDrawerWidth -= mTouchTargetWidth;}}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { //触碰响应final ViewConfiguration vc = ViewConfiguration.get(getContext());final float touchThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30.0f, getResources().getDisplayMetrics());final int widthPixels = getResources().getDisplayMetrics().widthPixels;final double hypo;final boolean overcameSlop;/* Immediately bomb out if the drawer is disabled */if (!mDrawerEnabled) {return false;}/* * ...otherwise, handle the various types of input events. */switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:/* * Record the starting X and Y positions for the possible gesture. */mGestureStartX = mGestureCurrentX = (int) (ev.getX() + 0.5f);mGestureStartY = mGestureCurrentY = (int) (ev.getY() + 0.5f);/* * If the starting X position is within the touch threshold of 30dp * inside the screen's left edge, set mGestureStared to true so that * future ACTION_MOVE events will continue being handled here. */if (mGestureStartX < touchThreshold && !mDrawerOpened) {mGestureStarted = true;}if (mGestureStartX > mDrawerWidth && mDrawerOpened) {mGestureStarted = true;}if (mDrawerMoving && mGestureStartX > mDecorOffsetX) {return true;}/* * We still want to return false here since we aren't positive we've * got a gesture we want just yet. */return false;case MotionEvent.ACTION_MOVE:/* * Make sure the gesture was started within 30dp of the screen's * left edge. */if (!mGestureStarted) {return false;}/* * Make sure we're not going backwards, but only if the drawer isn't * open yet */if (!mDrawerOpened&& (ev.getX() < mGestureCurrentX || ev.getX() < mGestureStartX)) {return (mGestureStarted = false);}/* * Update the current X and Y positions for the gesture. */mGestureCurrentX = (int) (ev.getX() + 0.5f);mGestureCurrentY = (int) (ev.getY() + 0.5f);/* * Decide whether there is enough movement to do anything real. */hypo = Math.hypot(mGestureCurrentX - mGestureStartX,mGestureCurrentY - mGestureStartY);overcameSlop = hypo > vc.getScaledTouchSlop();/* * If the last check is true, we'll start handling events in * DrawerGarment's onTouchEvent(MotionEvent) method from now on. */return overcameSlop;case MotionEvent.ACTION_UP:mGestureStarted = false;/* * If we just tapped the right edge with the drawer open, close the * drawer. */if (mGestureStartX > mDrawerWidth && mDrawerOpened) {closeDrawer();mGestureStartX = mGestureCurrentX = -1;mGestureStartY = mGestureCurrentY = -1;return true;} else {mGestureStartX = mGestureCurrentX = -1;mGestureStartY = mGestureCurrentY = -1;return false;}}return false;}@Overridepublic boolean onTouchEvent(MotionEvent event) { //子视图响应完触发final ViewConfiguration vc = ViewConfiguration.get(getContext());final int widthPixels = getResources().getDisplayMetrics().widthPixels;final int deltaX = (int) (event.getX() + 0.5f) - mGestureCurrentX;final int deltaY = (int) (event.getY() + 0.5f) - mGestureCurrentY;/* * Obtain a new VelocityTracker if we don't already have one. Also add * this MotionEvent to the new/existing VelocityTracker so we can * determine flings later on. */if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(event);/* * Update the current X and Y positions for the ongoing gesture. */mGestureCurrentX = (int) (event.getX() + 0.5f);mGestureCurrentY = (int) (event.getY() + 0.5f);switch (event.getAction()) {case MotionEvent.ACTION_MOVE:mDrawerContent.setVisibility(VISIBLE);mDrawerMoving = true;if (mDecorOffsetX + deltaX > mDrawerWidth) {if (mDecorOffsetX != mDrawerWidth) {mDrawerOpened = true;mDecorContent.offsetLeftAndRight(mDrawerWidth- mDecorOffsetX);mDecorOffsetX = mDrawerWidth;invalidate();}} else if (mDecorOffsetX + deltaX < 0) {if (mDecorOffsetX != 0) {mDrawerOpened = false;mDecorContent.offsetLeftAndRight(0 - mDecorContent.getLeft());mDecorOffsetX = 0;invalidate();}} else {mDecorContent.offsetLeftAndRight(deltaX);mDecorOffsetX += deltaX;invalidate();}return true;case MotionEvent.ACTION_UP:mGestureStarted = false;mDrawerMoving = false;/* * Determine if the user performed a fling based on the final * velocity of the gesture. */mVelocityTracker.computeCurrentVelocity(1000);if (Math.abs(mVelocityTracker.getXVelocity()) > vc.getScaledMinimumFlingVelocity()) {/* * Okay, the user did a fling, so determine the direction in * which the fling was flung so we know which way to toggle the * drawer state. */if (mVelocityTracker.getXVelocity() > 0) {mDrawerOpened = false;openDrawer();} else {mDrawerOpened = true;closeDrawer();}} else {/* * No sizable fling has been flung, so fling the drawer towards * whichever side we're closest to being flung at. */if (mDecorOffsetX > (widthPixels / 2.0)) {mDrawerOpened = false;openDrawer();} else {mDrawerOpened = true;closeDrawer();}}return true;}return false;}@Overrideprotected void dispatchDraw(Canvas canvas) { //绘制子视图super.dispatchDraw(canvas);if (mDrawerOpened || mDrawerMoving) {canvas.save();canvas.translate(mDecorOffsetX, 0);mShadowDrawable.draw(canvas);canvas.restore();}}//设置颜色public void setDecorContentBackgroundColor(final int color) {mDecorContentBackgroundColor = color;}public int getDecorContentBackgroundColor() {return mDecorContentBackgroundColor;}//设置目标宽度public void setTouchTargetWidth(final int width) {mTouchTargetWidth = width;}public int getTouchTargetWidth() {return mTouchTargetWidth;}/** * Sets the maximum width in pixels the drawer will open to. Default is * WRAP_CONTENT. Can also be MATCH_PARENT or another value in pixels. *  * @param maxWidth */public void setDrawerMaxWidth(final int maxWidth) {mDrawerMaxWidth = maxWidth;}public int getDrawerMaxWidth() {return mDrawerMaxWidth;}public void setDrawerEnabled(final boolean enabled) {mDrawerEnabled = enabled;}public boolean isDrawerEnabled() {return mDrawerEnabled;}public void toggleDrawer(final boolean animate) {if (!mDrawerOpened) {openDrawer(animate);} else {closeDrawer(animate);}}public void toggleDrawer() {toggleDrawer(true);}public void openDrawer(final boolean animate) {if (mDrawerMoving) {mScrollerHandler.removeCallbacks(mDrawCloseRunnable);mScrollerHandler.removeCallbacks(mDrawOpenRunnable);}if (mDrawerOpened) {return;}mDrawerContent.setVisibility(VISIBLE);mDrawerMoving = true;final int widthPixels = getResources().getDisplayMetrics().widthPixels;if (mDrawerWidth > widthPixels - mTouchTargetWidth) {mScroller.startScroll(mDecorOffsetX, 0,(widthPixels - mTouchTargetWidth) - mDecorOffsetX, 0,animate ? SCROLL_DURATION : 0);} else {mScroller.startScroll(mDecorOffsetX, 0, mDrawerWidth- mDecorOffsetX, 0, animate ? SCROLL_DURATION : 0);}mDrawOpenRunnable = new Runnable() {@Overridepublic void run() {final boolean scrolling = mScroller.computeScrollOffset();mDecorContent.offsetLeftAndRight(mScroller.getCurrX()- mDecorOffsetX);mDecorOffsetX = mScroller.getCurrX();postInvalidate();if (!scrolling) {mDrawerMoving = false;mDrawerOpened = true;if (mDrawerCallbacks != null) {mScrollerHandler.post(new Runnable() {@Overridepublic void run() {mDrawerCallbacks.onDrawerOpened();}});}} else {mScrollerHandler.post(this);}}};mScrollerHandler.post(mDrawOpenRunnable);}public void openDrawer() {openDrawer(true);}public void closeDrawer(final boolean animate) {if (mDrawerMoving) {mScrollerHandler.removeCallbacks(mDrawCloseRunnable);mScrollerHandler.removeCallbacks(mDrawOpenRunnable);} else if (!mDrawerOpened) {return;}mDrawerMoving = true;final int widthPixels = getResources().getDisplayMetrics().widthPixels;mScroller.startScroll(mDecorOffsetX, 0, -mDecorOffsetX, 0,animate ? SCROLL_DURATION : 0);mDrawCloseRunnable = new Runnable() {@Overridepublic void run() {final boolean scrolling = mScroller.computeScrollOffset();mDecorContent.offsetLeftAndRight(mScroller.getCurrX()- mDecorOffsetX);mDecorOffsetX = mScroller.getCurrX();postInvalidate();if (!scrolling) {mDrawerMoving = false;mDrawerOpened = false;mDrawerContent.setVisibility(INVISIBLE);if (mDrawerCallbacks != null) {mScrollerHandler.post(new Runnable() {@Overridepublic void run() {mDrawerCallbacks.onDrawerClosed();}});}} else {mScrollerHandler.post(this);}}};mScrollerHandler.post(mDrawCloseRunnable);}public void closeDrawer() {closeDrawer(true);}public boolean isDrawerOpened() {return mDrawerOpened;}public boolean isDrawerMoving() {return mDrawerMoving;}public void setDrawerCallbacks(final IDrawerCallbacks callbacks) {mDrawerCallbacks = callbacks;}public IDrawerCallbacks getDrawerCallbacks() {return mDrawerCallbacks;}public int getSlideTarget() {return mSlideTarget;}public void setSlideTarget(final int slideTarget) {if (mSlideTarget != slideTarget) {mSlideTarget = slideTarget;reconfigureViewHierarchy();}}}

呵呵,是不是非常的简单?

项目下载

原创粉丝点击