项目需求很复杂,各种要求。官方的drawerlayout和开源的slidingmenu已经不能满足我们的需求了,所以MultiSlidingDrawer就诞生了,支持左、右、上、下。甚至是在其它容器里面。
源码:
package com.lee.ademo.view; import android.annotation.SuppressLint; 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.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.RotateAnimation; import com.lee.ademo.activity.R; public class MultiSlidingDrawer 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; 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(); } protected RotateAnimation openRotateAnimation; protected RotateAnimation closeRotateAnimation; public MultiSlidingDrawer(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MultiSlidingDrawer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiSlidingDrawer, defStyle, 0); int orientation = a.getInt(R.styleable.MultiSlidingDrawer_direction, ORIENTATION_BTT); mVertical = (orientation == ORIENTATION_BTT || orientation == ORIENTATION_TTB); mBottomOffset = (int) a.getDimension( R.styleable.MultiSlidingDrawer_bottomOffset, 0.0f); mTopOffset = (int) a.getDimension( R.styleable.MultiSlidingDrawer_topOffset, 0.0f); mAllowSingleTap = a.getBoolean( R.styleable.MultiSlidingDrawer_allowSingleTap, true); mAnimateOnClick = a.getBoolean( R.styleable.MultiSlidingDrawer_animateOnClick, true); mInvert = (orientation == ORIENTATION_TTB || orientation == ORIENTATION_LTR); int handleId = a .getResourceId(R.styleable.MultiSlidingDrawer_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.MultiSlidingDrawer_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); if (openRotateAnimation == null) { openRotateAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); openRotateAnimation.setFillAfter(true); } if (closeRotateAnimation == null) { closeRotateAnimation = new RotateAnimation(0, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); closeRotateAnimation.setFillAfter(true); } } @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); int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) { throw new RuntimeException( "SlidingDrawer cannot have UNSPECIFIED dimensions"); } final View handle = mHandle; measureChild(handle, widthMeasureSpec, heightMeasureSpec); if (mVertical) { int height = heightSpecSize - handle.getMeasuredHeight() - mTopOffset; mContent.measure(MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); } else { int width = widthSpecSize - handle.getMeasuredWidth() - mTopOffset; mContent.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( heightSpecSize, MeasureSpec.EXACTLY)); } setMeasuredDimension(widthSpecSize, heightSpecSize); } @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); } } @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; int handleWidth = handle.getMeasuredWidth(); int handleHeight = handle.getMeasuredHeight(); int handleLeft; int handleTop; final View content = mContent; if (mVertical) { handleLeft = (width - handleWidth) / 2; if (mInvert) { 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_DOWN: break; 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); } 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; c1 = mInvert ? velocity < mMaximumMajorVelocity : velocity > mMaximumMajorVelocity; c2 = mInvert ? (bottom - (position + handleHeight)) + mBottomOffset > handleHeight : position > mTopOffset + (mVertical ? mHandleHeight : mHandleWidth); c3 = mInvert ? velocity < -mMaximumMajorVelocity : velocity > -mMaximumMajorVelocity; 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; 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)); 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(); } mHandle.startAnimation(closeRotateAnimation); } private void openDrawer() { moveHandle(EXPANDED_FULL_OPEN); mContent.setVisibility(View.VISIBLE); if (mExpanded) { return; } mExpanded = true; if (mOnDrawerOpenListener != null) { mOnDrawerOpenListener.onDrawerOpened(); } mHandle.startAnimation(openRotateAnimation); } 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(); } } } @SuppressLint("HandlerLeak") private class SlidingHandler extends Handler { public void handleMessage(Message m) { switch (m.what) { case MSG_ANIMATE: doAnimation(); break; } } } }
- 本文固定链接: http://www.ithtw.com/208.html
- 不清楚的欢迎访问: IT十万为什么
相关推荐
第11讲:深入理解指针(1)
springboot整合 freemarker方法
第14讲:深入理解指针(4)
《同行者4.1.2语音助手:车机版安装详解》 在现代科技日新月异的时代,智能车载设备已经成为了汽车生活的重要组成部分。"同行者4.1.2"便是这样一款专为车机设计的语音助手,旨在提供更为便捷、安全的驾驶体验。该版本针对掌讯全系列设备进行了兼容优化,让车主能够轻松实现语音控制,减少驾驶过程中的手动操作,提升行车安全性。 我们来了解下"同行者4.1.2"的核心功能。这款语音助手集成了智能语音识别技术,用户可以通过简单的语音指令完成导航、音乐播放、电话拨打等一系列操作,有效避免了因操作手机或车机带来的分心。此外,其强大的语义理解和自学习能力,使得它能逐步适应用户的口音和习惯,提供更个性化的服务。 在安装过程中,用户需要注意的是,"同行者4.1.2"包含了四个核心组件,分别是: 1. TXZCore.apk:这是同行者语音助手的基础框架,包含了语音识别和处理的核心算法,是整个应用运行的基础。 2. com.txznet.comm.base.BaseApplication.apk:这个文件可能包含了应用的公共模块和基础服务,为其他组件提供支持。 3. TXZsetting.apk:这
市场拓展主管绩效考核表
“线上购车3D全方位体验:汽车模型展示与个性化定制功能”,three.js案例- 线上购车3d展示(源码) 包含内容:1.汽车模型展示;2.汽车肤;3.轮毂部件更;4.开关车门动画;5.汽车尺寸测量;6.自动驾驶;7.镜面倒影;8.hdr运用;9.移动端适配; 本为html+css+three.js源码 ,核心关键词:three.js案例; 线上购车3D展示; 汽车模型展示; 汽车换肤; 轮毂部件更换; 开关车门动画; 汽车尺寸测量; 自动驾驶; 镜面倒影; HDR运用; 移动端适配; HTML+CSS+three.js源码。,"Three.js源码:线上购车3D展示案例,含汽车模型、换肤、轮毂更换等九大功能"
数据名称:2000-2022年各县市区主要社会经济发展指标面板数据 数据类型:dta格式 数据来源:中国县域统计
一、智慧环卫管理平台的建设背景与目标 智慧环卫管理平台的建设源于对环卫管理全面升级的需求。当前,城管局已拥有139辆配备车载GPS系统、摄像头和油耗传感器的环卫车辆,但环卫人员尚未配备智能移动终端,公厕也缺乏信息化系统和智能终端设备。为了提升环卫作业效率、实现精细化管理并节省开支,智慧环卫管理平台应运而生。该平台旨在通过信息化技术和软硬件设备,如车载智能终端和环卫手机App,实时了解环卫人员、车辆的工作状态、信息和历史记录,使环卫作业管理透明化、精细化。同时,平台还期望通过数据模型搭建和数据研读,实现更合理的环卫动态资源配置,为环卫工作的科学、健康、持续发展提供决策支持。 二、智慧环卫管理平台的建设内容与功能 智慧环卫管理平台的建设内容包括运行机制体制建设、业务流程设计、智慧公厕系统建设、网络建设、主机和储存平台需求、平台运维管理体系、硬件标准规范体系以及考核评价体系等多个方面。其中,智慧公厕系统建设尤为关键,它能实时监控公厕运行状态,保障公厕的清洁和正常运行。平台建设还充分利用了现有的电子政务网络资源,并考虑了有线和无线网络的需求。在功能上,平台通过普查、整合等手段全面收集环卫车辆、企业、人员、设施、设备等数据,建立智慧环卫基础数据库。利用智能传感、卫星定位等技术实现环卫作业的在线监管和远程监控,实现对道路、公共场所等的作业状况和卫生状况的全面监管。此外,平台还建立了环卫作业网格化管理责任机制,实现从作业过程到结果的全面监管,科学评价区域、部门、单位和人员的作业效果。 三、智慧环卫管理平台的效益与风险规避 智慧环卫管理平台的建设将带来显著的环境、经济和管理效益。环境方面,它将有力推进环境卫生监管服务工作,改善环境卫生状况,为人民群众创造更加清洁、卫生的工作和生活环境。经济方面,通过智慧化监管,大大降低了传统管理手段的成本,提高了监管的准确性和效率。管理方面,平台能够追踪溯源市民反映的问题,如公厕异味、渣土车辆抛洒等,并找到相应的责任单位进行处置,防止类似事件再次发生。同时,平台还拥有强大的预警机制功能,能够在很多环卫问题尚未出现前进行处置。然而,平台建设也面临一定的风险,如部门协调、配合问题,建设单位选择风险以及不可预测的自然灾害等。为了规避这些风险,需要加强领导、统一思想,选择优秀的系统集成商承接项目建设,并做好计算机和应用系统的培训工作。同时,也要注意标准制定工作和相关法律法规的制定工作,以保证系统建设完成后能够真正为环卫管理工作带来便利。
36 -企业管理主管绩效考核表1
1.1 -1.4 工程代码
USDT合约,USDT智能合约
基于姿态估计三维人脸形状重建.pdf
一般员工绩效考核表模板(通用版) (2)
全国各省295地级市互联网普及率、互联网用户数、每百人互联网宽带用户(2011-2022年) 数据年份:2011-2022年(2022存在部分缺失) 数据范围:全国各省295个地级市 数据来源:地方统计局
一、各省、分行业CO2排放、283个地级市碳排放及计算过程 2.分行业二氧化碳排放量 在这里插入图片描述 3、280多个地级市碳排放及计算过程 二、碳中和文献、最新政策、碳金融数据+数学建模 1.二氧化碳减排规划,碳金融数据收集及数学建模 2.碳中和政策和下载量最高的碳中和论文 三、碳排放+碳市场+碳交易+碳中和+碳排放核算Excel自动计算表 全行业碳排放核算Excel自动计算表 四、碳交易数据 五、主要能源碳排放计算参数
第20讲:自定义类型:结构体
视觉跟踪算法综述.pdf
MATLAB超效率SBM-DEA模型代码详解:简易操作指南及期望与非期望产出的超效率分析,附Malmquist指数与分解功能,MATLAB的超效率SBM-DEA模型代码(有安装教程和内容讲解之类的东西),操作很简单 可以做期望产出和非期望产出的超效率和非超效率sbm模型和Malmquist指数和分解 ,MATLAB; SBM-DEA模型; 超效率SBM-DEA; 安装教程; 内容讲解; 期望产出; 非期望产出; 超效率与非超效率sbm模型; Malmquist指数; 分解。,"MATLAB超效SBM-DEA模型代码:非期望产出分析的便捷工具"
人事行政主管绩效考核评分表
人力资源管理工具绩效考核excel模板