Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Layout animation not working on first run

I have an Activity with a list of items and when you click on an item, I want playback controls for that item to slide up from the bottom of the screen and become visible. I've defined an animation set for the slide in and the slide out and they work. I've setup my animationListener in my activity and started my slide in animation onClick of an item. My problem is, the first time I run the app, when I click on an item, the onClick callback is executed, but the animation doesn't happen. Second time I click, the slide in animation happens, but not the slide out. Third and subsequent times, it works as expected. Here are my animation sets.

vm_slide_in.xml

    <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" 
           android:fillAfter="true">
    <translate
        android:fromYDelta="800"
        android:toYDelta="0"
        android:duration="600" />
</set>

vm_slide_out.xml

    <?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" 
           android:fillAfter="true">
    <translate
        android:fromYDelta="0"
        android:toYDelta="800"
        android:duration="600" />
</set>

Here is my activity layout

    <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/AppBG">
    <RelativeLayout 
        style="@style/LogoBar" 
        android:id="@+id/logo_bar"      
        android:layout_alignParentTop="true">
        <include layout="@layout/logobar"></include>
    </RelativeLayout>
    <RelativeLayout 
        style="@style/TitleBar" 
        android:id="@+id/title_bar"     
        android:layout_below="@+id/logo_bar">
        <include layout="@layout/titlebar"></include>"
        <Button style="@style/TitleBarButton"
            android:id="@+id/vm_speaker_btn"
            android:text="@string/vm_speaker_btn_label" 
            android:layout_alignParentLeft="true"
            android:layout_margin="4dp">
        </Button>
        <Button style="@style/TitleBarButton"
            android:id="@+id/vm_edit_btn"
            android:text="@string/vm_edit_btn_label" 
            android:layout_alignParentRight="true"
            android:layout_margin="4dp">
        </Button>
    </RelativeLayout>
    <ListView
        android:id="@+id/@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/title_bar"/>
    <RelativeLayout
        android:id="@+id/vm_control_panel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/footer"
        android:visibility="gone">
        <include layout="@layout/vm_control"/>
    </RelativeLayout>
    <RelativeLayout
        android:id="@+id/footer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">
        <include layout="@layout/taskbar"/>
    </RelativeLayout>
</RelativeLayout>

Here is the layout for the included control

    <?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
    <RelativeLayout 
        android:id="@+id/vm_control"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/dark_grey">
        <TextView
            android:id="@+id/vm_ctl_branding"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="5dp"
            android:text="@string/branding"
            android:textColor="@color/white">
        </TextView>
        <TextView style="@style/TimeMark"
            android:id="@+id/vm_timestamp"
            android:layout_toLeftOf="@+id/vm_progress"
            android:layout_below="@+id/vm_ctl_branding"
            android:layout_marginRight="3dp"
            android:text="0:00">
        </TextView>
        <SeekBar
            android:id="@+id/vm_progress"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:layout_below="@+id/vm_ctl_branding"
            android:layout_centerHorizontal="true">
        </SeekBar>
        <TextView style="@style/TimeMark"
            android:id="@+id/vm_timeleft"
            android:layout_toRightOf="@+id/vm_progress"
            android:layout_below="@+id/vm_ctl_branding"
            android:layout_marginLeft="3dp"
            android:text="-0:00">
        </TextView>
        <LinearLayout
            android:id="@+id/vm_action_button_layout"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/vm_timestamp"
            android:layout_alignRight="@+id/vm_timeleft"
            android:orientation="horizontal"
            android:layout_below="@+id/vm_progress">
            <TextView style="@style/Vm_Action_Button"
                android:background="@drawable/vm_action_btn_call"
                android:id="@+id/vm_callback_btn"
                android:layout_marginRight="5dp"
                android:text="@string/vm_action_btn_callback">
            </TextView>
            <TextView style="@style/Vm_Action_Button"
                android:background="@drawable/vm_action_btn_delete"
                android:id="@+id/vm_delete_btn"
                android:layout_marginLeft="5dp"
                android:text="@string/vm_action_btn_delete">
            </TextView>
        </LinearLayout>
    </RelativeLayout>
</merge>

From my onCreate method...

// Handle list item selections
ListView lv = getListView();
lv.setOnItemClickListener(new OnItemClickListener() {
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        final Voicemail vmItem = (Voicemail) vmla.getItem(position);
        Toast.makeText(ctx, "Name: " + vmItem.getCallerName() + " Position: " + position, Toast.LENGTH_SHORT)
                .show();
        vVm_controls.startAnimation(mSlideIn);
    }
});

And my animation callbacks...

@Override
public void onAnimationStart(Animation animation) {
    vm_controls_in = !vm_controls_in;
    if (vm_controls_in) {
        vVm_controls.setVisibility(View.GONE);
    } else {
        vVm_controls.setVisibility(View.VISIBLE);
        // vVm_controls.bringToFront();
    }
}

@Override
public void onAnimationEnd(Animation animation) {
    if (!vm_controls_in) {
        try {
            Thread.sleep(1000);
            vVm_controls.startAnimation(mSlideOut);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

@Override
public void onAnimationRepeat(Animation animation) {
    // TODO Auto-generated method stub

}
like image 911
brockoli Avatar asked Dec 21 '10 19:12

brockoli


3 Answers

As you discovered, the issue is visibility. When a view is initially set as gone in the xml file, Android will not render the layout until the visibility is changed to visible or invisible. If you attempt to animate on a view that has not been rendered yet, the animation will occur on the view without a layout. After the animation has completed, it will render it and suddenly the view will appear without animation. It works subsequent times because the view has a layout even when set to gone.

The key is to set the visibility to invisible instead of gone in the xml file so that it is rendered yet hidden. When the animation occurs the first time the view appear and will move as expected.

like image 187
mindriot Avatar answered Oct 23 '22 05:10

mindriot


Of course not 20 minutes after posting this question I figured out my problem. Since I was setting the visibility of my layout to "GONE", when I tried to start the animation on it, it wasn't triggering the first time. Not sure why it goes the 2nd and subsequent times, but if I set the visibility to "VISIBLE" right before I start the animation, it fixed the problem. Here is the code where I changed it...

// Handle list item selections
ListView lv = getListView();
lv.setOnItemClickListener(new OnItemClickListener() {
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        final Voicemail vmItem = (Voicemail) vmla.getItem(position);
        Toast.makeText(ctx, "Name: " + vmItem.getCallerName() + " Position: " + position, Toast.LENGTH_SHORT)
                .show();
        vVm_controls.setVisibility(View.VISIBLE);
        vVm_controls.startAnimation(mSlideIn);
    }
});
like image 22
brockoli Avatar answered Oct 23 '22 06:10

brockoli


I do not know whether it is still up to date but yeah, it is a problem with the visibility. So I set the visibility in the xml file to invisible and called at the place where your view is initialized:

view.post(new Runnable() {
    @Override
    public void run() {
        view.animate().translationY(view.getHeight());
    }
};

Now the call

view.animate().translationY(0).setListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationStart(Animator animation) {
        super.onAnimationStart(animation);
        view.setVisibility(View.VISIBLE);
    }
}

works also on the first time.

like image 6
Jason Saruulo Avatar answered Oct 23 '22 05:10

Jason Saruulo