android Panel控件 抽屉控件

最近项目中需要实现抽屉式菜单 从网上搜集了些许资料做了一个小demo可以借鉴下

demo免积分下载地址:http://download.csdn.net/detail/zabio/7136907

控件部分xml:

panel:position:控制上下左右的属性


<com.hy.mypanel.Panel
        android:id="@+id/topPanel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerInParent="true"
        panel:openedHandle="panelHandle展开时的background 用selector比较好"
        panel:closedHandle="panelHandle收起时的background"
        panel:animationDuration="1000"
        panel:content="@+id/panelContent"
        panel:handle="@+id/panelHandle"
        panel:linearFlying="true"
        panel:position="left" >

        <RelativeLayout
            android:id="@+id/panelHandle"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:background="#f00"
            android:padding="10dp" >

            <TextView
                android:id="@+id/menuText"
                android:layout_width="25dp"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:gravity="center"
                android:text="我是导航菜单"
                android:textColor="#fff"
                android:textSize="24sp" />
        </RelativeLayout>

        <LinearLayout
            android:id="@+id/panelContent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#999932"
            android:gravity="center"
            android:orientation="vertical" >

            <Button
                android:id="@+id/menuA"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="会员到店" />

            <Button
                android:id="@+id/menuB"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="会员查询" />

            <Button
                android:id="@+id/menuC"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="购买商品" />

            <Button
                android:id="@+id/menuD"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="会员离开" />
        </LinearLayout>
    </com.hy.mypanel.Panel>







widget代码:

package com.hy.mypanel;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

public class Panel extends LinearLayout  
{  
  
    private static final String TAG = "Panel";  
  
    private static final float MAXIMUM_MAJOR_VELOCITY = 200.0f;  
    //控制时间 越大越快
    private static final float MAXIMUM_ACCELERATION = 2000.0f;  
    private static final int MSG_ANIMATE = 100;  
    private static final int MSG_PREPARE_ANIMATE = 2000;  
    private static final int ANIMATION_FRAME_DURATION = 1000 / 60;  
  
    private final Handler mHandler = new SlidingHandler();  
    private float mAnimatedAcceleration;  
    private long mAnimationLastTime;  
    private long mCurrentAnimationTime;  
    private boolean mAnimating;  
  
    private final int mMaximumMajorVelocity;  
    private final int mMaximumAcceleration;  
  
    private float lastRawX, lastRawY, curRawX, curRawY;  
    private float lastEventTime, curEventTime;  
  
    /** 
     * Callback invoked when the panel is opened/closed. 
     */  
    public static interface OnPanelListener  
    {  
        /** 
         * Invoked when the panel becomes fully closed. 
         */  
        public void onPanelClosed(Panel panel);  
  
        /** 
         * Invoked when the panel becomes fully opened. 
         */  
        public void onPanelOpened(Panel panel);  
    }  
  
    private boolean mIsShrinking;  
    private int mPosition;  
    private int mDuration;  
    private boolean mLinearFlying;  
    private int mHandleId;  
    private int mContentId;  
    private View mHandle;  
    private View mContent;  
    private Drawable mOpenedHandle;  
    private Drawable mClosedHandle;  
    private float mTrackX;  
    private float mTrackY;  
    private float mVelocity;  
  
    private OnPanelListener panelListener;  
  
    public static final int TOP = 0;  
    public static final int BOTTOM = 1;  
    public static final int LEFT = 2;  
    public static final int RIGHT = 3;  
  
    private enum State  
    {  
        ABOUT_TO_ANIMATE, ANIMATING, READY, TRACKING, FLYING, CLICK  
    };  
  
    private State mState;  
    private Interpolator mInterpolator;  
    private GestureDetector mGestureDetector;  
    private int mContentHeight;  
    private int mContentWidth;  
    private int mOrientation;  
    private float mWeight;  
    private PanelOnGestureListener mGestureListener;  
    private boolean mBringToFront;  
  
