Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running simultaneous animations on LinearLayout weight with ValueAnimator

I've tried so many different approaches to this and none of them have worked. I'm open to suggestion.

I am making a custom "button". It's not of type Button but will behave as a multi-state button. Each button has 3 differently colored glyphs which represent the different states (blue, white, and orange). Whichever glyph is in the primary state is much larger than the others, like so:

enter image description hereenter image description hereenter image description here

At the moment, each "button" is a LinearLayout with 3 ImageViews and each ImageView is populated by a PathShape drawable with weights 0.2 + 0.6 + 0.2 = 1.0. That part works just fine. The 0.6 is the primary state weight.

I have a simple button on screen to trigger the animation. The animation will reduce the current primary from 0.6 weight to 0.2 weight, and the new primary from 0.2 to 0.6. One shrinks, the other grows.

Problem: The animations will not run simultaneously, even though I've explicitly told them to run simultaneously. The first one shrinks from 0.6 to 0.2, then there is a slight pause, and then the second one grows from 0.2 to 0.6.

Button button = (Button)this.findViewById(R.id.shift);
button.setOnClickListener(new View.OnClickListener() {

    public Animator makeWeightAnimator(final View v, float startingWeight, float endingWeight) {
        long duration = 2000;

        ValueAnimator va = ValueAnimator.ofFloat(startingWeight, endingWeight);
        va.setDuration(duration);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                Float value = (Float) animation.getAnimatedValue();
                LinearLayout.LayoutParams paramsanim = (LinearLayout.LayoutParams)v.getLayoutParams();
                paramsanim.weight = value.floatValue();
                v.requestLayout();
            }
        });

        return va;
    }

    @Override
    public void onClick(View v) {

        float newBlueWeight = 0.2f;
        float newWhiteWeight = 0.2f;
        float newOrangeWeight = 0.2f;

        if (state == 0) {
            // Make blue larger
            newBlueWeight = 0.6f;
        } else if (state == 1) {
            // Make white larger
            newWhiteWeight = 0.6f;
        } else {
            // Make orange larger
            newOrangeWeight = 0.6f;
        }
        // Total will be 0.2 + 0.6 + 0.2 = 1.0

        List<Animator> animators = new LinkedList<Animator>();

        LinearLayout.LayoutParams blueParams = (LinearLayout.LayoutParams)blueImage.getLayoutParams();
        Log.d("TA", String.format("blue %f -> %f", blueParams.weight, newBlueWeight));
        if (Math.abs(blueParams.weight - newBlueWeight) > 0.001) {
            // new weight is different from existing weight
            Animator va = makeWeightAnimator(blueImage, blueParams.weight, newBlueWeight);
            animators.add(va);
        }

        LinearLayout.LayoutParams whiteParams = (LinearLayout.LayoutParams)whiteImage.getLayoutParams();
        Log.d("TA", String.format("white %f -> %f", whiteParams.weight, newWhiteWeight));
        if (Math.abs(whiteParams.weight - newWhiteWeight) > 0.001) {
            // new weight is different from existing weight
            Animator va = makeWeightAnimator(whiteImage, whiteParams.weight, newWhiteWeight);
            animators.add(va);
        }

        LinearLayout.LayoutParams orangeParams = (LinearLayout.LayoutParams)orangeImage.getLayoutParams();
        Log.d("TA", String.format("orange %f -> %f", orangeParams.weight, newOrangeWeight));
        if (Math.abs(orangeParams.weight - newOrangeWeight) > 0.001) {
            // new weight is different from existing weight
            Animator va = makeWeightAnimator(orangeImage, orangeParams.weight, newOrangeWeight);
            animators.add(va);
        }

        if (animators.size() > 0) {
            AnimatorSet s = new AnimatorSet();
            s.playTogether(animators);
            s.start();
        }

        state++;

        if (state > 2) {
            state = 0;
        }

    }
});

