How to create slide layout animation in Android?

后端 未结 1 729
醉梦人生
醉梦人生 2020-12-30 15:56

\"enter

Hi, I want to create a slide animation in My Activity like shown in Images. wh

相关标签:
1条回答
  • 2020-12-30 16:18

    Use this Panel class this will fulfil your requirement and you can also handle the event.

    Reference : http://code.google.com/p/android-misc-widgets/

    Panel.java

    public class Panel extends LinearLayout {
    
        /**
         * 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,
        };
        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();
    
            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);
                    }
                    post(startAnimation);
                } else {
                    mContent.setVisibility(open? VISIBLE : GONE);
                    postProcess();
                }
                return true;
            }
            return false;
        }
    
        /**
         * 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.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) {
                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() {
            int initX;
            int initY;
            boolean setInitialPosition;
            public boolean onTouch(View v, MotionEvent event) {
                if (mState == State.ANIMATING) {
                    // we are animating
                    return false;
                }
    //          //Log.d(TAG, "state: " + mState + " x: " + event.getX() + " y: " + event.getY());
                int action = event.getAction();
                if (action == MotionEvent.ACTION_DOWN) {
                    if (mBringToFront) {
                        bringToFront();
                    }
                    initX = 0;
                    initY = 0;
                    if (mContent.getVisibility() == GONE) {
                        // since we may not know content dimensions we use factors here
                        if (mOrientation == VERTICAL) {
                            initY = mPosition == TOP? -1 : 1;
                        } else {
                            initX = mPosition == LEFT? -1 : 1;
                        }
                    }
                    setInitialPosition = true;
                } else {
                    if (setInitialPosition) {
                        // now we know content dimensions, so we multiply factors...
                        initX *= mContentWidth;
                        initY *= mContentHeight;
                        // ... and set initial panel's position
                        mGestureListener.setScroll(initX, initY);
                        setInitialPosition = false;
                        // for offsetLocation we have to invert values
                        initX = -initX;
                        initY = -initY;
                    }
                    // offset every ACTION_MOVE & ACTION_UP event 
                    event.offsetLocation(initX, initY);
                }
                if (!mGestureDetector.onTouchEvent(event)) {
                    if (action == MotionEvent.ACTION_UP) {
                        // tup up after scrolling
                        post(startAnimation);
                    }
                }
                return false;
            }
        };
    
        OnClickListener clickListener = new OnClickListener() {
            public void onClick(View v) {
                if (mBringToFront) {
                    bringToFront();
                }
                if (initChange()) {
                    post(startAnimation);
                }
            }
        };
    
        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;
        }
    
        Runnable startAnimation = new Runnable() {
            public void run() {
    
                callPanListener();
    
                // 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);
                if (mState == State.FLYING && mLinearFlying) {
                    animation.setInterpolator(new LinearInterpolator());
                } else
                if (mInterpolator != null) {
                    animation.setInterpolator(mInterpolator);
                }
                startAnimation(animation);
            }
        };
    
        @Override
        protected void onAnimationEnd() {
            super.onAnimationEnd();
            mState = State.READY;
            if (mIsShrinking) {
                mContent.setVisibility(GONE);
            }
            postProcess();
        }
    
        @Override
        protected void onAnimationStart() {
            super.onAnimationStart();
            mState = State.ANIMATING;
        }
    
        private void postProcess() {
            if (mIsShrinking && mClosedHandle != null) {
                mHandle.setBackgroundDrawable(mClosedHandle);
            } else
            if (!mIsShrinking && mOpenedHandle != null) {
                mHandle.setBackgroundDrawable(mOpenedHandle);
            }
            // invoke listener if any
            callPanListener();
        }
        public void callPanListener()
        {
            if (panelListener != null) {
                if (mIsShrinking) {
                    panelListener.onPanelClosed(Panel.this);
                } else {
                    panelListener.onPanelOpened(Panel.this);
                }
            }
        }
    
        class PanelOnGestureListener implements OnGestureListener {
            float scrollY;
            float scrollX;
            public void setScroll(int initScrollX, int initScrollY) {
                scrollX = initScrollX;
                scrollY = initScrollY;
            }
            public boolean onDown(MotionEvent e) {
                scrollX = scrollY = 0;
                callPanListener();
                initChange();
                return true;
            }
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                mState = State.FLYING;
    //          velocityX=400;
    //          velocityY=400;
                mVelocity = mOrientation == VERTICAL? velocityY : velocityX;
    //          mVelocity=400;
                post(startAnimation);
                return true;
            }
            public void onLongPress(MotionEvent e) {
                // not used
            }
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                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();
                }
                return true;
            }
            public void onShowPress(MotionEvent e) {
                // not used
            }
            public boolean onSingleTapUp(MotionEvent e) {
                // not used
                return false;
            }
        }
    }
    

    main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:panel="http://schemas.android.com/apk/res/packagename"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"">
    
    <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:gravity="center_horizontal" >
    
            <packagename.Panel
                android:id="@+id/panel_menu"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                panel:animationDuration="1000"
                panel:closedHandle="#0000FF"
                panel:content="@+id/panelContent"
                panel:handle="@+id/panelHandle"
                panel:linearFlying="false"
                panel:openedHandle="#0000FF"
                android:paddingTop="4dip"
                panel:position="bottom" >
    
                <Button
                    android:id="@+id/panelHandle"
                    android:layout_width="33dp"
                    android:layout_height="33dp"
                    android:layout_gravity="center_horizontal"
                    android:text="^"
                    android:textColor="@android:color/white" />
    
                <RelativeLayout
                    android:id="@+id/panelContent"
                    android:layout_width="fill_parent"
                    android:layout_height="50dp"
                    android:background="#0000FF" >
                </RelativeLayout>
            </packagename.Panel>
    </RelativeLayout>
    

    attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="Panel">
            <!-- Defines panel animation duration in ms. -->
            <attr name="animationDuration" format="integer" />
            <!-- Defines panel position on the screen. -->
            <attr name="position">
                <!-- Panel placed at top of the screen. -->
                <enum name="top" value="0" />
                <!-- Panel placed at bottom of the screen. -->
                <enum name="bottom" value="1" />
                <!-- Panel placed at left of the screen. -->
                <enum name="left" value="2" />
                <!-- Panel placed at right of the screen. -->
                <enum name="right" value="3" />
            </attr>
            <!-- Identifier for the child that represents the panel's handle. -->
            <attr name="handle" format="reference" />
            <!-- Identifier for the child that represents the panel's content. -->
            <attr name="content" format="reference" />
            <!-- Defines if flying gesture forces linear interpolator in animation. -->
            <attr name="linearFlying" format="boolean" />
            <!-- Defines size relative to parent (must be in form: nn%p). -->
            <attr name="weight" format="fraction" />
            <!-- Defines opened handle (drawable/color). -->
            <attr name="openedHandle" format="reference|color" />
            <!-- Defines closed handle (drawable/color). -->
            <attr name="closedHandle" format="reference|color" />
        </declare-styleable>
    
    </resources>
    

    To play with this in your Activity you have

    SampleActivity.java

    public class SampleActivity extends Activity implements OnPanelListener {
    
    public Panel samplePanel;
      @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.panel_main)_layout;
    
            samplePanel = (Panel) findViewById(R.id.panel_id);
            samplePanel.setOnPanelListener(this);
            samplePanel.setInterpolator(new ExpoInterpolator(Type.OUT));
    
         }
    
         public void onPanelClosed(Panel panel) {}//Interface Listener
         public void onPanelOpened(Panel panel) {}//Interface Listener
    }
    
    0 讨论(0)
提交回复
热议问题