Android在设置View为Gone时添加简单的动画

347

我设计了一个简单的布局。我已经完成了没有动画的设计,但现在我想在TextView点击事件中添加动画,但我不知道该如何使用它。 我的XML设计看起来好不好? 任何建议都将不胜感激。

我的XML

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:longClickable="false"
    android:orientation="vertical"
    android:weightSum="16" >

<LinearLayout 
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:orientation="vertical"
    android:background="#00DDA0"
    android:layout_weight="3" >
</LinearLayout>
 <TextView
        android:id="@+id/Information1"
        android:layout_width="match_parent"
        android:layout_height="1dp" 
        android:text="Child Information" 
        android:background="#0390BE"
        android:layout_weight="0.75"
        android:textColor="#FFFFFF"
        android:layout_gravity="center|fill_horizontal"/>

 <LinearLayout
     android:id="@+id/layout1"
     android:layout_width="fill_parent"
     android:layout_height="0dp"
     android:layout_weight="8.5"
     android:background="#BBBBBB"
     android:orientation="vertical" >

     <TextView
         android:id="@+id/textView1"
         android:layout_width="match_parent"
         android:layout_height="match_parent"        
         android:text="TextView" />
 </LinearLayout>

  <TextView
        android:id="@+id/Information2"
        android:layout_width="match_parent"
        android:layout_height="0dp" 
        android:text="Parent Information" 
        android:background="#0390BE"
        android:layout_weight="0.75"
        android:textColor="#FFFFFF"
        android:layout_gravity="center|fill_horizontal"/>
  <LinearLayout 
          android:id="@+id/layout2"
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:orientation="vertical"
    android:background="#BBBBBB"
    android:layout_weight="8.5" >
     <TextView
         android:id="@+id/textView2"
         android:layout_width="match_parent"
         android:layout_height="match_parent"        
         android:text="TextView" />
      </LinearLayout>
   <TextView
        android:id="@+id/Information3"
        android:layout_width="match_parent"
        android:layout_height="0dp" 
        android:text="Siblings" 
        android:background="#0390BE"
        android:layout_weight="0.75"
        android:textColor="#FFFFFF"
        android:layout_gravity="center|fill_horizontal"/>
   <LinearLayout 
          android:id="@+id/layout3"
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:orientation="vertical"
    android:background="#BBBBBB"
    android:layout_weight="8.5" >
     <TextView
         android:id="@+id/textView3"
         android:layout_width="match_parent"
         android:layout_height="match_parent"        
         android:text="TextView" />
      </LinearLayout>
    <TextView
        android:id="@+id/Information4"
        android:layout_width="match_parent"
        android:layout_height="0dp" 
        android:text="Teacher Information" 
        android:background="#0390BE"
        android:layout_weight="0.75"
        android:textColor="#FFFFFF"
        android:layout_gravity="center|fill_horizontal"/>
    <LinearLayout 
          android:id="@+id/layout4"
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:orientation="vertical"
    android:background="#BBBBBB"
    android:layout_weight="8.5" >
     <TextView
         android:id="@+id/textView4"
         android:layout_width="match_parent"
         android:layout_height="match_parent"        
         android:text="TextView" />
      </LinearLayout>
     <TextView
        android:id="@+id/Information5"
        android:layout_width="match_parent"
        android:layout_height="0dp" 
        android:text="Grade Information" 
        android:background="#0390BE"
        android:layout_weight="0.75"
        android:textColor="#FFFFFF"
        android:layout_gravity="center|fill_horizontal"/>
     <LinearLayout 
          android:id="@+id/layout5"
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:orientation="vertical"
    android:background="#BBBBBB"
    android:layout_weight="8.5" >
     <TextView
         android:id="@+id/textView5"
         android:layout_width="match_parent"
         android:layout_height="match_parent"        
         android:text="TextView" />
      </LinearLayout>
      <TextView
        android:id="@+id/Information6"
        android:layout_width="match_parent"
        android:layout_height="0dp" 
        android:text="Health Information" 
        android:background="#0390BE"
        android:layout_weight="0.75"
        android:textColor="#FFFFFF"
        android:layout_gravity="center|fill_horizontal"/>
      <LinearLayout 
          android:id="@+id/layout6"
    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:orientation="vertical"
    android:background="#BBBBBB"
    android:layout_weight="8.5" >
    <TextView
         android:id="@+id/textView5"
         android:layout_width="match_parent"
         android:layout_height="match_parent"        
         android:text="TextView" 
         android:layout_weight="8.5" />
      </LinearLayout>

