【问题标题】:Show and hide a View with a slide up/down animation使用向上/向下滑动动画显示和隐藏视图
【发布时间】:2013-11-14 23:11:32
【问题描述】:

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

我已经看过一些样品,但没有一个适合我的需要。

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

【问题讨论】:

    标签: android animation android-animation


    【解决方案1】:

    使用 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);
            }
        });
    

    【讨论】:

    • 为什么视图消失后不可见?
    • 我想在可见和消失时为视图设置动画。但如果我第一次去查看。它无法看到并且查看位置是空白的
    • @Ram 当 View 的可见性设置为 View.GONE 时,你想通过动画实现什么?如果您将其可见性设置为除View.VISIBLE 之外的任何内容,则View 将不可见。我不明白你在问什么。如果您希望您的动画可见,请不要将 View 的可见性设置为 View.GONE
    • 面临与 Ram 相同的问题,第一次它工作正常,但从下一次当我使该视图处于消失状态并尝试使该视图再次可见时,它不会出现。跨度>
    • @XaverKapeller 我认为很多人遇到的问题是每次多次出现的动画都会调用监听器onAnimationEnd,这意味着在显示视图时也会调用onAnimationEnd,这将其可见性设置为 Gone 等。
    【解决方案2】:

    我在理解应用接受的答案时遇到了麻烦。我需要更多的上下文。现在我已经弄清楚了,这是一个完整的例子:

    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>
    

    注意事项

    • 感谢this article 为我指明了正确的方向。它比此页面上的其他答案更有帮助。
    • 如果要从屏幕上的视图开始,请不要将其初始化为INVISIBLE
    • 由于我们将动画完全移出屏幕,因此无需将其设置回INVISIBLE。但是,如果您的动画没有完全脱离屏幕,那么您可以添加一个 alpha 动画并使用 AnimatorListenerAdapter 设置可见性。
    • Property Animation docs

    【讨论】:

    • android:visibility="invisible" 将视图动画初始化为隐藏
    • 我不推荐使用 animate.setFillAfter(true);如果您在滑动视图下有可点击的视图,它将不会接收事件
    • 请注意,如果没有.setVisibility(View.INVISIBLE);,上滑功能将无法按视觉预期工作。
    • Translate Animation 移动视图。如果您想像缩放本身一样为视图设置动画,请使用ScaleAnimation anim = new ScaleAnimation(1, 1, 0, 1)
    • 请注意,TranslateAnimation 不会移动可点击对象,如果它们出现在您的视图中。它只会移动像素。在大多数情况下,这可能是意料之外的。参考这个来移动可点击的东西:stackoverflow.com/a/34631361/5353128
    【解决方案3】:

    现在可见性更改动画应通过支持 (androidx) 包中的Transition API 完成。只需使用Slide 转换调用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>
    

    检查this answer 与另一个默认和自定义转换示例。

    【讨论】:

    • @akubi 是的,应该是
    • 最好和最简单的答案之一!谢谢!
    • 请注意,这需要minSdkVersion 21
    • @lasec0203 不,课程来自androidx 包。它在 pre 21 api 上运行良好。
    • :thumbs_up: 这消除了我遇到的方法歧义错误
    【解决方案4】:

    最简单的解决方案:在保存视图的容器上设置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>
    

    您可以在Animating Layout Changes - Android Developer找到更多详细信息

    【讨论】:

    • 这是最简单的,但它的行为因手机制造商和他的代码更改而异
    • 这会为 alpha 设置动画,而不是位置。
    • 是的,但如果我理解正确的话,这就是最初的问题。如果您想为位置设置动画,可以使用 RecyclerView,它使用具有稳定 ID 的 ViewHolders。
    • 如果它在嵌套视图中不起作用,请参阅此答案stackoverflow.com/a/59649918/6039240
    【解决方案5】:

    科特林

    基于Suragchanswer,这里是一个使用视图扩展的优雅方式:

    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”,因为它会阻止子视图点击可访问性
    • 另外,您可能需要为 slideDown 动画添加一个侦听器,并使视图在动画结束时消失。
    【解决方案6】:

    LinearLayout 的可见性发生变化时,您可以通过创建LinearLayout 的新子类并覆盖setVisibility() 以启动Animations 来启动正确的Animation。考虑这样的事情:

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

    【讨论】:

    • 我实际上更喜欢子类方法。非常感谢。
    • 这是一个很棒的解决方案,我将在我的 BaseView 中实施。谢谢!
    • 因为这在显示时有效,在隐藏时,视图会在动画可见之前消失。有什么解决方法吗?
    • @BramVandenbussche 这是一个糟糕的解决方案。它使View 负责它自己的动画,这是您想要的NEVER。想象一下,您想在应用程序的另一部分以不同方式为 View 设置动画。那你怎么办呢?添加标志以不自动为可见性设置动画?继承View 并覆盖setVisibility() 以删除动画?或者更糟糕的是用另一个动画实现setVisibility()?它只是从那里变得越来越丑陋。不要使用这种“解决方案”。
    • 最好叫它AnimatedLinearLayout
    【解决方案7】:
    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);
                    }
                });
    }
    

    【讨论】:

    • 这个答案的问题:1)糟糕的代码格式。 2)您使用代码 sn-p 发布实际上无法在浏览器中运行的代码。这不仅添加了两个无用的按钮,而且还破坏了语法高亮。 3)这只是一些随机代码转储,没有任何解释或目的。 4) 您在执行动画时正在更改可见性。除了这是明显的代码气味之外,这也将无法正常工作。改变可见性会开始一个新的布局过程,只有在完成之后,动画才真正具有可使用的值。名单还在继续……
    • 我已经编辑了您的答案以修复格式并将代码 sn-p 转换为实际的代码块。但是你必须填写其余的......
    • 对不起,伙计,我从你的代码中编写了代码,因为它对我来说效果不佳,我的这个代码有效。但我同意发布方式需要更改。
    • @AmeenMaheen setAlpha 是干什么用的?
    • @Igor Ganapolsky 它用于透明度,即产生褪色效果
    【解决方案8】:

    您可以在 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 的单位是什么?
    • 这里 120 和 0 是与 Y 轴相关的距离,如果您放置硬代码而不是在大屏幕或平板电脑中遇到问题,因此,您需要为所有不同的设备输入 string.xml 值中的值.持续时间是您要显示布局动画的时间....!!!对不起我的英语不好......!
    • @varotariyavajsi 这实际上并没有显示/隐藏视图的可见性。
    • 你好 igor ganapolsky 我知道这些......它只是在 y 方向上平移视图,如果用户需要像底部滑块一样上下显示它会正常工作。
    【解决方案9】:

    使用 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()
        }
    }
    

    【讨论】:

    • 小改进:我们可以使用常量 View.TRANSLATION_Y 代替“translationY”,并且在上滑 ObjectAnimation 中我们可以执行 .apply { doOnEnd { view.visibility = View.GONE } .... ...}.start()
    • 0.toFloat() 也可以是0f
    【解决方案10】:

    使用这个类:

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

    【讨论】:

      【解决方案11】:

      你可以用简单的三行代码来展示动画...

      //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)
      
                  }
      

      【讨论】:

        【解决方案12】:

        有了 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
        

        【讨论】:

          【解决方案13】:

          我有一个极端情况,我的视图高度仍然是 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);
                              }
                          });
              }
          
          }
          

          【讨论】:

            【解决方案14】:

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

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

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

            【讨论】:

            • 为什么需要 GlobalLayoutListener 来执行此操作?你为什么要以这种奇怪的方式设置可见性?为什么要在答案中包含与问题无关的启动延迟之类的内容?
            【解决方案15】:

            Suragch 在 Kotlin 中的回答。这对我有用。

            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
            }
            

            }

            【讨论】:

              【解决方案16】:

              一种简单的方法:

              containerView.setLayoutTransition(LayoutTransition())
              containerView.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
              

              【讨论】:

                【解决方案17】:

                这是处理多个Button(在本例中为ImageView)的另一种方法

                MainActivity.java

                findViewById(R.id.arrowIV).setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                
                                if (strokeWidthIV.getAlpha() == 0f) {
                                    findViewById(R.id.arrowIV).animate().rotationBy(180);
                
                                    strokeWidthIV.animate().translationXBy(-120 * 4).alpha(1f);
                                    findViewById(R.id.colorChooseIV).animate().translationXBy(-120 * 3).alpha(1f);
                                    findViewById(R.id.saveIV).animate().translationXBy(-120 * 2).alpha(1f);
                                    findViewById(R.id.clearAllIV).animate().translationXBy(-120).alpha(1f);
                                } else {
                                    findViewById(R.id.arrowIV).animate().rotationBy(180);
                
                                    strokeWidthIV.animate().translationXBy(120 * 4).alpha(0f);
                                    findViewById(R.id.colorChooseIV).animate().translationXBy(120 * 3).alpha(0f);
                                    findViewById(R.id.saveIV).animate().translationXBy(120 * 2).alpha(0f);
                                    findViewById(R.id.clearAllIV).animate().translationXBy(120).alpha(0f);
                                }
                            }
                        });
                
                

                activity_main.xml

                <?xml version="1.0" encoding="utf-8"?>
                <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 xmlns:app="http://schemas.android.com/apk/res-auto"
                 xmlns:tools="http://schemas.android.com/tools"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 tools:context=".activity.MainActivity">
                
                 <ImageView
                     android:id="@+id/strokeWidthIV"
                     android:layout_width="48dp"
                     android:layout_height="48dp"
                     android:layout_margin="8dp"
                     android:alpha="0"
                     android:contentDescription="Clear All"
                     android:padding="4dp"
                     android:scaleType="fitXY"
                     android:src="@drawable/ic_edit"
                     app:layout_constraintEnd_toEndOf="parent"
                     app:layout_constraintTop_toTopOf="parent"
                     tools:ignore="HardcodedText" />
                
                 <ImageView
                     android:id="@+id/colorChooseIV"
                     android:layout_width="48dp"
                     android:layout_height="48dp"
                     android:layout_margin="8dp"
                     android:alpha="0"
                     android:contentDescription="Clear All"
                     android:padding="4dp"
                     android:scaleType="fitXY"
                     android:src="@drawable/ic_palette"
                     app:layout_constraintEnd_toEndOf="parent"
                     app:layout_constraintTop_toTopOf="parent"
                     tools:ignore="HardcodedText" />
                
                 <ImageView
                     android:id="@+id/saveIV"
                     android:layout_width="48dp"
                     android:layout_height="48dp"
                     android:layout_margin="8dp"
                     android:alpha="0"
                     android:contentDescription="Clear All"
                     android:padding="4dp"
                     android:scaleType="fitXY"
                     android:src="@drawable/ic_save"
                     app:layout_constraintEnd_toEndOf="parent"
                     app:layout_constraintTop_toTopOf="parent"
                     tools:ignore="HardcodedText" />
                
                 <ImageView
                     android:id="@+id/clearAllIV"
                     android:layout_width="48dp"
                     android:layout_height="48dp"
                     android:layout_margin="8dp"
                     android:alpha="0"
                     android:contentDescription="Clear All"
                     android:padding="4dp"
                     android:scaleType="fitXY"
                     android:src="@drawable/ic_clear_all"
                     app:layout_constraintEnd_toEndOf="parent"
                     app:layout_constraintTop_toTopOf="parent"
                     tools:ignore="HardcodedText" />
                
                 <ImageView
                     android:id="@+id/arrowIV"
                     android:layout_width="48dp"
                     android:layout_height="48dp"
                     android:layout_margin="8dp"
                     android:contentDescription="Arrow"
                     android:padding="4dp"
                     android:scaleType="fitXY"
                     android:src="@drawable/ic_arrow"
                     app:layout_constraintEnd_toEndOf="parent"
                     app:layout_constraintTop_toTopOf="parent"
                     tools:ignore="HardcodedText" />
                </androidx.constraintlayout.widget.ConstraintLayout>
                

                【讨论】:

                  【解决方案18】:

                  一个完整的答案,它可以在 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 not working in big nested views,这基本上使其他视图在隐藏recyclerview时平滑向上移动

                  subRootLinearView.layoutTransition.enableTransitionType(LayoutTransition.CHANGING)
                  

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

                  android:animateLayoutChanges="true"
                  

                  然后用你的观点调用方法

                   toggleRecyclerViewVisibility(
                                      recycler,
                                      header,
                                      arrowImageView
                                  )
                  

                  【讨论】:

                    【解决方案19】:

                    来自 Kotlin 用户的ashakirov 的回答

                     val transition: Transition = Slide(Gravity.BOTTOM)
                                    transition.duration = 600
                                    transition.addTarget(you_parent_layout_id)
                    TransitionManager.beginDelayedTransition(rootLayoutId, transition)
                                    yourViewIdToHide.visibility = if (yourViewIdToHide.isShown) View.GONE else View.VISIBLE
                    

                    【讨论】:

                      猜你喜欢
                      • 2018-10-20
                      • 2015-03-29
                      • 2019-06-14
                      • 2012-06-26
                      • 1970-01-01
                      • 2016-05-03
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多