In the course of your work, you encounter this need.The two ListViews are side-by-side. The effect is that the left ListView (list1) keeps decreasing in width from right to left and the right ListView (list2) shifts to the left.Reverse animation works the same way, just modify the parameters.
Here are the ideas for implementation:
The panning drawing of list2 on the right is very simple. Just use ObjectAnimator to pan to the left. It's very simple, with specific code behind it, let's not say much.
-
The animation for list1 also tried to use ObjectAnimator, but it was not successful.The code is as follows:
ObjectAnimator anim2 = ObjectAnimator.ofFloat(ll_left, "width", 0F, -offset * dm.density);
Check to find that there is no setWidth method in View and mRight - mLeft in getWidth method, then modify the parameters that try to animate with mRight.
int right = rl_right.getRight(); ObjectAnimator anim2 = ObjectAnimator.ofFloat(ll_left, "right", 0F, right-offset * dm.density);
Or no, look at the internal implementation of setRight and setTranslationX methods that are different, which may be why animation is invalid.I am not familiar with the specific code of the View drawing process and I do not understand it.
This idea ended here. -
Instead, since ObjectAnimator is not available, use ValueAnimator to calculate width, or right, and setLayoutParams to modify the layout directly.
ValueAnimator anim2 = ValueAnimator.ofInt(0, offset); ............ anim2.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // TODO Auto-generated method stub int value = (Integer) animation.getAnimatedValue(); LayoutParams lp = (LayoutParams) ll_left.getLayoutParams(); lp.width = (int) (lp.width - value * dm.density); ll_left.setLayoutParams(lp); } });
Two problems occurred:
a) The animation effect is very poor, there is obvious Carton;
b) When the ListView panning drawing on the right is finished, another offset will be panned to the left.Reason analysis:
a) View the setLayoutParams method source and call the requestLayout method to refresh the component after reassigning LayoutParams.Looking at the requestLayout method again, it's probably looking from the child view to the parent view, then going to ViewRootImpl and going down one level to determine if the layout needs to be refreshed. RequLayout calls the measure and layout processes again instead of going to draw.The reason for Carton may be that the measurements plus layout process are too computationally intensive and require a simplified process.
b) The two ListView s are written inside the LinearLayout, and there is a layout dependency between them, so the decrease in the width of list1 causes the X value of list2 to decrease by one offset, which is what happens.Solution:
a) Width calculation is done in the animation listening, so the measure process is not required and the layout can be modified directly by calling the layout method.
b) Change the parent container to RelativeLayout, removing the dependencies on the two ListView locations.Modified code:
ValueAnimator anim2 = ValueAnimator.ofInt(0, offset); ............ final int right = rl_right.getRight(); anim2.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // TODO Auto-generated method stub int value = (Integer) animation.getAnimatedValue(); ll_left.layout(ll_left.getLeft(), ll_left.getTop(), (int)(right - value * dm.density) , ll_left.getBottom()); } });
-
The above code basically satisfies the animation effect and moves smoothly.After the actual operation, there is another problem.After clicking the item in List2 or calling the setSelection method of list2, the layout of list1 will revert to what it was before the width was reduced, and the extra part will emerge from the transparent background of list2.
Looking at the source code, you can see that both of them call the requestLayout method, as mentioned above, requestLayout calls the measure and layout methods because the previous animation only calls layout and does not actually change the width of the view, so it refreshes to its original state.
anim2.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // TODO Auto-generated method stub LayoutParams lp = (LayoutParams) ll_left.getLayoutParams(); lp.width = (int) (lp.width - offset * dm.density); ll_left.setLayoutParams(lp); isAnimating = false; } });
In fact, calling setLayoutParams at the beginning of the animation should be okay, but it will result in a logical error in focus, which is another problem and has nothing to do with what this article says.
Finally, complete code is attached:
private int offset = 200;
private int duration = 200;
private boolean isAnimating = false;
private void startAnim() {
//rl_right is list2, and DM is the android.util.DisplayMetrics class, which gets the screen density dm.density
ObjectAnimator anim1 = ObjectAnimator.ofFloat(rl_right, "translationX", 0F, -offset * dm.density);
anim1.setDuration(duration);
anim1.setInterpolator(new AccelerateDecelerateInterpolator());
final int right = ll_left.getRight(); //ll_left is list1
ValueAnimator anim2 = ValueAnimator.ofInt(0, offset);
anim2.setDuration(duration);
anim2.setInterpolator(new AccelerateDecelerateInterpolator());
anim2.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
int value = (Integer) animation.getAnimatedValue();
ll_left.layout(ll_left.getLeft(), ll_left.getTop()
, (int)(right - value * dm.density), ll_left.getBottom());
}
});
anim2.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
isAnimating = true;
logo.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
LayoutParams lp = (LayoutParams) ll_left.getLayoutParams();
lp.width = (int) (lp.width - offset * dm.density);
ll_left.setLayoutParams(lp);
isAnimating = false;
}
});
anim1.start();
anim2.start();
}
private void closeAnim() {
ObjectAnimator anim1 = ObjectAnimator.ofFloat(rl_right, "translationX", -offset * dm.density, 0F);
anim1.setDuration(duration);
anim1.setInterpolator(new AccelerateDecelerateInterpolator());
final int right = ll_left.getRight();
ValueAnimator anim2 = ValueAnimator.ofInt(0, offset);
anim2.setDuration(duration);
anim2.setInterpolator(new AccelerateDecelerateInterpolator());
anim2.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
int value = (Integer) animation.getAnimatedValue();
ll_left.layout(ll_left.getLeft(), ll_left.getTop()
, (int)(right + value * dm.density), ll_left.getBottom());
}
});
anim2.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
isAnimating = true;
LayoutParams lp = (LayoutParams) ll_left.getLayoutParams();
lp.width = (int) (lp.width + offset * dm.density);
ll_left.setLayoutParams(lp);
}
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
isAnimating = false;
logo.setVisibility(View.VISIBLE);
}
});
anim1.start();
anim2.start();
}