我有一个线性布局,我想显示或隐藏与动画,推动布局向上或向下每当我改变其可见性。

我在那里看过一些样品,但没有一个适合我的需要。

我已经为动画创建了两个xml文件,但我不知道当我改变线性布局的可见性时如何启动它们。


当前回答

使用ObjectAnimator

private fun slideDown(view: View) {
    val height = view.height
    ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0f, height.toFloat()).apply {
        duration = 1000
        start()
    }
}

private fun slideUp(view: View) {
    val height = view.height
    ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, height.toFloat(), 0f)).apply {
        duration = 1000
        start()
    }
}

其他回答

最简单的解决方案:在持有视图的容器上设置android:animateLayoutChanges="true"。

如果你有一个如下所示的布局,这个容器中所有视图的可见性变化都将自动动画化。

<LinearLayout android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    >

    <Views_which_change_visibility>

</LinearLayout>

你可以在动画布局变化- Android开发者上找到更多细节

这是我的解决方案。只需要获取视图的引用并调用这个方法:

public static void animateViewFromBottomToTop(final View view){

    view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {

            view.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            final int TRANSLATION_Y = view.getHeight();
            view.setTranslationY(TRANSLATION_Y);
            view.setVisibility(View.GONE);
            view.animate()
                .translationYBy(-TRANSLATION_Y)
                .setDuration(500)
                .setStartDelay(200)
                .setListener(new AnimatorListenerAdapter() {

                    @Override
                    public void onAnimationStart(final Animator animation) {

                        view.setVisibility(View.VISIBLE);
                    }
                })
                .start();
        }
    });
}

不需要做任何其他事情=)

使用这个类:

public class ExpandCollapseExtention {

 public static void expand(View view) {
    view.setVisibility(View.VISIBLE);

    final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    view.measure(widthSpec, heightSpec);

    ValueAnimator mAnimator = slideAnimator(view, 0, view.getMeasuredHeight());
    mAnimator.start();
}


public static void collapse(final View view) {
    int finalHeight = view.getHeight();

    ValueAnimator mAnimator = slideAnimator(view, finalHeight, 0);

    mAnimator.addListener(new Animator.AnimatorListener() {

        @Override
        public void onAnimationEnd(Animator animator) {               
            view.setVisibility(View.GONE);
        }


        @Override
        public void onAnimationStart(Animator animation) {

        }


        @Override
        public void onAnimationCancel(Animator animation) {

        }


        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    mAnimator.start();
}


private static ValueAnimator slideAnimator(final View v, int start, int end) {

    ValueAnimator animator = ValueAnimator.ofInt(start, end);

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {

            int value = (Integer) valueAnimator.getAnimatedValue();
            ViewGroup.LayoutParams layoutParams = v.getLayoutParams();
            layoutParams.height = value;
            v.setLayoutParams(layoutParams);
        }
    });
    return animator;
}
}

一个完整的答案,在onClick中切换视图可见性,也翻转箭头,并在隐藏组件时平滑地向上移动其他视图。

private fun toggleRecyclerViewVisibility(
    recyclerView: RecyclerView,
    container: FrameLayout,
    arrow: ImageView
) {
    //toggle arrow direction, also block user clicks until animation finishes.
    arrow
        .animate()
        .rotation(
            if (arrow.rotation == 0F)
                180F
            else 0F
        )
        .withStartAction { container.isClickable = false }
        .withEndAction { container.isClickable = true }
        .start()

    //toggle recyclerview visibility with animation.
    with(recyclerView) {
        var cof = -1
        var vis = View.GONE
        var alph = 0F

        if (visibility == View.GONE) {
            cof = 0
            vis = View.VISIBLE
            alph = 1F
        }
        animate()
            .translationY(height.toFloat() * cof)
            .alpha(alph)
            .withStartAction {//in case showing the recyclerview show it at the beginning.
                if (vis == View.VISIBLE)
                    visibility = View.VISIBLE
            }
            .withEndAction {//in case hiding the recyclerview hide it at the end.
                if (vis == View.GONE)
                    visibility = View.GONE
            }
            .start()
    }
}

