在slidedrawer中简化动画

时间:2014-06-08 10:03:33

标签: android android-animation interpolation slidingdrawer

我想使用插值器减速SlidingDrawer开启速度(DecelerateInterpolator) 这可能吗。我想实现轻松动画。

final Animation slowDown = AnimationUtils.loadAnimation(((Swipe)getActivity()), R.anim.ease);

这是XML

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
    android:fromYDelta="0"
    android:toYDelta="100%p"
    android:duration="2000"/>
</set>

使用这个我没有得到我想要的东西。

1 个答案:

答案 0 :(得分:0)

实际上,如果您只想打开/关闭该滑动布局,那么我建议使用自定义ViewGroup而不是弃用SlidingDrawer。以下是该实现的一些简单示例:

public class MySimpleSlidingDrawer extends RelativeLayout implements View.OnClickListener {

    private static final int SLIDING_TIME = 500;

    private View mHandle;
    private int mHandleId;
    private View mContent;
    private int mContentId;

    private ObjectAnimator mOpenAnimator = ObjectAnimator.ofFloat(this, "slide", 0f, 1f);
    private ObjectAnimator mCloseAnimator = ObjectAnimator.ofFloat(this, "slide", 1f, 0f);

    private int mAnimationTime = SLIDING_TIME;
    private boolean mOpened = false;

    private int mSlideHeight = 0;

    public MySimpleSlidingDrawer(final Context context) {
        super(context);
        init(context, null, 0);
    }

    public MySimpleSlidingDrawer(final Context context, final AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public MySimpleSlidingDrawer(final Context context, final AttributeSet attrs, final int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs, defStyle);
    }

    private void init(final Context context, final AttributeSet attrs, final int defStyle) {
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MySlidingDrawer, defStyle, 0);

        mHandleId = a.getResourceId(R.styleable.MySlidingDrawer_handle, 0);
        mContentId = a.getResourceId(R.styleable.MySlidingDrawer_content, 0);

        mOpenAnimator.setInterpolator(new AccelerateInterpolator());
        mOpenAnimator.setDuration(SLIDING_TIME);
        mCloseAnimator.setInterpolator(new DecelerateInterpolator());
        mCloseAnimator.setDuration(SLIDING_TIME);

        setClipChildren(false);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        if (getChildCount() != 2) {
            throw new InflateException("Only to child are supported for this layout");
        }

        if (mHandleId != 0) {
            mHandle = findViewById(mHandleId);
        } else {
            mHandle = getChildAt(0);
        }

        if (mContentId != 0) {
            mContent = findViewById(mContentId);
        } else {
            mContent = getChildAt(1);
        }

        final LayoutParams handleParams = (LayoutParams) mHandle.getLayoutParams();

        handleParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
        handleParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
        handleParams.addRule(ALIGN_PARENT_BOTTOM, 1/* true */);
        handleParams.addRule(CENTER_HORIZONTAL, 1/* true */);
        mHandle.setLayoutParams(handleParams);

