blob: ae61b62444e1e62a5b5e70becdfec7cb878c284b [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.design.widget;
import android.content.Context;
import android.support.design.widget.CoordinatorLayout.Behavior;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ScrollerCompat;
import android.util.AttributeSet;
import android.view.View;
/**
* The {@link Behavior} for a view that sits vertically above scrolling a view.
* See {@link HeaderScrollingViewBehavior}.
*/
abstract class HeaderBehavior<V extends View> extends ViewOffsetBehavior<V> {
private Runnable mFlingRunnable;
private ScrollerCompat mScroller;
public HeaderBehavior() {
super();
}
public HeaderBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout,
View header, int newOffset) {
return setHeaderTopBottomOffset(coordinatorLayout, header, newOffset,
Integer.MIN_VALUE, Integer.MAX_VALUE);
}
protected int setHeaderTopBottomOffset(CoordinatorLayout coordinatorLayout,
View header, int newOffset, int minOffset, int maxOffset) {
final int curOffset = getTopAndBottomOffset();
int consumed = 0;
if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
// If we have some scrolling range, and we're currently within the min and max
// offsets, calculate a new offset
newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
if (curOffset != newOffset) {
setTopAndBottomOffset(newOffset);
// Update how much dy we have consumed
consumed = curOffset - newOffset;
}
}
return consumed;
}
protected int getTopBottomOffsetForScrollingSibling() {
return getTopAndBottomOffset();
}
protected int scroll(CoordinatorLayout coordinatorLayout, View header,
int dy, int minOffset, int maxOffset) {
return setHeaderTopBottomOffset(coordinatorLayout, header,
getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset);
}
protected boolean fling(CoordinatorLayout coordinatorLayout, View layout, int minOffset,
int maxOffset, float velocityY) {
if (mFlingRunnable != null) {
layout.removeCallbacks(mFlingRunnable);
}
if (mScroller == null) {
mScroller = ScrollerCompat.create(layout.getContext());
}
mScroller.fling(
0, getTopAndBottomOffset(), // curr
0, Math.round(velocityY), // velocity.
0, 0, // x
minOffset, maxOffset); // y
if (mScroller.computeScrollOffset()) {
mFlingRunnable = new FlingRunnable(coordinatorLayout, layout);
ViewCompat.postOnAnimation(layout, mFlingRunnable);
return true;
} else {
mFlingRunnable = null;
return false;
}
}
private class FlingRunnable implements Runnable {
private final CoordinatorLayout mParent;
private final View mLayout;
FlingRunnable(CoordinatorLayout parent, View layout) {
mParent = parent;
mLayout = layout;
}
@Override
public void run() {
if (mLayout != null && mScroller != null && mScroller.computeScrollOffset()) {
setHeaderTopBottomOffset(mParent, mLayout, mScroller.getCurrY());
// Post ourselves so that we run on the next animation
ViewCompat.postOnAnimation(mLayout, this);
}
}
}
}