- 浏览: 5820186 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (890)
- WindowsPhone (0)
- android (88)
- android快速迭代 (17)
- android基础 (34)
- android进阶 (172)
- android高级 (0)
- android拾遗 (85)
- android动画&效果 (68)
- Material Design (13)
- LUA (5)
- j2me (32)
- jQuery (39)
- spring (26)
- hibernate (20)
- struts (26)
- tomcat (9)
- javascript+css+html (62)
- jsp+servlet+javabean (14)
- java (37)
- velocity+FCKeditor (13)
- linux+批处理 (9)
- mysql (19)
- MyEclipse (9)
- ajax (7)
- wap (8)
- j2ee+apache (24)
- 其他 (13)
- phonegap (35)
最新评论
-
Memories_NC:
本地lua脚本终于执行成功了,虽然不是通过redis
java中调用lua脚本语言1 -
ZHOU452840622:
大神://处理返回的接收状态 这个好像没有监听到 遇 ...
android 发送短信的两种方式 -
PXY:
拦截部分地址,怎么写的for(int i=0;i<lis ...
判断是否登录的拦截器SessionFilter -
maotou1988:
Android控件之带清空按钮(功能)的AutoComplet ...
自定义AutoCompleteTextView -
yangmaolinpl:
希望有表例子更好。。。,不过也看明白了。
浅谈onInterceptTouchEvent、onTouchEvent与onTouch
http://blog.sephiroth.it/2011/03/29/widget-slidingdrawer-top-to-bottom/
你好,能请教一下具体是哪句代码吗?因为行数方面可能不太一样
543 Log.d( LOG_TAG, "COLLAPSED. always: " + always + ", c1: " 545 + c1 + ", c2: " + c2 + ", c3: " + c3 );
545 if ( !always && ( c1 || ( c2 && c3 ) ) ) {
546 mAnimatedAcceleration = mMaximumAcceleration;
本文546行的 mAnimatedAcceleration = mMaximumAcceleration;
改为 mAnimatedAcceleration = -mMaximumAcceleration;
就可以实现 左-右的handle点击 打开了..- -刚测试出来的 ...希望能看到^^
你好,能请教一下具体是哪句代码吗?因为行数方面可能不太一样
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Modifications by: Alessandro Crugnola */ package it.sephiroth.demo.slider.widget; import it.sephiroth.demo.slider.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; 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; 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; /** * Callback invoked when the drawer is opened. */ public static interface OnDrawerOpenListener { /** * Invoked when the drawer becomes fully open. */ public void onDrawerOpened(); } /** * Callback invoked when the drawer is closed. */ public static interface OnDrawerCloseListener { /** * Invoked when the drawer becomes fully closed. */ public void onDrawerClosed(); } /** * Callback invoked when the drawer is scrolled. */ public static interface OnDrawerScrollListener { /** * Invoked when the user starts dragging/flinging the drawer's handle. */ public void onScrollStarted(); /** * Invoked when the user stops dragging/flinging the drawer's handle. */ public void onScrollEnded(); } /** * Creates a new SlidingDrawer from a specified set of attributes defined in * XML. * * @param context * The application's environment. * @param attrs * The attributes defined in XML. */ public MultiDirectionSlidingDrawer( Context context, AttributeSet attrs ) { this( context, attrs, 0 ); } /** * Creates a new SlidingDrawer from a specified set of attributes defined in * XML. * * @param context * The application's environment. * @param attrs * The attributes defined in XML. * @param defStyle * The style to apply to this widget. */ public 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 ); 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 ); } } 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; int handleWidth = handle.getMeasuredWidth(); int handleHeight = handle.getMeasuredHeight(); Log.d( LOG_TAG, "handleHeight: " + handleHeight ); int handleLeft; int handleTop; final View content = mContent; if ( mVertical ) { handleLeft = ( width - handleWidth ) / 2; if ( mInvert ) { 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 } /** * Toggles the drawer open and close. Takes effect immediately. * * @see #open() * @see #close() * @see #animateClose() * @see #animateOpen() * @see #animateToggle() */ public void toggle() { if ( !mExpanded ) { openDrawer(); } else { closeDrawer(); } invalidate(); requestLayout(); } /** * Toggles the drawer open and close with an animation. * * @see #open() * @see #close() * @see #animateClose() * @see #animateOpen() * @see #toggle() */ public void animateToggle() { if ( !mExpanded ) { animateOpen(); } else { animateClose(); } } /** * Opens the drawer immediately. * * @see #toggle() * @see #close() * @see #animateOpen() */ public void open() { openDrawer(); invalidate(); requestLayout(); sendAccessibilityEvent( AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED ); } /** * Closes the drawer immediately. * * @see #toggle() * @see #open() * @see #animateClose() */ public void close() { closeDrawer(); invalidate(); requestLayout(); } /** * Closes the drawer with an animation. * * @see #close() * @see #open() * @see #animateOpen() * @see #animateToggle() * @see #toggle() */ public void animateClose() { prepareContent(); final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener; if ( scrollListener != null ) { scrollListener.onScrollStarted(); } animateClose( mVertical ? mHandle.getTop() : mHandle.getLeft() ); if ( scrollListener != null ) { scrollListener.onScrollEnded(); } } /** * Opens the drawer with an animation. * * @see #close() * @see #open() * @see #animateClose() * @see #animateToggle() * @see #toggle() */ public void animateOpen() { prepareContent(); final OnDrawerScrollListener scrollListener = mOnDrawerScrollListener; if ( scrollListener != null ) { scrollListener.onScrollStarted(); } animateOpen( mVertical ? mHandle.getTop() : mHandle.getLeft() ); sendAccessibilityEvent( AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED ); if ( scrollListener != null ) { scrollListener.onScrollEnded(); } } 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(); } } /** * Sets the listener that receives a notification when the drawer becomes * open. * * @param onDrawerOpenListener * The listener to be notified when the drawer is opened. */ public void setOnDrawerOpenListener( OnDrawerOpenListener onDrawerOpenListener ) { mOnDrawerOpenListener = onDrawerOpenListener; } /** * Sets the listener that receives a notification when the drawer becomes * close. * * @param onDrawerCloseListener * The listener to be notified when the drawer is closed. */ public void setOnDrawerCloseListener( OnDrawerCloseListener onDrawerCloseListener ) { mOnDrawerCloseListener = onDrawerCloseListener; } /** * Sets the listener that receives a notification when the drawer starts or * ends a scroll. A fling is considered as a scroll. A fling will also * trigger a drawer opened or drawer closed event. * * @param onDrawerScrollListener * The listener to be notified when scrolling starts or stops. */ public void setOnDrawerScrollListener( OnDrawerScrollListener onDrawerScrollListener ) { mOnDrawerScrollListener = onDrawerScrollListener; } /** * Returns the handle of the drawer. * * @return The View reprenseting the handle of the drawer, identified by the * "handle" id in XML. */ public View getHandle() { return mHandle; } /** * Returns the content of the drawer. * * @return The View reprenseting the content of the drawer, identified by the * "content" id in XML. */ public View getContent() { return mContent; } /** * Unlocks the SlidingDrawer so that touch events are processed. * * @see #lock() */ public void unlock() { mLocked = false; } /** * Locks the SlidingDrawer so that touch events are ignores. * * @see #unlock() */ public void lock() { mLocked = true; } /** * Indicates whether the drawer is currently fully opened. * * @return True if the drawer is opened, false otherwise. */ public boolean isOpened() { return mExpanded; } /** * Indicates whether the drawer is scrolling or flinging. * * @return True if the drawer is scroller or flinging, false otherwise. */ public boolean isMoving() { return mTracking || mAnimating; } 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; } } } }
package it.sephiroth.demo.slider; import it.sephiroth.demo.slider.widget.MultiDirectionSlidingDrawer; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.widget.Button; public class MainActivity extends Activity { Button mCloseButton; Button mOpenButton; MultiDirectionSlidingDrawer mDrawer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature( Window.FEATURE_NO_TITLE ); setContentView(R.layout.main); mCloseButton.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { mDrawer.animateClose(); } }); mOpenButton.setOnClickListener( new OnClickListener() { @Override public void onClick( View v ) { if( !mDrawer.isOpened() ) mDrawer.animateOpen(); } }); } @Override public void onContentChanged() { super.onContentChanged(); mCloseButton = (Button) findViewById( R.id.button_close ); mOpenButton = (Button) findViewById( R.id.button_open ); mDrawer = (MultiDirectionSlidingDrawer) findViewById( R.id.drawer ); } }
<?xml version="1.0" encoding="UTF-8"?> <RelativeLayout android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <Button android:layout_height="wrap_content" android:layout_width="100dp" android:visibility="gone" android:layout_centerInParent="true" android:text="@string/open" android:id="@+id/button_open"/> <it.sephiroth.demo.slider.widget.MultiDirectionSlidingDrawer android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/drawer" my:content="@+id/content" my:handle="@+id/handle" my:direction="topToBottom" xmlns:my="http://schemas.android.com/apk/res/it.sephiroth.demo.slider"> <include android:id="@id/content" layout="@layout/pen_content"/> <ImageView android:layout_height="40px" android:layout_width="wrap_content" android:id="@id/handle" android:src="@drawable/sliding_drawer_handle_bottom"/> </it.sephiroth.demo.slider.widget.MultiDirectionSlidingDrawer> </RelativeLayout>
- SlidingDrawerDemo1.zip (113.8 KB)
- 下载次数: 363
评论
6 楼
glmxzaj
2014-10-11
怎么content内容大小不会改变呢 一直是fillpartent
5 楼
postxx
2013-11-08
下滑后 第一个页面的按钮还能点击,怎么屏蔽呀
4 楼
zw6296149
2013-07-23
djjackylei 写道
zzheng.wang 写道
左到右点击无效的 530行 改成负的就行 感谢楼主分享
你好,能请教一下具体是哪句代码吗?因为行数方面可能不太一样
543 Log.d( LOG_TAG, "COLLAPSED. always: " + always + ", c1: " 545 + c1 + ", c2: " + c2 + ", c3: " + c3 );
545 if ( !always && ( c1 || ( c2 && c3 ) ) ) {
546 mAnimatedAcceleration = mMaximumAcceleration;
本文546行的 mAnimatedAcceleration = mMaximumAcceleration;
改为 mAnimatedAcceleration = -mMaximumAcceleration;
就可以实现 左-右的handle点击 打开了..- -刚测试出来的 ...希望能看到^^
3 楼
djjackylei
2013-05-08
zzheng.wang 写道
左到右点击无效的 530行 改成负的就行 感谢楼主分享
你好,能请教一下具体是哪句代码吗?因为行数方面可能不太一样
2 楼
zzheng.wang
2013-04-26
左到右点击无效的 530行 改成负的就行 感谢楼主分享
1 楼
djjackylei
2013-04-12
左-右方向的handle点击无效,拖动才能出来,不知道楼主是否有解决办法
发表评论
-
NestedScrollView滚动到顶部固定子View悬停挂靠粘在顶端
2018-10-31 20:45 6993网上有一个StickyScrollView,称之为粘性Scro ... -
自定义Behavior实现AppBarLayout越界弹性效果
2017-03-31 09:33 10369一、继承AppBarLayout.Beha ... -
Android - 一种相似图片搜索算法的实现
2017-03-31 09:33 2622算法 缩小尺寸。 将图片缩小到8x8的尺寸,总共64个 ... -
使用SpringAnimation实现带下拉弹簧动画的 ScrollView
2017-03-30 11:30 2848在刚推出的 Support Library 25.3.0 里面 ... -
Android为应用添加角标(Badge)
2017-03-30 11:21 61771.需求简介 角标是什么意思呢? 看下图即可明了: 可 ... -
Android端与笔记本利用局域网进行FTP通信
2017-03-23 10:17 978先看图 打开前: 打开后: Activity类 ... -
PorterDuffColorFilter 在项目中的基本使用
2017-03-03 10:58 1354有时候标题栏会浮在内容之上,而内容会有颜色的变化,这时候就要求 ... -
ColorAnimationView 实现了滑动Viewpager 时背景色动态变化的过渡效果
2017-02-24 09:41 2220用法在注释中: import android.anima ... -
迷你轻量级全方向完美滑动处理侧滑控件SlideLayout
2017-01-16 16:53 2594纯手工超级迷你轻量级全方向完美滑动处理侧滑控件(比官方 sup ... -
Effect
2017-01-05 09:57 0https://github.com/JetradarMobi ... -
动态主题库Colorful,容易地改变App的配色方案
2016-12-27 14:49 2565Colorful是一个动态主题库,允许您很容易地改变App的配 ... -
对视图的对角线切割DiagonalView
2016-12-27 14:23 1118提供对视图的对角线切割,具有很好的用户定制 基本用法 ... -
仿淘宝京东拖拽商品详情页上下滚动黏滞效果
2016-12-26 16:53 3494比较常用的效果,有现成的,如此甚好!:) import ... -
让任意view具有滑动效果的SlideUp
2016-12-26 09:26 1707基本的类,只有一个: import android.a ... -
AdvancedWebView
2016-12-21 09:44 16https://github.com/delight-im/A ... -
可设置圆角背景边框的按钮, 通过调节色彩明度自动计算按下(pressed)状态颜色
2016-11-02 22:13 1920可设置圆角背景边框的的按钮, 通过调节色彩明度自动计算按下(p ... -
网络请求库相关
2016-10-09 09:35 62https://github.com/amitshekhari ... -
ASimpleCache一个简单的缓存框架
2015-10-26 22:53 2178ASimpleCache 是一个为android制定的 轻量级 ... -
使用ViewDragHelper实现的DragLayout开门效果
2015-10-23 10:55 3415先看一下图,有个直观的了解,向下拖动handle就“开门了”: ... -
保证图片长宽比的同时拉伸图片ImageView
2015-10-16 15:40 3733按比例放大图片,不拉伸失真 import android. ...
相关推荐
在这个"四个方向的SlidingDrawer实例"中,我们将深入探讨如何创建一个能从上下左右四个方向滑出的抽屉效果,并为初学者提供学习指导。 首先,SlidingDrawer主要由两部分组成:handle(手柄)和content(内容区域)...
本文将详细介绍如何自定义一个可以支持四个方向——上、下、左、右滑动的SlidingDrawer。 首先,理解SlidingDrawer的基本结构和工作原理至关重要。SlidingDrawer由两部分组成:handle(手柄)和content(内容区域)...
SlidingDrawer有四个主要属性需要设置: 1. `android:handle`:这是抽屉的手柄,通常是一个ImageView或Button,用户可以通过点击或拖动它来打开或关闭抽屉。 2. `android:content`:定义抽屉的内容区域,可以包含...
**四、SlidingDrawer的替代方案** 由于Android SDK 21(Lollipop)开始,SlidingDrawer已被弃用,推荐使用其他组件来实现类似功能: 1. ** CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout**:适用于...
本篇将深入探讨“四个方向的抽屉控件”的实现和相关知识点。 一、抽屉控件的基本概念 1. 抽屉控件(Drawer):在UI设计中,抽屉通常以滑动的形式出现,可以从屏幕边缘滑出,提供导航、设置或其他操作选项。在...
滑动式抽屉(SlidingDrawer)是Android平台上一个独特的控件,它提供了一种隐藏和显示内容的方式,常用于创建类似抽屉的交互体验。在Android 1.5版本的模拟器中,进入应用程序列表时所展示的效果就是SlidingDrawer的...
标题"android 多方向抽屉"暗示我们将探讨的不只是传统的侧滑抽屉,而是支持上下左右多个方向滑动的抽屉效果。这种设计可以为应用提供更丰富的用户体验,使用户能够从四个不同的方向触发隐藏内容。 描述中的"android...
这种菜单通常从屏幕的左侧或右侧滑出,但此资源可能包含了一些不寻常的设计,允许菜单从上、下、左、右四个方向弹出。下面将详细讲解如何在Android中实现这种多功能的边菜单及其涉及的技术点。 1. **Sliding Drawer...
SlidingDrawer组件提供了一种方式来隐藏屏幕上的内容,当用户需要时,可以通过拖动手柄将其显示出来。这个组件可以沿着垂直或水平方向滑动,由两个主要部分组成:一个是可以拖动的手柄View,另一个是隐藏的内容View...
它可以轻松地实现上下左右四个方向的抽屉效果,不仅支持简单的左右滑动,还支持上下滑动,使得开发者在设计界面时有更多的选择。在使用Panel控件的过程中,需要注意的一个问题是在某些版本中可能出现抽屉“闪烁”的...
SlidingDrawer是Android SDK提供的一种滑动抽屉控件,它允许开发者在屏幕上添加一个可以从屏幕边缘滑出的抽屉,通常用于隐藏或显示额外的功能或信息。抽屉可以水平或垂直滑动,且支持设置抽屉的把手,用户可以通过...
在Android应用开发中,抽屉(SlidingDrawer)是一个常用组件,它允许开发者将内容以抽屉的形式隐藏在屏幕边缘,通过一个可拖动的手柄(handle)来展示或隐藏这些内容。抽屉通常用于创建导航菜单或者隐藏不常用但重要...
早期的Android版本中,存在一个叫做`SlidingDrawer`的组件,它提供了类似的功能。但随着Material Design设计语言的引入,`SlidingDrawer`已被废弃,取而代之的是`Navigation Drawer`。`Navigation Drawer`更符合现代...