使用上下滑动动画显示和隐藏视图


318

LinearLayout想显示或隐藏一个Animation,只要更改可见性,它就会向上或向下推动布局。

我已经看到了一些样本,但是没有一个能满足我的需求。

我已经为动画创建了两个xml文件,但是当更改的可见性时,我不知道如何启动它们LinearLayout

Answers:


639

借助Android 3.0(Honeycomb)中引入的新动画API,创建此类动画非常简单。

View向下滑动一段距离:

view.animate().translationY(distance);

您以后可以View像这样将后盖滑回其原始位置:

view.animate().translationY(0);

您还可以轻松地组合多个动画。以下动画将View向下滑动其高度,并同时使其淡入:

// Prepare the View for the animation
view.setVisibility(View.VISIBLE);
view.setAlpha(0.0f);

// Start the animation
view.animate()
    .translationY(view.getHeight())
    .alpha(1.0f)
    .setListener(null);

然后,您可以淡出View后背并将其滑回到其原始位置。我们还设置了一个,AnimatorListener以便我们可以在动画完成后将View背面的可见性设置GONE为:

view.animate()
    .translationY(0)
    .alpha(0.0f)
    .setListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            view.setVisibility(View.GONE);
        }
    });

1
为什么视图消失后不可见?
Ram

1
我想在可见和不可见时为视图设置动画。但是如果我第一次去查看它,它就不可见并且查看的地方是空白
Ram

3
@Ram View当其可见性设置为时,您要通过设置动画来实现View.GONE什么?如果将其可见性设置为除其他以外View.VISIBLEView则将不可见。我不明白你在问什么。如果您希望动画可见,请不要将的可见性设置ViewView.GONE
Xaver Kapeller '16

2
面对Ram所面临的相同问题,第一次运行良好,但是从下一次开始,当我使该视图处于消失状态并尝试再次使该视图可见时,它就不会出现。
Pankaj kumar'8

12
@XaverKapeller我认为这个问题有许多是听者onAnimationEnd被要求多发生的动画,这意味着,每一次onAnimationEnd也被称为时得到所示的视图,设置它的知名度已经一去不复返了,等等
oldergod

129

我在理解应用接受的答案时遇到了麻烦。我需要更多背景信息。现在我已经弄清楚了,这里是一个完整的示例:

在此处输入图片说明

MainActivity.java

public class MainActivity extends AppCompatActivity {

    Button myButton;
    View myView;
    boolean isUp;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myView = findViewById(R.id.my_view);
        myButton = findViewById(R.id.my_button);

        // initialize as invisible (could also do in xml)
        myView.setVisibility(View.INVISIBLE);
        myButton.setText("Slide up");
        isUp = false;
    }

    // slide the view from below itself to the current position
    public void slideUp(View view){
        view.setVisibility(View.VISIBLE);
        TranslateAnimation animate = new TranslateAnimation(
                0,                 // fromXDelta
                0,                 // toXDelta
                view.getHeight(),  // fromYDelta
                0);                // toYDelta
        animate.setDuration(500);
        animate.setFillAfter(true);
        view.startAnimation(animate);
    }

    // slide the view from its current position to below itself
    public void slideDown(View view){
        TranslateAnimation animate = new TranslateAnimation(
                0,                 // fromXDelta
                0,                 // toXDelta
                0,                 // fromYDelta
                view.getHeight()); // toYDelta
        animate.setDuration(500);
        animate.setFillAfter(true);
        view.startAnimation(animate);
    }

    public void onSlideViewButtonClick(View view) {
        if (isUp) {
            slideDown(myView);
            myButton.setText("Slide up");
        } else {
            slideUp(myView);
            myButton.setText("Slide down");
        }
        isUp = !isUp;
    }
}

activity_mail.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.slideview.MainActivity">

    <Button
        android:id="@+id/my_button"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp"
        android:onClick="onSlideViewButtonClick"
        android:layout_width="150dp"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:id="@+id/my_view"
        android:background="#a6e1aa"
        android:orientation="vertical"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="200dp">

    </LinearLayout>

</RelativeLayout>

