How to make custom CoordinatorLayout.Behavior with

2019-02-11 01:10发布

问题:

I try to make a parallax scrolling effect for google MapView and RecycleView using CoordinatorLayour.

so base on some tutorials found on web I made below code.

The layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <com.google.android.gms.maps.MapView android:id="@+id/map_view"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        app:layout_behavior="net.lunavulpo.coordinatorlayouttest.MapViewBehavior"
        />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_marginTop="200dp"
        android:layout_height="match_parent"/>

</android.support.design.widget.CoordinatorLayout>

and I made my implementation of CoordinatorLayout.Behavior:

    public class MapViewBehavior extends CoordinatorLayout.Behavior<MapView> {

        public MapViewBehavior(Context context, AttributeSet attrs) {
        }

        @Override
        public boolean layoutDependsOn(CoordinatorLayout parent, MapView child, View dependency) {
            return true;
        }

        @Override
        public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, MapView child, View directTargetChild, View target, int nestedScrollAxes) {
            return true;
        }

        @Override
        public void onNestedScroll(CoordinatorLayout coordinatorLayout, MapView child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {

            //child.setTranslationY(child.getTranslationY() - dyConsumed);
            child.setBottom(child.getBottom() - dyConsumed);
//what should I make here?

        }


    @Override
    public boolean onNestedFling(CoordinatorLayout coordinatorLayout, MapView child, View target, float velocityX, float velocityY, boolean consumed) {
//what should be here?
        return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
    }

    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, MapView child, View target, float velocityX, float velocityY) {

//what should be here?
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }
    }

How to correct implement the parallax scrolling effect? Maybe there is a library with ready to use Behaviors? or I miss something and there is a simplest way?

I don't want to use Toolbar for this at all.

回答1:

Update:

I'm going to give you an overview how to do it (I can't post all the answer because I posted it in another question with MORE requeriments than question and my old answer got marked as duplicated...)any way this is how you do it:

For custom CoordinatorLayout:
Because you can't extend BottomSheetBehavior, you have 2 options: trying doing your own Behavior or like I did, just copy paste the code from original BottomSheetBehavior and modifying it to get another state (when google maps open a bottomsheet it stop in the middle of the screen, this is the state that I'm talking about).
here the CustomBottomSheet with the behavior I was talking above

Now for Image parallax effect:
I tried all default parallax effect in XML avoiding a Custom behavior but in the end I ended doing one, its not hard, you need to override 2 methods like this:

public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
    return dependency instanceof NestedScrollView;
}

public boolean onDependentViewChanged(CoordinatorLayout parent, View child,
                                      View dependency) {

    if (mYmultiplier == 0) {
        initValues(child, dependency);
        return true;
    }

    float dVerticalScroll = dependency.getY() - mPreviousY;
    mPreviousY = dependency.getY();

    //going up
    if (dVerticalScroll <= 0 && child.getY() <= 0) {
        child.setY(0);
        return true;
    }

    //going down
    if (dVerticalScroll >= 0 && dependency.getY() <= mImageHeight)
        return false;

    child.setY( (int)(child.getY() + (dVerticalScroll * mYmultiplier) ) );

    return true;
}


Here the link to image parallax behavior

And here is how its looks like:
[]

A link to the project I must clarify that the previous link has FAB and toolbars animations but if you only want bottom and image behavior just ignore the others java files (clarifying because I have to "adjust to the question")



回答2:

As @Xaver Kapeller suggested, most painless way to achieve the parallax is to wrap it in CollapsingToolbarLayout with collapising mode PARALLAXand parallax multiplier of your choice.

However, if you plan to go with your own behavior, you need to write the implementation in onPreNestedScroll() method.

First, you only care about vertical scrolling. In onStartNestedScrolling() you check wither nestedScrollAxes is vertical.

Then you accumulate all the dy scrolled and translate the dummy view (that works as header) and MapView. Reset the mTotalDy to 0 when there is change in the direction of scrolling.

Then for the same negative mTotalDy value translate the ViewCompat.setTranslationY(mDummyView, -mTotalDy). To achieve proper parallax, the parallax should translate slower than the dummy view, which means that mTotalDy value should be at least half the speed of translation speed of the dummy view, i.e: ViewCompat.setTranslationY(mMapView, -mTotalDy/2). Therefore you will see it translating up/down slower.

View hierarchy should be: Make sure your z-order is preserved as I described

-CordinatorLayout
  -FrameLayout
    -MapView
    -DummyView (same height as MapView, different z-order)
    -NestedScrollView (same z-order as DummyView)

You should handle in same manner when there is nested flinging, hint: use target.getScrollY(), where target is the view that scrolls.