    public Panel(Context context, AttributeSet attrs)  
    {  
        super(context, attrs);  
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Panel);  
        mDuration = a.getInteger(R.styleable.Panel_animationDuration, 750);     // duration defaults to 750 ms  
        mPosition = a.getInteger(R.styleable.Panel_position, BOTTOM);           // position defaults to BOTTOM  
        mLinearFlying = a.getBoolean(R.styleable.Panel_linearFlying, false);    // linearFlying defaults to false  
        mWeight = a.getFraction(R.styleable.Panel_weight, 0, 1, 0.0f);          // weight defaults to 0.0  
        if (mWeight < 0 || mWeight > 1)  
        {  
            mWeight = 0.0f;  
            Log.w(TAG, a.getPositionDescription()  
                    + ": weight must be > 0 and <= 1");  
        }  
        mOpenedHandle = a.getDrawable(R.styleable.Panel_openedHandle);  
        mClosedHandle = a.getDrawable(R.styleable.Panel_closedHandle);  
  
        RuntimeException e = null;  
        mHandleId = a.getResourceId(R.styleable.Panel_handle, 0);  
        if (mHandleId == 0)  
        {  
            e = new IllegalArgumentException(  
                    a.getPositionDescription()  
                            + ": The handle attribute is required and must refer to a valid child.");  
        }  
        mContentId = a.getResourceId(R.styleable.Panel_content, 0);  
        if (mContentId == 0)  
        {  
            e = new IllegalArgumentException(  
                    a.getPositionDescription()  
                            + ": The content attribute is required and must refer to a valid child.");  
        }  
        a.recycle();  
  
        final float density = getResources().getDisplayMetrics().density;  
        mMaximumMajorVelocity = (int) (MAXIMUM_MAJOR_VELOCITY * density + 0.5f);  
        mMaximumAcceleration = (int) (MAXIMUM_ACCELERATION * density + 0.5f);  
  
        if (e != null)  
        {  
            throw e;  
        }  
        mOrientation = (mPosition == TOP || mPosition == BOTTOM) ? VERTICAL  
                : HORIZONTAL;  
        setOrientation(mOrientation);  
        mState = State.READY;  
        mGestureListener = new PanelOnGestureListener();  
        mGestureDetector = new GestureDetector(mGestureListener);  
        mGestureDetector.setIsLongpressEnabled(false);  
  
