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:
- Using the
measuremethod to accurately obtain the view's expected height - Setting initial height to 1 pixel to avoid animation cancellation issues in older Android versions
- Setting height to
WRAP_CONTENTat animation completion to ensure view adapts to content changes - Dynamically calculating animation duration based on pixel density for consistent visual speed
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:
- Set appropriate animation durations to avoid affecting user experience with overly long or short animations
- Perform accurate measurements before animation starts to avoid performance issues from layout calculations
- Consider using modern animation APIs like
ValueAnimatorfor complex animation scenarios - Ensure proper handling of view recycling when using animations in lists
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.