        mHandle.setOnClickListener(this);
    }

    @Override
    public void onClick(final View v) {
        if (mSlideHeight == 0) {
            mSlideHeight = getHeight() - mHandle.getHeight();
        }

        // Handle have been clicked. Execute animation depending on open / close state
        if (!mOpened) {
            mOpened = true;

            mCloseAnimator.cancel();
            mOpenAnimator.start();
        } else {
            mOpened = false;

            mOpenAnimator.cancel();
            mCloseAnimator.start();
        }
    }

    /**
     * Sets slide percent value
     *
     * @param slidePercent % of slide (0 - closed, 1 - opened)
     */
    @SuppressWarnings("UnusedDeclaration")
    public void setSlide(final float slidePercent) {
        final LayoutParams handleParams = (LayoutParams) mHandle.getLayoutParams();

        handleParams.bottomMargin = (int) (slidePercent * mSlideHeight);
        mHandle.setLayoutParams(handleParams);

        final LayoutParams contentParams = (LayoutParams) mContent.getLayoutParams();

        contentParams.bottomMargin = (int) -((1- slidePercent) * mSlideHeight);
        mContent.setLayoutParams(contentParams);
    }

    /**
     * Sets open interpolator
     *
     * @param interpolator {@link android.view.animation.Interpolator} for open animation
     */
    @SuppressWarnings("UnusedDeclaration")
    public void setOpenInterpolator(final Interpolator interpolator) {
        if (mOpenAnimator.isRunning()) {
            mOpenAnimator.cancel();
        }

        mOpenAnimator.setInterpolator(interpolator);
    }

    /**
     * Sets close interpolator
     *
     * @param interpolator {@link android.view.animation.Interpolator} for close animation
     */
    @SuppressWarnings("UnusedDeclaration")
    public void setCloseInterpolator(final Interpolator interpolator) {
        if (mCloseAnimator.isRunning()) {
            mCloseAnimator.cancel();
        }

        mCloseAnimator.setInterpolator(interpolator);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // Trick to avoid content to be resized - measure it as it visible
        final int contentHeightMeasure = MeasureSpec.makeMeasureSpec(getMeasuredHeight() - mHandle.getMeasuredHeight(), MeasureSpec.EXACTLY);
        final int contentWidthMeasure = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);

        mContent.measure(contentHeightMeasure, contentWidthMeasure);

        if (mSlideHeight == 0) {
            mSlideHeight = getMeasuredHeight() - mHandle.getMeasuredHeight();

            final LayoutParams contentParams = (LayoutParams) mContent.getLayoutParams();

            contentParams.height = mSlideHeight;
            contentParams.addRule(ALIGN_PARENT_BOTTOM, 1 /* true */);
            contentParams.bottomMargin = - mSlideHeight;

            mContent.setLayoutParams(contentParams);
        }
    }
}

注意:它没有经过优化或测试,但基本功能有效。

另一种方法是根据您的需要调整现有的SlidingDrawer代码。对我来说,它看起来不那么容易或灵活,因为它具有特定的现有实现。首先,它在SlidingDrawer文档中明确提到:

  

不再支持此课程。建议您以此为基础   自己实现Android开源的源代码   项目,如果您必须在您的应用程序中使用它。

SlidingDrawer没有公开动画更改API。主要问题是根本没有动画,有些计时事件只是发送到更新视图位置,这里:

private void doAnimation() {
    if (mAnimating) {
        incrementAnimation();
        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);
        }
    }
}