笔记

  • 感谢本文为我指出了正确的方向。它比此页面上的其他答案更有帮助。
  • 如果要从屏幕上的视图开始,则不要将其初始化为INVISIBLE
  • 由于我们将其动画设置为完全不在屏幕上,因此无需将其设置为INVISIBLE。但是,如果您不完全在屏幕外制作动画,则可以添加Alpha动画并使用设置可见性AnimatorListenerAdapter
  • 属性动画文档

android:visibility =“ invisible”将视图动画初始化为皮革
Goodlife,

2
我不建议使用animate.setFillAfter(true); 如果您在滑动视图下有可单击的视图,则不会接收事件
HOCiNE BEKKOUCHE

2
请注意,如果没有.setVisibility(View.INVISIBLE);向上滑动功能,将无法按预期的效果工作。
Advait S '18年

Translate Animation移动视图。如果您想为视图本身缩放设置动画,请使用ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1)
Zohab Ali

33

现在,应该通过Transition API支持(androidx)包中的可用动画来完成可见性更改动画。只需使用Slide Transition调用TransitionManager.beginDelayedTransition方法,然后更改视图的可见性即可。

在此处输入图片说明

import androidx.transition.Slide;
import androidx.transition.Transition;
import androidx.transition.TransitionManager;

private void toggle(boolean show) {
    View redLayout = findViewById(R.id.redLayout);
    ViewGroup parent = findViewById(R.id.parent);

    Transition transition = new Slide(Gravity.BOTTOM);
    transition.setDuration(600);
    transition.addTarget(R.id.redLayout);

    TransitionManager.beginDelayedTransition(parent, transition);
    redLayout.setVisibility(show ? View.VISIBLE : View.GONE);
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="play" />

    <LinearLayout
        android:id="@+id/redLayout"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:background="#5f00"
        android:layout_alignParentBottom="true" />
</RelativeLayout>

请使用另一个默认和自定义转换示例来检查此答案


@akubi是的,应该是
阿坝

1
最佳和简单的答案之一!谢谢!
krisDrOid

请注意,这需要minSdkVersion 21
lasec0203

@ lasec0203不,类来自androidx软件包。它在pre 21 API上运行良好。
ashakirov

:thumbs_up:这摆脱了我得到的方法歧义错误
lasec0203

30

最简单的解决方案:将其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开发者


这是最简单的方法,但是由于手机制造商的不同,其行为也有所不同,并且他的代码也有所更改
b2mob

这会为Alpha(而非位置)设置动画。
Suragch

是的,但这就是我是否正确理解了最初的问题。如果要设置位置动画,则可以使用RecyclerView,该视图使用具有稳定ID的ViewHolders。
Stefan Medack '17

12

您可以通过创建的新子类并重写以启动,从而AnimationLinearLayout更改的可见性正确时启动正确的代码。考虑这样的事情:LinearLayoutsetVisibility()Animations

public class SimpleViewAnimator extends LinearLayout
{
    private Animation inAnimation;
    private Animation outAnimation;

    public SimpleViewAnimator(Context context)
    {
        super(context);
    }

    public void setInAnimation(Animation inAnimation)
    {
        this.inAnimation = inAnimation;
    }

    public void setOutAnimation(Animation outAnimation)
    {
        this.outAnimation = outAnimation;
    }

    @Override
    public void setVisibility(int visibility)
    {
        if (getVisibility() != visibility)
        {
            if (visibility == VISIBLE)
            {
                if (inAnimation != null) startAnimation(inAnimation);
            }
            else if ((visibility == INVISIBLE) || (visibility == GONE))
            {
                if (outAnimation != null) startAnimation(outAnimation);
            }
        }

        super.setVisibility(visibility);
    }
}

1
我实际上更喜欢子类方法。非常感谢你。
MichelReap 2013年

1
这是一个很棒的解决方案,我将在BaseView中实现。谢谢!
Bram Vandenbussche 2014年

1
由于在显示时有效,在隐藏时,视图消失了,无法看到动画。任何解决方法?
2014年

12
@BramVandenbussche这是一个糟糕的解决方案。它使View负责自己的动画,这是从来没有你想要的。想象一下,您想View在应用程序的另一部分中对动画进行不同的设置。那你怎么办呢?添加标记以不自动设置可见性动画?为View和覆盖子类setVisibility()以删除动画?或更糟糕的是setVisibility()用另一个动画来实现?从那里变得越来越丑陋。不要使用此“解决方案”。
Xaver Kapeller 2015年

3
最好将其称为AnimatedLinearLayout
Roel 2015年

12

科特林

根据Suragch答案,这是使用View扩展的一种优雅方法:

fun View.slideUp(duration: Int = 500) {
    visibility = View.VISIBLE
    val animate = TranslateAnimation(0f, 0f, this.height.toFloat(), 0f)
    animate.duration = duration.toLong()
    animate.fillAfter = true
    this.startAnimation(animate)
}

fun View.slideDown(duration: Int = 500) {
    visibility = View.VISIBLE
    val animate = TranslateAnimation(0f, 0f, 0f, this.height.toFloat())
    animate.duration = duration.toLong()
    animate.fillAfter = true
    this.startAnimation(animate)
}

然后,无论你想使用它,你只需要myView.slideUp()myView.slideDown()


唯一的错误是,它不需要“ fillAfter = true”,因为它可以阻止子视图单击可访问性
Ranjan

另外,您可能需要向SlideDown动画添加侦听器,并在onAnimationEnd上使视图消失。
Manohar Reddy

9
if (filter_section.getVisibility() == View.GONE) {
    filter_section.animate()
            .translationY(filter_section.getHeight()).alpha(1.0f)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    super.onAnimationStart(animation);
                    filter_section.setVisibility(View.VISIBLE);
                    filter_section.setAlpha(0.0f);
                }
            });
} else {
    filter_section.animate()
            .translationY(0).alpha(0.0f)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    filter_section.setVisibility(View.GONE);
                }
            });
}