</LinearLayout>

我的Java

public class Certify_Info extends Activity {

    private static TextView tv2,tv3,tv5,tv6,tv4,tv1;
    private static LinearLayout l1,l2,l3,l4,l5,l6;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_certify__info);

        tv1=(TextView) findViewById(R.id.Information1);
        tv2=(TextView) findViewById(R.id.Information2);
        tv3=(TextView) findViewById(R.id.Information3);
        tv4=(TextView) findViewById(R.id.Information4);
        tv5=(TextView) findViewById(R.id.Information5);
        tv6=(TextView) findViewById(R.id.Information6); 

        l1=(LinearLayout) findViewById(R.id.layout1);
        l2=(LinearLayout) findViewById(R.id.layout2);
        l3=(LinearLayout) findViewById(R.id.layout3);
        l4=(LinearLayout) findViewById(R.id.layout4);
        l5=(LinearLayout) findViewById(R.id.layout5);
        l6=(LinearLayout) findViewById(R.id.layout6); 

        l2.setVisibility(View.GONE);
        l3.setVisibility(View.GONE); 
        l4.setVisibility(View.GONE); 
        l5.setVisibility(View.GONE);
        l6.setVisibility(View.GONE);

        tv1.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                l2.setVisibility(View.GONE);
                l3.setVisibility(View.GONE); 
                l4.setVisibility(View.GONE); 
                l5.setVisibility(View.GONE);
                l6.setVisibility(View.GONE);
                l1.setVisibility(View.VISIBLE);
            }
        });
        tv2.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                l1.setVisibility(View.GONE);
                l3.setVisibility(View.GONE); 
                l4.setVisibility(View.GONE); 
                l5.setVisibility(View.GONE);
                l6.setVisibility(View.GONE);
                l2.setVisibility(View.VISIBLE);
            }
        });
        tv3.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                l1.setVisibility(View.GONE);
                l2.setVisibility(View.GONE);
                l4.setVisibility(View.GONE); 
                l5.setVisibility(View.GONE);
                l6.setVisibility(View.GONE);
                l3.setVisibility(View.VISIBLE);

            }
        });
        tv4.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                l1.setVisibility(View.GONE);
                l2.setVisibility(View.GONE);
                l3.setVisibility(View.GONE); 
                l4.setVisibility(View.GONE); 
                l5.setVisibility(View.GONE);
                l6.setVisibility(View.GONE);
                l4.setVisibility(View.VISIBLE); 
            }
        });
        tv5.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                l1.setVisibility(View.GONE);
                l2.setVisibility(View.GONE);
                l3.setVisibility(View.GONE); 
                l4.setVisibility(View.GONE); 
                l6.setVisibility(View.GONE);
                l5.setVisibility(View.VISIBLE); 
            }
        });
        tv6.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                l1.setVisibility(View.GONE);
                l2.setVisibility(View.GONE);
                l3.setVisibility(View.GONE); 
                l4.setVisibility(View.GONE); 
                l5.setVisibility(View.GONE);
                l6.setVisibility(View.VISIBLE);
            }
        });

    }
}
11个回答

884

你可以通过两种方式添加动画效果,第一种是让android自动为你执行布局变化的动画。这样每当你在布局中更改某些内容,如更改视图可见性或视图位置时,android将自动创建淡入/淡出或过渡动画。要使用此功能,请设置

android:animateLayoutChanges="true"

在您的布局中的根节点。

第二个选项是手动添加动画。为此,我建议您使用Android 3.0(Honeycomb)中引入的新动画API。我可以给您一些示例:

这会使一个View渐隐:

view.animate().alpha(0.0f);

这将使其重新出现:

view.animate().alpha(1.0f);

这会使一个 View 沿着垂直方向下移其自身的高度:

view.animate().translationY(view.getHeight());

View从其他位置返回到其起始位置:

view.animate().translationY(0);