视图是这样的

        <LinearLayout
            android:id="@+id/subRootLinearView"
            android:animateLayoutChanges="true"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <!--other views-->
            <LinearLayout
                android:id="@+id/Container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <FrameLayout
                    android:id="@+id/header"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="@color/backgroundGray"
                    android:padding="16dp">

                    <TextView
                        android:id="@+id/text_view"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="@string/awaitingConfirmation"
                        android:textColor="@color/colorText"
                        android:textSize="16sp" />

                    <ImageView
                        android:id="@+id/arrow_image_view"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="end|center"
                        android:src="@drawable/ic_arrow" />
                </FrameLayout>

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical">

                    <androidx.recyclerview.widget.RecyclerView
                        android:id="@+id/recycler"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
                </LinearLayout>
            </LinearLayout>

            <!--other views-->
            </LinearLayout>

然后在代码中首先添加这一行,这解决了animateLayoutChanges在大嵌套视图中不工作的问题,这基本上使其他视图在隐藏recyclerview时平滑地向上移动

subRootLinearView.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)

你的父线性布局也应该包含这个属性

android:animateLayoutChanges="true"

然后用视图调用该方法

 toggleRecyclerViewVisibility(
                    recycler,
                    header,
                    arrowImageView
                )

通过Kotlin扩展,你可以这样使用:

enum class SlideDirection{
    UP,
    DOWN,
    LEFT,
    RIGHT
}

enum class SlideType{
    SHOW,
    HIDE
}

fun View.slideAnimation(direction: SlideDirection, type: SlideType, duration: Long = 250){
    val fromX: Float
    val toX: Float
    val fromY: Float
    val toY: Float
    val array = IntArray(2)
    getLocationInWindow(array)
    if((type == SlideType.HIDE && (direction == SlideDirection.RIGHT || direction == SlideDirection.DOWN)) ||
        (type == SlideType.SHOW && (direction == SlideDirection.LEFT || direction == SlideDirection.UP))   ){
        val displayMetrics = DisplayMetrics()
        val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
        windowManager.defaultDisplay.getMetrics(displayMetrics)
        val deviceWidth = displayMetrics.widthPixels
        val deviceHeight = displayMetrics.heightPixels
        array[0] = deviceWidth
        array[1] = deviceHeight
    }
    when (direction) {
        SlideDirection.UP -> {
            fromX = 0f
            toX = 0f
            fromY = if(type == SlideType.HIDE) 0f else (array[1] + height).toFloat()
            toY = if(type == SlideType.HIDE) -1f * (array[1] + height)  else 0f
        }
        SlideDirection.DOWN -> {
            fromX = 0f
            toX = 0f
            fromY = if(type == SlideType.HIDE) 0f else -1f * (array[1] + height)
            toY = if(type == SlideType.HIDE) 1f * (array[1] + height)  else 0f
        }
        SlideDirection.LEFT -> {
            fromX = if(type == SlideType.HIDE) 0f else 1f * (array[0] + width)
            toX = if(type == SlideType.HIDE) -1f * (array[0] + width) else 0f
            fromY = 0f
            toY = 0f
        }
        SlideDirection.RIGHT -> {
            fromX = if(type == SlideType.HIDE) 0f else -1f * (array[0] + width)
            toX = if(type == SlideType.HIDE) 1f * (array[0] + width) else 0f
            fromY = 0f
            toY = 0f
        }
    }
    val animate = TranslateAnimation(
        fromX,
        toX,
        fromY,
        toY
    )
    animate.duration = duration
    animate.setAnimationListener(object: Animation.AnimationListener{
        override fun onAnimationRepeat(animation: Animation?) {

        }

        override fun onAnimationEnd(animation: Animation?) {
            if(type == SlideType.HIDE){
                visibility = View.INVISIBLE
            }
        }

        override fun onAnimationStart(animation: Animation?) {
            visibility = View.VISIBLE
        }

    })
    startAnimation(animate)
}

扩展示例:

view.slideAnimation(SlideDirection.UP, SlideType.HIDE)//to make it disappear through top of the screen
view.slideAnimation(SlideDirection.DOWN, SlideType.SHOW)//to make it reappear from top of the screen

view.slideAnimation(SlideDirection.DOWN, SlideType.HIDE)//to make it disappear through bottom of the screen
view.slideAnimation(SlideDirection.UP, SlideType.SHOW)//to make it reappear from bottom of the screen