10
这个答案的问题:1)糟糕的代码格式。2)您使用代码段发布实际上无法在浏览器中运行的代码。这不仅增加了两个无用的按钮,而且还破坏了语法高亮显示。3)这只是一些随机代码转储,没有任何解释或目的。4)您正在执行动画时更改可见性。除了这是明显的代码气味这一事实之外,这也将无法正常工作。更改可见性将启动新的布局过程,只有在完成动画之后,动画才真正具有要使用的值。这份清单还在继续……
Xaver Kapeller

我已经编辑了您的答案以解决格式问题,并将代码片段转换为实际的代码块。但您必须填写其余内容...
Xaver Kapeller

不好意思,我帮您编好了代码,因为它对我来说不好用,我的代码行得通。但是我同意在发帖方式上进行一些更改。
Ameen Maheen

@AmeenMaheen是setAlpha干什么的?
IgorGanapolsky

@ Igor Ganapolsky它用于透明度,即产生褪色效果
Ameen Maheen 2015年

4

您可以通过在Android应用中使用以下代码在任何视图或布局上上下滑动

boolean isClicked=false;
LinearLayout mLayoutTab = (LinearLayout)findViewById(R.id.linearlayout);

        if(isClicked){
                    isClicked = false;
                    mLayoutTab.animate()
                    .translationYBy(120)
                    .translationY(0)     
                    .setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));

        }else{
                isClicked = true;
                mLayoutTab.animate()
                .translationYBy(0)
                .translationY(120)
                .setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
                }

120是多少?什么是0?如果要对此硬编码,setDuration的单位是什么?
斯坦利·桑托索2015年

1
如果在硬屏上放置代码而不是在大屏幕或平板电脑上遇到问题,则此处的120和0是与Y轴相关的距离,因此,您需要为所有不同的设备放置来自string.xml值的值。持续时间是您要显示布局动画的时间。对不起,我英语不好...!
varotariya vajsi

@varotariyavajsi这实际上并不显示/隐藏视图的可见性。
IgorGanapolsky

您好igor ganapolsky我知道这些...它只是沿y方向平移视图,如果用户需要像底部滑块一样上下显示,它将很好用。
varotariya vajsi

4