你还可以使用 setDuration() 来设置动画的持续时间。例如,以下代码会在 2 秒钟内使一个 View 渐变消失:

view.animate().alpha(0.0f).setDuration(2000);

您可以将尽可能多的动画组合在一起,例如这个示例会在 0.3 秒内淡出一个 View 并将其同时向下移动:

view.animate()
        .translationY(view.getHeight())
        .alpha(0.0f)
        .setDuration(300);

你还可以为动画分配监听器,并对各种事件做出反应。比如当动画开始、结束或重复等等。通过使用抽象类AnimatorListenerAdapter,你不必一次实现AnimatorListener的所有回调函数,只需要实现你需要的那些。这使得代码更易读。例如,以下代码将一个View淡出并将其高度向下移动0.3秒(300毫秒),当动画完成时,其可见性设置为View.GONE

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

7
@Natix,我不会将其称为“多余的超级调用”。如果没有问题,请不要进行肤浅的编辑。 - Xaver Kapeller
5
好的,但是反向操作不正确,为什么?view.setVisibility(View.VISIBLE); 透明度为1.0f时,出现了100个padding top... - user3402040
25
在设置布局不可见并消失之前,必须先清除动画效果。代码为 view.clearAnimation(); view.setVisibility(View.GONE); 否则布局将保持不可见而无法消失。 - Eftekhari
2
@Eftekhari 它们并不需要太多资源。如果你的安卓版本是最新的,但还是感到卡顿,那么你可能是因为你的手机内存较低或者显卡不好。我指的不是新库。动画器是本地的,它们是Android框架的一部分。它们已经发布了5年,现在97.9%的设备都支持它们。没有理由不使用动画器或至少使用ViewCompat.animate(),它是支持库的一部分,在更新的版本上使用动画器,在Android 3.0及以下版本上使用视图动画。 - Xaver Kapeller
1
但是当97.9%的设备都是Android 4.0及以上版本时,对于ViewCompat.animate()来说,剩下的用例并不多。 - Xaver Kapeller
显示剩余5条评论

191

最简单的动画可视性变化的方法是使用支持(androidx)包中提供的Transition API。只需调用TransitionManager.beginDelayedTransition方法,然后改变视图的可见性。还有几种默认过渡效果,例如FadeSlide

import androidx.transition.TransitionManager;
import androidx.transition.Transition;
import androidx.transition.Fade;

private void toggle() {
    Transition transition = new Fade();
    transition.setDuration(600);
    transition.addTarget(R.id.image);

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

其中parent是被动画视图的父ViewGroup。结果:

图片描述

这是使用Slide转换后的结果:

import androidx.transition.Slide;

Transition transition = new Slide(Gravity.BOTTOM);

输入图像描述

如果您需要不同的过渡效果,可以轻松编写自定义过渡。以下是我在另一个答案中编写的CircularRevealTransition示例。它使用CircularReveal动画显示和隐藏视图。

Transition transition = new CircularRevealTransition();

输入图像描述

android:animateLayoutChanges="true"选项实现相同的功能,它只是使用AutoTransition作为过渡效果。


1
@TouhidulIslam 不是的。这些类在androidx包中可用。一切都向后兼容。 - ashakirov
9
如何展开/收起视图组? - TheRealChx101
你能执行多个 beginDelayedTransition 吗? - Jeongbebs
1
非常有帮助且易于实现!谢谢! - Benjamin RD

56
尝试将此行添加到xml父布局中。
 android:animateLayoutChanges="true"

您的布局将如下所示

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    android:longClickable="false"
    android:orientation="vertical"
    android:weightSum="16">

    .......other code here

    </LinearLayout>

1
这对我很有效。我的设置使用约束布局作为根视图。当视图设置为View.VISIBLE时,动画会显示。 - EdgeDev
非常简单而优雅的解决方案 - Naveed Ahmad
这真是太棒了。我需要一个三态切换开关的过渡效果,这个方法非常完美。非常感谢你!!! - Akshay Shah

31
请查看此链接,它将允许像L2R、R2L、T2B、B2T动画这样的动画。
此代码显示从左到右的动画。
TranslateAnimation animate = new TranslateAnimation(0,view.getWidth(),0,0);
animate.setDuration(500);
animate.setFillAfter(true);
view.startAnimation(animate);
view.setVisibility(View.GONE);

如果您希望从右到左进行操作,则使用

TranslateAnimation animate = new TranslateAnimation(0,-view.getWidth(),0,0);

从上到下

TranslateAnimation animate = new TranslateAnimation(0,0,0,view.getHeight());

反之亦然。


11
这会使视角从原位移动。 - Relm

29
基于 @ashakirov 的回答,以下是我扩展的内容,以显示/隐藏带有淡入淡出动画的视图。
fun View.fadeVisibility(visibility: Int, duration: Long = 400) {
    val transition: Transition = Fade()
    transition.duration = duration
    transition.addTarget(this)
    TransitionManager.beginDelayedTransition(this.parent as ViewGroup, transition)
    this.visibility = visibility
}

使用示例

view.fadeVisibility(View.VISIBLE)
view.fadeVisibility(View.GONE, 2000)

这是最好的解决方案,它既简单又有效。谢谢。 - Wadia SALEM

17
我能这样显示/隐藏菜单:
MenuView.java(扩展FrameLayout)
private final int ANIMATION_DURATION = 500;

public void showMenu()
{
    setVisibility(View.VISIBLE);
    animate()
            .alpha(1f)
            .setDuration(ANIMATION_DURATION)
            .setListener(null);
}

private void hideMenu()
{
    animate()
            .alpha(0f)
            .setDuration(ANIMATION_DURATION)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    setVisibility(View.GONE);
                }
            });
}