Here is a video I made showing the animations running sequentially instead of in parallel:

http://inadaydevelopment.com/stackoverflow/AndroidLinearLayoutAnimation.html

I have even tried combining all of the changes down into one animator instead of trying to have multiple animators running parallel, but the animations STILL happen sequentially. I feel like I'm losing my mind:

public Animator makeSimultaneousAnimator(final ViewGroup parentViewGroup, final View growingView, final View shrinkingView, final View otherView, float startingWeight, float endingWeight) {
    long duration = 2000;

    ValueAnimator va = ValueAnimator.ofFloat(startingWeight, endingWeight);
    va.setDuration(duration);
    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            Float value = (Float) animation.getAnimatedValue();
            float growingWeight = value.floatValue();

            LinearLayout.LayoutParams growingParams = (LinearLayout.LayoutParams)growingView.getLayoutParams();
            LinearLayout.LayoutParams shrinkingParams = (LinearLayout.LayoutParams)shrinkingView.getLayoutParams();
            LinearLayout.LayoutParams otherParams = (LinearLayout.LayoutParams)otherView.getLayoutParams();

            float otherWeight = otherParams.weight;
            float shrinkingWeight = 1.0f - growingWeight - otherWeight;

            growingParams.weight = growingWeight;
            shrinkingParams.weight = shrinkingWeight;

            parentViewGroup.requestLayout();
            //growingView.requestLayout();
            //shrinkingView.requestLayout();
        }
    });

    return va;
}
like image 299
Kenny Wyland Avatar asked Apr 05 '15 02:04

Kenny Wyland


1 Answers

I'd keep a reference to the big ImageView, to easily follow it. Then every time an event is fired, decrease the big one and increase another one.

I've done an example with a click listener on each ImageView inside a LinearLayout.

The big one initially is the middle ImageView and it's weight is pre-set to be 0.6 while others have it at 0.2.

Code:

private static final float FROM_WEIGHT = 0.2f;
private static final float TO_WEIGHT = 0.6f;
private static final int DURATION = 500;

private View v1, v2, v3, big;
private View.OnClickListener clickListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (v == big) {
            return;
        } else if (v1 == big) {
            animate(v1, TO_WEIGHT, FROM_WEIGHT);
        } else if (v2 == big) {
            animate(v2, TO_WEIGHT, FROM_WEIGHT);
        } else if (v3 == big) {
            animate(v3, TO_WEIGHT, FROM_WEIGHT);
        }
        big = v;
        animate(v, FROM_WEIGHT, TO_WEIGHT);
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    v1 = findViewById(R.id.tv1);
    big = v2 = findViewById(R.id.tv2);
    v3 = findViewById(R.id.tv3);
    v1.setOnClickListener(clickListener);
    v2.setOnClickListener(clickListener);
    v3.setOnClickListener(clickListener);
}

private void animate(final View v, float from, float to) {
    ValueAnimator va = ValueAnimator.ofFloat(from, to);
    va.setDuration(DURATION);
    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            float growingWeight = (Float) animation.getAnimatedValue();
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) v.getLayoutParams();
            params.weight = growingWeight;
            v.setLayoutParams(params);
        }
    });
    va.start();
}

Layout:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ImageView
        android:background="#00FFFF"
        android:layout_weight="0.2"
        android:id="@+id/tv1"
        android:layout_width="100dp"
        android:layout_height="0dp"/>

    <ImageView
        android:background="#FFFF00"
        android:layout_weight="0.6"
        android:id="@+id/tv2"
        android:layout_width="100dp"
        android:layout_height="0dp"/>

    <ImageView
        android:background="#FF00FF"
        android:layout_weight="0.2"
        android:id="@+id/tv3"
        android:layout_width="100dp"
        android:layout_height="0dp"/>
</LinearLayout>

Effect:

effect example

like image 173
Simas Avatar answered Nov 04 '22 12:11

Simas