使用此类:

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;
}
}

3

使用ObjectAnimator

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

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

2
次要改进:我们可以使用常量View.TRANSLATION_Y而不是“ translationY”,并且在向上滑动ObjectAnimation时,也可以执行.apply {doOnEnd {view.visibility = View.GONE} .......}。start()
塔西

0.toFloat()也可以是0f
styler1972

2

我有一个极端的情况,我的视线高度仍然zero如此...

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.View;

public final class AnimationUtils {

  public static void slideDown(final View view) {
        view.animate()
                .translationY(view.getHeight())
                .alpha(0.f)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        // superfluous restoration
                        view.setVisibility(View.GONE);
                        view.setAlpha(1.f);
                        view.setTranslationY(0.f);
                    }
                });
    }

    public static void slideUp(final View view) {
        view.setVisibility(View.VISIBLE);
        view.setAlpha(0.f);

        if (view.getHeight() > 0) {
            slideUpNow(view);
        } else {
            // wait till height is measured
            view.post(new Runnable() {
                @Override
                public void run() {
                    slideUpNow(view);
                }
            });
        }
    }

    private static void slideUpNow(final View view) {
        view.setTranslationY(view.getHeight());
        view.animate()
                .translationY(0)
                .alpha(1.f)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        view.setVisibility(View.VISIBLE);
                        view.setAlpha(1.f);
                    }
                });
    }

}

1

这是我的解决方案。只需获取对您的视图的引用,然后调用此方法:

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();
        }
    });
}

无需执行其他任何操作=)


1
为什么需要一个GlobalLayoutListener来做到这一点?您为什么以这种怪异的方式设置可见性?为什么要包括诸如启动延迟之类的与您的答案无关的东西?
Xaver Kapeller '16

1

苏拉奇在科特林的回答。这对我有用。

class MainActivity : AppCompatActivity() {

var isUp: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    var myView: View = findViewById(R.id.my_view)
    var myButton: Button = findViewById(R.id.my_button)

    //Initialize as invisible
    myView.visibility = View.INVISIBLE
    myButton.setText("Slide up")

    isUp = false

}


fun View.slideUp(duration: Int = 500){
    visibility = View.VISIBLE
    val animate = TranslateAnimation(0f, 0f, this.height.toFloat(), 0f)
    animate.duration = duration.toLong()
    animate.fillAfter = true
    this.startAnimation(animate)
}

fun View.slideDown(duration: Int = 500) {
    visibility = View.VISIBLE
    val animate = TranslateAnimation(0f, 0f, 0f, this.height.toFloat())
    animate.duration = duration.toLong()
    animate.fillAfter = true
    this.startAnimation(animate)
}

fun onSlideViewButtonClick(view: View){
    if(isUp){
        my_view.slideDown()
        my_button.setText("Slide Up")

    }
    else{
        my_view.slideUp()
        my_button.setText("Slide Down")
    }
    isUp = !isUp
}

}


0

您可以使用简单的三行代码来显示动画...

//getting the hiding view by animation

 mbinding.butn.setOnClickListener {

                val SlideOutLeft = AnimationUtils.loadAnimation(this, R.anim.slide_out_left)
                simplelayout.visibility = View.INVISIBLE
                simplelayout.startAnimation(SlideOutLeft)


                val SlideInRight = AnimationUtils.loadAnimation(applicationContext, R.anim.slide_in_right)
                animation1.visibility = View.VISIBLE
                animation1.startAnimation(SlideInRight)

            }
            //again unhide the view animation
            mbinding.buttn.setOnClickListener {


               val SlideInLeft=AnimationUtils.loadAnimation(this,R.anim.slide_in_left)
                //set the layout
               simplelayout.visibility=View.VISIBLE
               simplelayout.startAnimation(SlideInLeft)

               val SlideOutRight=AnimationUtils.loadAnimation(this,R.anim.slide_out_right)
               animation1.visibility=View.INVISIBLE
               animation1.startAnimation(SlideOutRight)

            }

0

使用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

0

一种简单的方法:

containerView.setLayoutTransition(LayoutTransition())
containerView.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.