Optimized Implementation of Expand and Collapse Animations in Android

Nov 21, 2025 · Programming · 12 views · 7.8

Keywords: Android Animation | View Expansion | View Collapse | Animation Class | Performance Optimization

Abstract: This article provides an in-depth exploration of two primary methods for implementing expand and collapse animations on the Android platform: custom animation based on the traditional Animation class and system-automated animations using the animateLayoutChanges attribute. It focuses on key technical aspects from the best answer, including measuring target height, setting initial height to 1 pixel to avoid flickering, and dynamically calculating animation duration. The article compares the applicability and performance of different methods, offers complete code examples, and provides practical recommendations to help developers address common issues in animation implementation.

Problem Background and Challenges

In Android application development, implementing expand and collapse animations for views is a common requirement. Developers typically want to enhance user experience through smooth animation effects when showing or hiding views. However, various technical challenges often arise during implementation, with the most typical being flickering issues at the start of animations.

Analysis of Traditional Animation Implementation Issues

Initial attempts often use the traditional Animation class for implementation. As shown in the example code, developers dynamically modify view height by overriding the applyTransformation method:

Animation a = new Animation() {
    int initialHeight;

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final int newHeight = (int)(initialHeight * interpolatedTime);
        v.getLayoutParams().height = newHeight;
        v.requestLayout();
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        initialHeight = height;
    }

    @Override
    public boolean willChangeBounds() {
        return true;
    }
};

While this approach achieves basic animation effects, noticeable flickering occurs during actual execution. The root cause lies in the view briefly displaying at full size before the animation is applied.

Optimized Expand Animation Implementation

To address the aforementioned issues, we propose an optimized solution. The core concept involves accurately measuring the target height before animation starts and setting an appropriate initial state:

public static void expand(final View v) {
    int matchParentMeasureSpec = View.MeasureSpec.makeMeasureSpec(((View) v.getParent()).getWidth(), View.MeasureSpec.EXACTLY);
    int wrapContentMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    v.measure(matchParentMeasureSpec, wrapContentMeasureSpec);
    final int targetHeight = v.getMeasuredHeight();
    
    v.getLayoutParams().height = 1;
    v.setVisibility(View.VISIBLE);
    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            v.getLayoutParams().height = interpolatedTime == 1
                    ? LayoutParams.WRAP_CONTENT
                    : (int)(targetHeight * interpolatedTime);
            v.requestLayout();
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };
    
    a.setDuration((int)(targetHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}

Key technical aspects of this implementation include:

Corresponding Collapse Animation Implementation

Corresponding to the expand animation, the collapse implementation also needs to consider smooth state transitions:

public static void collapse(final View v) {
    final int initialHeight = v.getMeasuredHeight();

    Animation a = new Animation() {
        @Override
        protected void applyTransformation(float interpolatedTime, Transformation t) {
            if(interpolatedTime == 1){
                v.setVisibility(View.GONE);
            }else{
                v.getLayoutParams().height = initialHeight - (int)(initialHeight * interpolatedTime);
                v.requestLayout();
            }
        }

        @Override
        public boolean willChangeBounds() {
            return true;
        }
    };
    
    a.setDuration((int)(initialHeight / v.getContext().getResources().getDisplayMetrics().density));
    v.startAnimation(a);
}

In the collapse animation, we only set the view to GONE state when the animation completely finishes, ensuring continuity throughout the animation process.

System Automated Animation Solution

Beyond custom animation implementations, the Android system provides a more convenient automated animation solution. By setting the android:animateLayoutChanges="true" attribute in the layout file, the system automatically handles animation effects during view visibility changes:

<LinearLayout android:id="@+id/container"
android:animateLayoutChanges="true"
...
/>

This method suits simple layout change scenarios, but for complex animation requirements or performance-sensitive situations, custom animation solutions offer greater flexibility.

Special Handling in RecyclerView

When using expand and collapse animations in RecyclerView, special attention must be paid to view recycling handling. The correct approach involves setting view visibility in onBindViewHolder and triggering animations through notifyItemChanged:

override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
    holder.list.visibility = data[position].listVisibility
    holder.expandCollapse.setOnClickListener {
        data[position].listVisibility = if (data[position].listVisibility == View.GONE) View.VISIBLE else View.GONE
        notifyItemChanged(position)
    }
}

For performance-sensitive scenarios, payload-based update methods can be used to avoid unnecessary rebinding:

private const val UPDATE_LIST_VISIBILITY = 1

override fun onBindViewHolder(holder: ItemViewHolder, position: Int, payloads: MutableList<Any>) {
    if (payloads.contains(UPDATE_LIST_VISIBILITY)) {
        holder.list.visibility = data[position].listVisibility
    } else {
        onBindViewHolder(holder, position)
    }
}

Performance Optimization and Best Practices

In practical development, animation performance optimization is crucial. Here are some practical optimization recommendations:

Conclusion

Implementing expand and collapse animations for Android views requires comprehensive consideration of multiple factors including performance, compatibility, and user experience. Through the optimized solutions introduced in this article, developers can effectively resolve flickering issues during animation processes and achieve smooth view transformation effects. Whether choosing custom animation implementations or system automated animations, appropriate technical selections should be made based on specific application scenarios and performance requirements.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.