来源


15

根据@Xaver Kapeller的答案,我找到了一种在屏幕上出现新视图时创建滚动动画(以及隐藏它们的动画)的方法。

它从这个状态开始:

  • 按钮
  • 最后一个按钮

  • 按钮
  • 按钮1
  • 按钮2
  • 按钮3
  • 按钮4
  • 最后一个按钮

反之亦然。

因此,当用户单击第一个按钮时,元素“Button 1”,“Button 2”,“Button 3”和“Button 4”将使用淡入淡出动画显示,并且元素“Last Button”将向下移动直到结束。布局的高度也会改变,从而允许正确使用滚动视图。

以下是显示带有动画的元素的代码:

private void showElements() {
    // Precondition
    if (areElementsVisible()) {
        Log.w(TAG, "The view is already visible. Nothing to do here");
        return;
    }

    // Animate the hidden linear layout as visible and set
    // the alpha as 0.0. Otherwise the animation won't be shown
    mHiddenLinearLayout.setVisibility(View.VISIBLE);
    mHiddenLinearLayout.setAlpha(0.0f);
    mHiddenLinearLayout
            .animate()
            .setDuration(ANIMATION_TRANSITION_TIME)
            .alpha(1.0f)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    super.onAnimationEnd(animation);
                    updateShowElementsButton();
                    mHiddenLinearLayout.animate().setListener(null);
                }
            })
    ;

    mLastButton
            .animate()
            .setDuration(ANIMATION_TRANSITION_TIME)
            .translationY(mHiddenLinearLayoutHeight);

    // Update the high of all the elements relativeLayout
    LayoutParams layoutParams = mAllElementsRelativeLayout.getLayoutParams();

    // TODO: Add vertical margins
    layoutParams.height = mLastButton.getHeight() + mHiddenLinearLayoutHeight;
}

以下是隐藏动画元素的代码:

private void hideElements() {
    // Precondition
    if (!areElementsVisible()) {
        Log.w(TAG, "The view is already non-visible. Nothing to do here");
        return;
    }

    // Animate the hidden linear layout as visible and set
    mHiddenLinearLayout
            .animate()
            .setDuration(ANIMATION_TRANSITION_TIME)
            .alpha(0.0f)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    Log.v(TAG, "Animation ended. Set the view as gone");
                    super.onAnimationEnd(animation);
                    mHiddenLinearLayout.setVisibility(View.GONE);
                    // Hack: Remove the listener. So it won't be executed when
                    // any other animation on this view is executed
                    mHiddenLinearLayout.animate().setListener(null);
                    updateShowElementsButton();
                }
            })
    ;

    mLastButton
            .animate()
            .setDuration(ANIMATION_TRANSITION_TIME)
            .translationY(0);

    // Update the high of all the elements relativeLayout
    LayoutParams layoutParams = mAllElementsRelativeLayout.getLayoutParams();

    // TODO: Add vertical margins
    layoutParams.height = mLastButton.getHeight();
}