因此,为了修改插值,您需要更改动画逻辑或提供自己的动画逻辑。第二种方式更安全,因为不会影响现有逻辑。下面是一些草稿变体如何做到(检测与原始SlidingDrawer相比,在您的Android SDK安装中使用原始API 19版本打开下面的类差异的原始public class MySlidingDrawer extends ViewGroup { /** Click animation duration */ // TODO: ideally you should properly calculate that value private static final long CLICK_ANIMATION_DURATION = 1000; /** New field for custom interpolator */ private TimeInterpolator mAnimationInterpolator = new BounceInterpolator(); /** just to distinguish click and moving by user animations */ private boolean mAnimatedClick = false; /** Specific click animator */ private ObjectAnimator mClickToggleAnimation; /** Specific listener just to handle animation end properly */ private Animator.AnimatorListener mClickAnimationListener = new Animator.AnimatorListener() { @Override public void onAnimationStart(final Animator animation) { // nothing to do here } @Override public void onAnimationEnd(final Animator animation) { mAnimating = false; // Determine if it close or open, by comparing to some final value if (mAnimationPosition == mTopOffset) { openDrawer(); } else { closeDrawer(); } } @Override public void onAnimationCancel(final Animator animation) { // TODO: should be handled properly } @Override public void onAnimationRepeat(final Animator animation) { } }; ... /** * 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 MySlidingDrawer(Context context, AttributeSet attrs) { this(context, attrs, 0); } ... @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; } if (xVelocity > mMaximumMinorVelocity) { xVelocity = mMaximumMinorVelocity; } } else { negative = xVelocity < 0; if (yVelocity < 0) { yVelocity = -yVelocity; } if (yVelocity > mMaximumMinorVelocity) { yVelocity = mMaximumMinorVelocity; } } float velocity = (float) Math.hypot(xVelocity, yVelocity); if (negative) { velocity = -velocity; } final int top = mHandle.getTop(); final int left = mHandle.getLeft(); if (Math.abs(velocity) < mMaximumTapVelocity) { if (vertical ? (mExpanded && top < mTapThreshold + mTopOffset) || (!mExpanded && top > mBottomOffset + getBottom() - getTop() - mHandleHeight - mTapThreshold) : (mExpanded && left < mTapThreshold + mTopOffset) || (!mExpanded && left > mBottomOffset + getRight() - getLeft() - mHandleWidth - mTapThreshold)) { if (mAllowSingleTap) { playSoundEffect(SoundEffectConstants.CLICK); animateToggle(); /* if (mExpanded) { animateClose(vertical ? top : left); } else { animateOpen(vertical ? top : left); } */ } else { performFling(vertical ? top : left, velocity, false); } } else { performFling(vertical ? top : left, velocity, false); } } else { performFling(vertical ? top : left, velocity, false); } } break; } } return mTracking || mAnimating || super.onTouchEvent(event); } ... /** * Toggles the drawer open and close with an animation. * * @see #open() * @see #close() * @see #animateClose() * @see #animateOpen() * @see #toggle() */ public void animateToggle() { mAnimatedClick = true; if (!mExpanded) { animateClickOpen(); } else { animateClickClose(); } } /** * For doing our animation for close */ private void animateClickClose() { mAnimating = true; mClickToggleAnimation = ObjectAnimator.ofInt(this, "togglePosition", (int) mAnimationPosition, mBottomOffset + (mVertical ? getHeight() : getWidth()) - 1); mClickToggleAnimation.setInterpolator(mAnimationInterpolator); mClickToggleAnimation.setDuration(CLICK_ANIMATION_DURATION); mClickToggleAnimation.addListener(mClickAnimationListener); mClickToggleAnimation.start(); } /** * For doing our animation for open */ private void animateClickOpen() { mAnimating = true; mClickToggleAnimation = ObjectAnimator.ofInt(this, "togglePosition", (int)mAnimationPosition, mTopOffset); mClickToggleAnimation.setInterpolator(mAnimationInterpolator); mClickToggleAnimation.setDuration(CLICK_ANIMATION_DURATION); mClickToggleAnimation.addListener(mClickAnimationListener); mClickToggleAnimation.start(); } /** * Sets current animation position * * @param position to be set */ @SuppressWarnings("UnusedDeclaration") public void setTogglePosition(final int position) { mAnimationPosition = position; moveHandle((int) mAnimationPosition); } ... 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(); } } } ... /** * New API to modify timing of interpolator * * @param interpolator {@link android.animation.TimeInterpolator} to be used for onClick open / close * * TODO: it's also possible to add XML attribute for the same */ public void setAnimationInterpolator(final TimeInterpolator interpolator) { mAnimationInterpolator = interpolator; } } ),只有更改的代码如下所示:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:custom="http://schemas.android.com/apk/res/com.alexstarc.testapp"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- Some fragment with main content of activity screen -->
    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.alexstarc.testapp.VoteListFragment"
        android:tag="mainFragment"/>

    <com.alexstarc.testapp.MySlidingDrawer
        android:id="@+id/drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        custom:handle="@+id/handle"
        custom:orientation="vertical"
        custom:content="@+id/content">

        <ImageView
            android:id="@id/handle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@android:drawable/ic_delete"/>

        <ImageView
            android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/cover"
            android:scaleType="fitXY"/>
    </com.alexstarc.testapp.MySlidingDrawer>
    <!--
    <com.alexstarc.testapp.MySimpleSlidingDrawer
        android:id="@+id/drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        custom:handle="@+id/handle"
        custom:content="@+id/content">

        <ImageView
            android:id="@id/handle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@android:drawable/ic_delete"/>

        <ImageView
            android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/cover"
            android:scaleType="fitXY"/>
    </com.alexstarc.testapp.MySimpleSlidingDrawer>
    -->
</RelativeLayout>

还有我用于测试的xml(只应启用一个滑动抽屉部分):

{{1}}