0

I'm trying to animate a view and hide it after some DP's were scrolled and i made everything fine, but the problem is that it will flick horribly when you are scrolling slowly before or after the Y value that is supposed to trigger the animation.

I think the flick is because i have to set its visibility to Gone and update the other view as match_parent, it won't work with just the TraslationY:

           view.animate()
                    .translationY(-view.getBottom())
                    .alpha(0)
                    .setDuration(HEADER_HIDE_ANIM_DURATION)
                    .setInterpolator(new DecelerateInterpolator());

I tried to set the layout to relative and View 2 as match_parent to see if i could avoid the visibility change but it didn't work...

I have implemented all required code from Google I/O 2014 BaseActivity.java file: https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/BaseActivity.java#L17

And the animation works... but i assume that, as my customview isn't an actionbar with overlay properties, the customview won't leave and LinearLayout below won't fill the empty space (there is none).

SO, i made it to work with an animationlistener and setting customview visibility to gone when the animation is over but it will flick in a horrible way when you are close to the expected Y point that trigger the animation (flick as customview visibility is gone and LinearLayout below needs to resize itself to fill the empty space, and will quickly repeat if you scroll slowly around there).

XML:

<?xml version="1.0" encoding="utf-8"?>

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


    android:clickable="false"
    android:animateLayoutChanges="true">

    <com.project.app.layouts.TabsLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
/>
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/tabs">
    <FrameLayout
        android:id="@+id/content_frame_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="1"
        android:background="@android:color/white"/>
    </LinearLayout>

</RelativeLayout>

Is there any way to do this when it's not an actionbar?

EDIT: Added the comment about how it will flick when you scroll slowly around Y point that triggers the animation to hide/show.

Motheus
  • 533
  • 5
  • 21
  • I recommend you to draw the steps of your animation you want to achieve on a piece of paper and post it here as image or you can use some drawing program. This way we will undestand what's the final effect you're looking for. – vovahost Nov 03 '14 at 23:31
  • It's easy, just want a view 1 to disappear and view 2 fill the empty space that view 1 left. Like when actionbar hides when you scroll down on the Google I/O 2014 but with a custom view, not an actionbar. – Motheus Nov 04 '14 at 00:53
  • Maybe it's easy to implement but it's difficult to understand how view 1 and view 2 are positioned in layout. You speak about setting the visibility to gone but we dont't see any code. If you want to get a good answer then please add some code and maybe a sketch. – vovahost Nov 04 '14 at 08:24
  • Give me a couple of minutes... – Motheus Nov 04 '14 at 11:29
  • 1
    That's better but you can give us some screenshots or yuotube video of two cases: one when it's working and the other one when you see it flick. – vovahost Nov 04 '14 at 13:48

3 Answers3

1

I recommend you to use android:hardwareAccelerated="true" attribute in your Manifest file. It will use your device's GPU to draw views and animations.

Arshak92
  • 634
  • 3
  • 10
  • 24
  • Sorry i forgot to add a small comment. I think that the issue is that when you are slowly scrolling trough that Y point that triggers the animation the flick happens. if* you scroll quickly then it will be fine. – Motheus Nov 04 '14 at 12:09
  • 1
    Also, my minSDK is 14, so android:hardwareAccelerated is true by default. – Motheus Nov 04 '14 at 12:12
  • @Motheus have you tried another Interpolator? Try to add acceleration attribute on manifest, because sometimes it is not enabled by default even if your minSDK is 14 – Arshak92 Nov 04 '14 at 12:14
  • @Motheus i have found some information in another question http://stackoverflow.com/questions/9387711/android-animation-flicker – Arshak92 Nov 04 '14 at 12:17
1

I suggest you to check the value of view.getBottom() in both cases (when it works and when not).
It may be that it flicks because the value returned by view.getBottom() is very big.
Add this line in your code:

Log.i("YourAppName", "view.getBottom(): -" + view.getBottom());
view.animate()
        .translationY(-view.getBottom())
        .alpha(0)
        .setDuration(HEADER_HIDE_ANIM_DURATION)
        .setInterpolator(new DecelerateInterpolator());

Then check your log to see if the values are the same or not.

vovahost
  • 34,185
  • 17
  • 113
  • 116
0

I have made it in a slightly different way. Note that the call I'm using requires SDK 19 (KitKat), but you can still do it using ViewPropertyAnimatorCompat

I have a FrameLayout that has the header view and the main view, with the header view on front.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fab="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <include layout="@layout/main_layout" android:id="@+id/main_layout" />
    <include layout="@layout/header_layout" android:id="@+id/header_layout" />
</FrameLayout>

Once the views are measured (posting a runnable during onResume) I set the topPadding of the main view to be the Height of the header.

In the hide and show animation, I add an update listener and inside it I update the top padding of the main view to be the Height of the header + the Translation on Y.

final View header = findViewById(R.id. header_layout);              
header.animate()
 .translationY(-header.getBottom())
 .setDuration(200)
 .setInterpolator(new DecelerateInterpolator())
 .setUpdateListener(new AnimatorUpdateListener() {          
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int top = (int) (header.getHeight()+header.getTranslationY());
        findViewById(R.id.main_view).setPadding(0, top, 0, 0);
    }
 });

This makes it a bit smoother, since the padding gets updated together with the translation.

Actually, setting an AnimationListener using ViewPropertyAnimatorCompat does not work. The listener is never called, so for backwards compatibility I opted for this solution, not elegant, but at least it work on pre-KitKat devices:

final View mainView = findViewById(R.id.main_view);
Runnable mainUpdateRunnable = new Runnable() {          
    @Override
    public void run() {
        int top = (int) (header.getHeight()+header.getTranslationY());
        mainView.setPadding(0, top, 0, 0);
        if (mAnimatingHeader) {
            mainView.post(this);
        }
    }
};
mainView.post(mainUpdateRunnable);

The variable mAnimatingHeader is updated using the AnimationListener (which works)

shalafi
  • 3,926
  • 2
  • 23
  • 27