请注意,有一个简单的方法可以隐藏动画。在动画监听器mHiddenLinearLayout中,我必须使用以下方法来移除监听器本身:

mHiddenLinearLayout.animate().setListener(null);

这是因为一旦将动画监听器附加到视图上,下次在该视图上执行任何动画时,监听器也将被执行。这可能是动画监听器中的一个 bug。
该项目的源代码位于 GitHub 上: https://github.com/jiahaoliuliu/ViewsAnimated 祝编码愉快!
更新:对于附加到视图上的任何监听器,在动画结束后都应该将其移除。可以使用以下方法实现:
view.animate().setListener(null);

1
这个答案是最好的。 - Naveen Kumar M
2
view.animate().setListener(null); 这条语句救了我的一天。这明显是一个 bug。 - Michal Vician
@MichalVician 很高兴这样做! - jiahao

8

我的解决方案扩展

fun View.slideVisibility(visibility: Boolean, durationTime: Long = 300) {
    val transition = Slide(Gravity.BOTTOM)
    transition.apply {
        duration = durationTime
        addTarget(this@slideVisibility)
    }
    TransitionManager.beginDelayedTransition(this.parent as ViewGroup, transition)
    this.isVisible = visibility
    }

使用:

textView.slideVisibility(true)

1
找到以下代码以使圆形揭示视图可见,如果您发送true,则它将变为不可见/消失。如果您发送false,则它将变为可见。anyView是您要使其可见/隐藏的视图,它可以是任何视图(布局、按钮等)。
 private fun toggle(flag: Boolean, anyView: View) {
    if (flag) {
        val cx = anyView.width / 2
        val cy = anyView.height / 2
        val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
        val anim = ViewAnimationUtils.createCircularReveal(anyView, cx, cy, initialRadius, 0f)
        anim.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                super.onAnimationEnd(animation)
                anyView.visibility = View.INVISIBLE
            }
        })
        anim.start()
    } else {
        val cx = anyView.width / 2
        val cy = anyView.height / 2
        val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
        val anim = ViewAnimationUtils.createCircularReveal(anyView, cx, cy, 0f, finalRadius)
        anyView.visibility = View.VISIBLE
        anim.start()
    }
}

如何降低动画速度? - IntelliJ Amiya
现在可以通过库级别进行控制。如果您觉得有帮助,请点赞。 - Harish Reddy

0
一个不同的版本
fun View.fadeIn(
        offset: Long = 0L,
        duration: Long = 200L,
        onStart: (() -> Unit)? = null,
        onFinish: (() -> Unit)? = null
) {
    if (!isAttachedToWindow) {
        onStart?.invoke()
        visible()
        onFinish?.invoke()
        return
    }
    val anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_in)
    anim.startOffset = offset
    anim.duration = duration
    anim.setAnimationListener(object : Animation.AnimationListener {
        override fun onAnimationRepeat(animation: Animation?) {}
        override fun onAnimationEnd(animation: Animation?) = onFinish?.invoke() ?: Unit
        override fun onAnimationStart(animation: Animation?) {
            visible()
            onStart?.invoke()
        }
    })
    startAnimation(anim)
}

fun View.fadeOut(
        offset: Long = 0L,
        duration: Long = 200L,
        onStart: (() -> Unit)? = null,
        onFinish: (() -> Unit)? = null
) {
    if (!isAttachedToWindow) {
        onStart?.invoke()
        invisible()
        onFinish?.invoke()
        return
    }
    val anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_out)
    anim.startOffset = offset
    anim.duration = duration
    anim.setAnimationListener(object : Animation.AnimationListener {
        override fun onAnimationRepeat(animation: Animation?) {}
        override fun onAnimationEnd(animation: Animation?) {
            invisible()
            onFinish?.invoke()
        }

        override fun onAnimationStart(animation: Animation?) {
            onStart?.invoke()
        }
    })
    startAnimation(anim)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接