        // i DON'T really know why i need this...  
        setBaselineAligned(false);  
    }  
  
    /** 
     * Sets the listener that receives a notification when the panel becomes 
     * open/close. 
     *  
     * @param onPanelListener 
     *            The listener to be notified when the panel is opened/closed. 
     */  
    public void setOnPanelListener(OnPanelListener onPanelListener)  
    {  
        panelListener = onPanelListener;  
    }  
  
    /** 
     * Gets Panel's mHandle 
     *  
     * @return Panel's mHandle 
     */  
    public View getHandle()  
    {  
        return mHandle;  
    }  
  
    /** 
     * Gets Panel's mContent 
     *  
     * @return Panel's mContent 
     */  
    public View getContent()  
    {  
        return mContent;  
    }  
  
    /** 
     * Sets the acceleration curve for panel's animation. 
     *  
     * @param i 
     *            The interpolator which defines the acceleration curve 
     */  
    public void setInterpolator(Interpolator i)  
    {  
        mInterpolator = i;  
    }  
  
    /** 
     * Set the opened state of Panel. 
     *  
     * @param open 
     *            True if Panel is to be opened, false if Panel is to be closed. 
     * @param animate 
     *            True if use animation, false otherwise. 
     *  
     * @return True if operation was performed, false otherwise. 
     *  
     */  
    public boolean setOpen(boolean open, boolean animate)  
    {  
        if (mState == State.READY && isOpen() ^ open)  
        {  
            mIsShrinking = !open;  
            if (animate)  
            {  
                mState = State.ABOUT_TO_ANIMATE;  
                if (!mIsShrinking)  
                {  
                    // this could make flicker so we test mState in  
                    // dispatchDraw()  
                    // to see if is equal to ABOUT_TO_ANIMATE  
                    mContent.setVisibility(VISIBLE);  
                }  
                long now = SystemClock.uptimeMillis();  
                mAnimationLastTime = now;  
                mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;  
                mAnimating = true;  
                post(startAnimation);
            }  
            else  
            {  
                mContent.setVisibility(open ? VISIBLE : GONE);  
                postProcess();  
            }  
            return true;  
        }  
        return false;  
    }  
    
	Runnable startAnimation = new Runnable() {
		public void run() {
			// this is why we post this Runnable couple of lines above:
			// now its save to use mContent.getHeight() && mContent.getWidth()
			TranslateAnimation animation;
			int fromXDelta = 0, toXDelta = 0, fromYDelta = 0, toYDelta = 0;
			if (mState == State.FLYING) {
				mIsShrinking = (mPosition == TOP || mPosition == LEFT) ^ (mVelocity > 0);
			}
			int calculatedDuration;
			if (mOrientation == VERTICAL) {
				int height = mContentHeight;
				if (!mIsShrinking) {
					fromYDelta = mPosition == TOP? -height : height;
				} else {
					toYDelta = mPosition == TOP? -height : height;
				}
				if (mState == State.TRACKING) {
					if (Math.abs(mTrackY - fromYDelta) < Math.abs(mTrackY - toYDelta)) {
						mIsShrinking = !mIsShrinking;
						toYDelta = fromYDelta;
					}
					fromYDelta = (int) mTrackY;
				} else
				if (mState == State.FLYING) {
					fromYDelta = (int) mTrackY;
				}
				// for FLYING events we calculate animation duration based on flying velocity
				// also for very high velocity make sure duration >= 20 ms
				if (mState == State.FLYING && mLinearFlying) {
					calculatedDuration = (int) (1000 * Math.abs((toYDelta - fromYDelta) / mVelocity));
					calculatedDuration = Math.max(calculatedDuration, 20);
				} else {
					calculatedDuration = mDuration * Math.abs(toYDelta - fromYDelta) / mContentHeight;
				}
			} else {
				int width = mContentWidth;
				if (!mIsShrinking) {
					fromXDelta = mPosition == LEFT? -width : width;
				} else {
					toXDelta = mPosition == LEFT? -width : width;
				}
				if (mState == State.TRACKING) {
					if (Math.abs(mTrackX - fromXDelta) < Math.abs(mTrackX - toXDelta)) {
						mIsShrinking = !mIsShrinking;
						toXDelta = fromXDelta;
					}
					fromXDelta = (int) mTrackX;
				} else
				if (mState == State.FLYING) {
					fromXDelta = (int) mTrackX;
				}
				// for FLYING events we calculate animation duration based on flying velocity
				// also for very high velocity make sure duration >= 20 ms
				if (mState == State.FLYING && mLinearFlying) {
					calculatedDuration = (int) (1000 * Math.abs((toXDelta - fromXDelta) / mVelocity));
					calculatedDuration = Math.max(calculatedDuration, 20);
				} else {
					calculatedDuration = mDuration * Math.abs(toXDelta - fromXDelta) / mContentWidth;
				}
			}
			
			mTrackX = mTrackY = 0;
			if (calculatedDuration == 0) {
				mState = State.READY;
				if (mIsShrinking) {
					mContent.setVisibility(GONE);
				}
				postProcess();
				return;
			}
			
			animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);
			animation.setDuration(calculatedDuration);
			animation.setAnimationListener(animationListener);
			if (mState == State.FLYING && mLinearFlying) {
				animation.setInterpolator(new LinearInterpolator());
			} else
			if (mInterpolator != null) {
				animation.setInterpolator(mInterpolator);
			}
			startAnimation(animation);
		}
	};
    
    
    private AnimationListener animationListener = new AnimationListener() {
		public void onAnimationEnd(Animation animation) {
			mState = State.READY;
			if (mIsShrinking) {
				mContent.setVisibility(GONE);
			}
			postProcess();
			mAnimating = false;
		}
		public void onAnimationRepeat(Animation animation) {
		}
		public void onAnimationStart(Animation animation) {
			mState = State.ANIMATING;
		}
	};
  
    /** 
     * Returns the opened status for Panel. 
     *  
     * @return True if Panel is opened, false otherwise. 
     */  
    public boolean isOpen()  
    {  
        return mContent.getVisibility() == VISIBLE;  
    }  
  
    @Override  
    protected void onFinishInflate()  
    {  
        super.onFinishInflate();  
        mHandle = findViewById(mHandleId);  
        if (mHandle == null)  
        {  
            String name = getResources().getResourceEntryName(mHandleId);  
            throw new RuntimeException(  
                    "Your Panel must have a child View whose id attribute is 'R.id."  
                            + name + "'");  
        }  
        mHandle.setClickable(true);  
        mHandle.setOnTouchListener(touchListener);  
        // mHandle.setOnClickListener(clickListener);  
  
        mContent = findViewById(mContentId);  
        if (mContent == null)  
        {  
            String name = getResources().getResourceEntryName(mHandleId);  
            throw new RuntimeException(  
                    "Your Panel must have a child View whose id attribute is 'R.id."  
                            + name + "'");  
        }  
  
        // reposition children  
        removeView(mHandle);  
        removeView(mContent);  
        if (mPosition == TOP || mPosition == LEFT)  
        {  
            addView(mContent);  
            addView(mHandle);  
        }  
        else  
        {  
            addView(mHandle);  
            addView(mContent);  
        }  
  
        if (mClosedHandle != null)  
        {  
            mHandle.setBackgroundDrawable(mClosedHandle);  
        }  
        mContent.setClickable(true);  
        mContent.setVisibility(GONE);  
        if (mWeight > 0)  
        {  
            ViewGroup.LayoutParams params = mContent.getLayoutParams();  
            if (mOrientation == VERTICAL)  
            {  
                params.height = ViewGroup.LayoutParams.FILL_PARENT;  
            }  
            else  
            {  
                params.width = ViewGroup.LayoutParams.FILL_PARENT;  
            }  
            mContent.setLayoutParams(params);  
        }  
        
        
        
    }  
  
    @Override  
    protected void onAttachedToWindow()  
    {  
        super.onAttachedToWindow();  
        ViewParent parent = getParent();  
        if (parent != null && parent instanceof FrameLayout)  
        {  
            mBringToFront = true;  
        }  
    }  
  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
    {  
        if (mWeight > 0 && mContent.getVisibility() == VISIBLE)  
        {  
            View parent = (View) getParent();  
            if (parent != null)  
            {  
                if (mOrientation == VERTICAL)  
                {  
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(  
                            (int) (parent.getHeight() * mWeight),  
                            MeasureSpec.EXACTLY);  
                }  
                else  
                {  
                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(  
                            (int) (parent.getWidth() * mWeight),  
                            MeasureSpec.EXACTLY);  
                }  
            }  
        }  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
    }  
  
    @Override  
    protected void onLayout(boolean changed, int l, int t, int r, int b)  
    {  
        super.onLayout(changed, l, t, r, b);  
        mContentWidth = mContent.getWidth();  
        mContentHeight = mContent.getHeight();  
    }  
  
    @Override  
    protected void dispatchDraw(Canvas canvas)  
    {  
        // String name = getResources().getResourceEntryName(getId());  
        // Log.d(TAG, name + " ispatchDraw " + mState);  
        // this is why 'mState' was added:  
        // avoid flicker before animation start  
        if (mState == State.ABOUT_TO_ANIMATE && !mIsShrinking)  
        {  
            int delta = mOrientation == VERTICAL ? mContentHeight  
                    : mContentWidth;  
            if (mPosition == LEFT || mPosition == TOP)  
            {  
                delta = -delta;  
            }  
            if (mOrientation == VERTICAL)  
            {  
                canvas.translate(0, delta);  
            }  
            else  
            {  
                canvas.translate(delta, 0);  
            }  
        }  
        if (mState == State.TRACKING || mState == State.FLYING  
                || mState == State.CLICK)  
        {  
            canvas.translate(mTrackX, mTrackY);  
        }  
        super.dispatchDraw(canvas);  
    }  
  
    private float ensureRange(float v, int min, int max)  
    {  
        v = Math.max(v, min);  
        v = Math.min(v, max);  
        return v;  
    }  
  
    OnTouchListener touchListener = new OnTouchListener()  
    {  
          
        public boolean onTouch(View v, MotionEvent event)  
        {  
  
        	Log.d("a", "OnTouch() mAnimating = " + mAnimating );
        	
            if (mAnimating)  
            {  
                // we are animating  
                return true;// 动画中不响应onTouch事件  
            }  
  
            int action = event.getAction();  
            if (action == MotionEvent.ACTION_DOWN)  
            {  
                if (mBringToFront)  
                {  
                    bringToFront();  
                }  
            }  
  
            if (!mGestureDetector.onTouchEvent(event))  
            {  
                if (action == MotionEvent.ACTION_UP)  
                {  
                    // tup up after scrolling  
  
                    long now = SystemClock.uptimeMillis();  
                    mAnimationLastTime = now;  
                    mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;  
                    mAnimating = true;  
                    mHandler.removeMessages(MSG_ANIMATE);  
                    mHandler.removeMessages(MSG_PREPARE_ANIMATE);  
                    mHandler.sendMessageAtTime(  
                            mHandler.obtainMessage(MSG_PREPARE_ANIMATE),  
                            mCurrentAnimationTime);  
                }  
            }  
            return false;  
        }  
    };  
  
    public boolean initChange()  
    {  
        if (mState != State.READY)  
        {  
            // we are animating or just about to animate  
            return false;  
        }  
        mState = State.ABOUT_TO_ANIMATE;  
        mIsShrinking = mContent.getVisibility() == VISIBLE;  
        if (!mIsShrinking)  
        {  
            // this could make flicker so we test mState in dispatchDraw()  
            // to see if is equal to ABOUT_TO_ANIMATE  
            mContent.setVisibility(VISIBLE);  
        }  
        return true;  
    }  
  
    private void postProcess()  
    {  
        if (mIsShrinking && mClosedHandle != null)  
        {  
            mHandle.setBackgroundDrawable(mClosedHandle);  
        }  
        else if (!mIsShrinking && mOpenedHandle != null)  
        {  
            mHandle.setBackgroundDrawable(mOpenedHandle);  
        }  
        // invoke listener if any  
        if (panelListener != null)  
        {  
            if (mIsShrinking)  
            {  
                panelListener.onPanelClosed(Panel.this);  
            }  
            else  
            {  
                panelListener.onPanelOpened(Panel.this);  
            }  
        }  
    }  
  
    class PanelOnGestureListener implements OnGestureListener  
    {  
        float scrollY;  
        float scrollX;  
  
        @Override  
        public boolean onDown(MotionEvent e)  
        
        {  
        	Log.d("a", "PanelOnGestureListener onDown ");
            scrollX = scrollY = 0;  
            lastRawX = curRawX = lastRawY = curRawY = -1;  
            lastEventTime = curEventTime = -1;  
            initChange();  
            return true;  
        }  
  
        @Override  
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,  
                float velocityY)  
        {  
        	Log.d("a", "PanelOnGestureListener onFling ");
            mState = State.FLYING;  
  
            float velocityX2, velocityY2;  
            if (lastRawX == -1 && lastRawY == -1)   //见onScroll方法  
            {  
                velocityX2 = (curRawX - e1.getRawX())  
                        / (curEventTime - e1.getEventTime()) * 1000; //  px/s  
                velocityY2 = (curRawY - e1.getRawY())  
                        / (curEventTime - e1.getEventTime()) * 1000;  
            }  
            else  
            {  
                velocityX2 = (curRawX - lastRawX)  
                        / (curEventTime - lastEventTime) * 1000;  
                velocityY2 = (curRawY - lastRawY)  
                        / (curEventTime - lastEventTime) * 1000;  
            }  
  
            mVelocity = mOrientation == VERTICAL ? velocityY2 : velocityX2;  
  
            if (mState == State.FLYING) {
				mIsShrinking = (mPosition == TOP || mPosition == LEFT) ^ (mVelocity > 0);
			}
            
            if (Math.abs(mVelocity) > 50)  
            {  
                if (mVelocity > 0)  
                {  
                    mAnimatedAcceleration = mMaximumAcceleration;  
                }  
                else  
                {  
                    mAnimatedAcceleration = -mMaximumAcceleration;  
                }  
  
                long now = SystemClock.uptimeMillis();  
                mAnimationLastTime = now;  
                mCurrentAnimationTime = now + ANIMATION_FRAME_DURATION;  
                mAnimating = true;  
                mHandler.removeMessages(MSG_ANIMATE);  
                mHandler.removeMessages(MSG_PREPARE_ANIMATE);  
                mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),  
                        mCurrentAnimationTime);  
                return true;  
            }  
            return false;  
        }  
  
        @Override  
        public void onLongPress(MotionEvent e)  
        {  
            // not used  
        }  
  
        @Override  
        public boolean onScroll(MotionEvent e1, MotionEvent e2,  
                float distanceX, float distanceY)  
        {  
        	Log.d("a", "PanelOnGestureListener onScroll");
            mState = State.TRACKING;  
            float tmpY = 0, tmpX = 0;  
            if (mOrientation == VERTICAL)  
            {  
                scrollY -= distanceY;  
                if (mPosition == TOP)  
                {  
                    tmpY = ensureRange(scrollY, -mContentHeight, 0);  
                }  
                else  
                {  
                    tmpY = ensureRange(scrollY, 0, mContentHeight);  
                }  
            }  
            else  
            {  
                scrollX -= distanceX;  
                if (mPosition == LEFT)  
                {  
                    tmpX = ensureRange(scrollX, -mContentWidth, 0);  
                }  
                else  
                {  
                    tmpX = ensureRange(scrollX, 0, mContentWidth);  
                }  
            }  
  
            if (tmpX != mTrackX || tmpY != mTrackY)  
            {  
                mTrackX = tmpX;  
                mTrackY = tmpY;  
                // invalidate(); //放在此导致极快速滑动至touch区域外界面不刷新(mTrackX、mTrackY均为0)  
            }  
            invalidate();  
  
            lastRawX = curRawX;  
            lastRawY = curRawY;  
            lastEventTime = curEventTime;  
            curRawX = e2.getRawX();  
            curRawY = e2.getRawY();  
            curEventTime = e2.getEventTime();  
            return true;  
        }  
  
        @Override  
        public void onShowPress(MotionEvent e)  
        {  
            // not used  
        }  
  
        @Override  
        public boolean onSingleTapUp(MotionEvent e)  
        {  
            // not used  
            return false;  
        }  
    }  
  
    private void prepareAnimation()  
    {  
  
        switch (mPosition)  
        {  
            case LEFT:  
                if (mIsShrinking)  
                {  
                    mVelocity = -mMaximumMajorVelocity;  
                    mAnimatedAcceleration = -mMaximumAcceleration;  
  
                }  
                else  
                {  
                    mVelocity = mMaximumMajorVelocity;  
                    mAnimatedAcceleration = mMaximumAcceleration;  
                    if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)  
                    {  
                        mTrackX = -mContentWidth;  
                    }  
                }  
                break;  
            case RIGHT:  
                if (mIsShrinking)  
                {  
                    mVelocity = mMaximumMajorVelocity;  
                    mAnimatedAcceleration = mMaximumAcceleration;  
                }  
                else  
                {  
                    mVelocity = -mMaximumMajorVelocity;  
                    mAnimatedAcceleration = -mMaximumAcceleration;  
  
                    if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)  
                    {  
                        mTrackX = mContentWidth;  
                    }  
                }  
                break;  
            case TOP:  
                if (mIsShrinking)  
                {  
                    mVelocity = -mMaximumMajorVelocity;  
                    mAnimatedAcceleration = -mMaximumAcceleration;  
                }  
                else  
                {  
                    mVelocity = mMaximumMajorVelocity;  
                    mAnimatedAcceleration = mMaximumAcceleration;  
  
                    if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)  
                    {  
                        mTrackY = -mContentHeight;  
                    }  
                }  
                break;  
            case BOTTOM:  
                if (mIsShrinking)  
                {  
                    mVelocity = mMaximumMajorVelocity;  
                    mAnimatedAcceleration = mMaximumAcceleration;  
                }  
                else  
                {  
                    mVelocity = -mMaximumMajorVelocity;  
                    mAnimatedAcceleration = -mMaximumAcceleration;  
  
                    if (mTrackX == 0 && mState == State.ABOUT_TO_ANIMATE)  
                    {  
                        mTrackY = mContentHeight;  
                    }  
                }  
                break;  
        }  
  
        if (mState == State.TRACKING)  
        {  
            if (mIsShrinking)  
            {  
                if ((mOrientation == VERTICAL && Math.abs(mTrackY) < mContentHeight / 2)  
                        || (mOrientation == HORIZONTAL && Math.abs(mTrackX) < mContentWidth / 2))  
                {  
                    mVelocity = -mVelocity;  
                    mAnimatedAcceleration = -mAnimatedAcceleration;  
                    mIsShrinking = !mIsShrinking;  
                }  
            }  
            else  
            {  
                if ((mOrientation == VERTICAL && Math.abs(mTrackY) > mContentHeight / 2)  
                        || (mOrientation == HORIZONTAL && Math.abs(mTrackX) > mContentWidth / 2))  
                {  
                    mVelocity = -mVelocity;  
                    mAnimatedAcceleration = -mAnimatedAcceleration;  
                    mIsShrinking = !mIsShrinking;  
                }  
            }  
        }  
        if (mState != State.FLYING && mState != State.TRACKING)  
        {  
            mState = State.CLICK;  
        }  
    }  
  
    private void doAnimation()  
    {  
  
        if (mAnimating)  
        {  
            long now = SystemClock.uptimeMillis();  
            float t = (now - mAnimationLastTime) / 1000.0f;     // ms -> s  
            final float v = mVelocity;                          // px/s  
            final float a = mAnimatedAcceleration;              // px/s/s  
            mVelocity = v + (a * t);                            // px/s  
            mAnimationLastTime = now;  
  
            switch (mPosition)  
            {  
                case LEFT:  
                    mTrackX = mTrackX + (v * t) + (0.5f * a * t * t); // px  
                    if (mTrackX > 0)  
                    {  
                        mTrackX = 0;  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    else if (mTrackX < -mContentWidth)  
                    {  
                        mTrackX = -mContentWidth;  
                        mContent.setVisibility(GONE);  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    break;  
                case RIGHT:  
                    mTrackX = mTrackX + (v * t) + (0.5f * a * t * t);  
                    if (mTrackX < 0)  
                    {  
                        mTrackX = 0;  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    else if (mTrackX > mContentWidth)  
                    {  
                        mTrackX = mContentWidth;  
                        mContent.setVisibility(GONE);  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    break;  
                case TOP:  
                    mTrackY = mTrackY + (v * t) + (0.5f * a * t * t);  
                    if (mTrackY > 0)  
                    {  
                        mTrackY = 0;  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    else if (mTrackY < -mContentHeight)  
                    {  
                        mTrackY = -mContentHeight;  
                        mContent.setVisibility(GONE);  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    break;  
                case BOTTOM:  
                    mTrackY = mTrackY + (v * t) + (0.5f * a * t * t);  
                    if (mTrackY < 0)  
                    {  
                        mTrackY = 0;  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    else if (mTrackY > mContentHeight)  
                    {  
                        mTrackY = mContentHeight;  
                        mContent.setVisibility(GONE);  
                        mState = State.READY;  
                        mAnimating = false;  
                    }  
                    break;  
            }  
            invalidate();  
  
            if (!mAnimating)  
            {  
                postProcess();  
                return;  
            }  
            mCurrentAnimationTime += ANIMATION_FRAME_DURATION;  
            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE),  
                    mCurrentAnimationTime);  
  
        }  
    }  
  
    private class SlidingHandler extends Handler  
    {  
        public void handleMessage(Message m)  
        {  
            switch (m.what)  
            {  
                case MSG_ANIMATE:  
                    doAnimation();  
                    break;  
                case MSG_PREPARE_ANIMATE:  
                    prepareAnimation();  
                    doAnimation();  
                    break;  
            }  
        }  
    }  
}












  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SwipePanel(侧划控件)Background对市面上实现的侧划返回不是很满意(仿微信,QQ 通过修改窗口透明坑太多),最终决定还是亲手写一个高实用性的吧,效果如下所示,换个图标,更多划动功能可以由你自己解锁,总共一个 600 多行代码的类,推荐通过阅读源码,你肯定会收获很多哈。Preview DownloadGradle:implementation 'com.blankj:swipe-panel:1.0'How to use动态final SwipePanel swipePanel = new SwipePanel(this); swipePanel.setLeftEdgeSize(SizeUtils.dp2px(100));// 设置左侧触发阈值 100dp swipePanel.setLeftDrawable(R.drawable.base_back);// 设置左侧 icon swipePanel.wrapView(findViewById(R.id.rootLayout));// 设置嵌套在 rootLayout 外层 swipePanel.setOnFullSwipeListener(new SwipePanel.OnFullSwipeListener() {// 设置完全划开松手后的监听     @Override     public void onFullSwipe(int direction) {         finish();         swipePanel.close(direction);// 关闭     } });静态<com.blankj.swipepanel.SwipePanel         xmlns:android="http://schemas.android.com/apk/res/android"         xmlns:tools="http://schemas.android.com/tools"         xmlns:app="http://schemas.android.com/apk/res-auto"         android:id="@ id/swipePanel"         android:background="@color/mediumGray"         android:layout_width="match_parent"         android:layout_height="match_parent"         tools:context=".LayoutSwipePanelActivity"         app:isLeftCenter="false"         app:leftEdgeSize="100dp"         app:leftSwipeColor="@color/colorPrimary"         app:leftDrawable="@drawable/base_back">     ... </com.blankj.swipepanel.SwipePanel>API方法名属性名说明setLeft(Top, Right, Bottom)SwipeColorapp:left(top, right, bottom)SwipeColor设置左(上、右、下)测颜色setLeft(Top, Right, Bottom)EdgeSizeapp:left(top, right, bottom)EdgeSize设置左(上、右、下)测触发阈值setLeft(Top, Right, Bottom)Drawableapp:left(top, right, bottom)Drawable设置左(上、右、下)测 iconsetLeft(Top, Right, Bottom)Centerapp:isLeft(Top, Right, Bottom)Center设置左(上、右、下)测是否居中setLeft(Top, Right, Bottom)Enabledapp:isLeft(Top, Right, Bottom)Enabled设置左(上、右、下)测是否可用wrapView---设置嵌套在该 view 的外层setOnFullSwipeListener---设置完全划开松手后的监听isOpen---判断是否被划开close---关闭
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值