0
votes

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.

3
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
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
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
Give me a couple of minutes...Motheus
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

3 Answers

1
votes

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.

1
votes

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.

0
votes

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)