您的位置:首页 > 其它

自定义Behavior实现底部View的平移动画

2016-11-08 13:28 363 查看

自定义Behavior实现底部View的平移动画

最新在研究一些5.0的新控件,看着新控件的酷炫效果真是手痒痒恨不得一下子全吃肚子里了,奈何是个小菜,只得慢慢来了。写这个全当是做做笔记记录一下吧,方便以后使用。

说道5.0.我觉得不能不提CoordinatorLayout,通过实现NestedScrollingParent接口,配合子View实现NestedScrollingChild接口可以实现各种酷炫效果,比如说今天我们说的Toolbar的伸缩效果,我们都知道CoordinatorLayout嵌套Toolbar之后给Toolbar设置app:layout_scrollFlags=”scroll|enterAlways”,并给可以滚动的视图设置app:layout_behavior=”@string/appbar_scrolling_view_behavior”之后,Toolbar在视图滚动的时候有一个上下平移动画,如下图



现在我们的App一般都有底部导航的,既然头部可以伸缩,那如何给我们底部布局也加上一个伸缩的效果呢(平移动画),这就需要我们自定义behavior了。如图,图上底部布局是一个线性布局,需求是实现列表RecyclerView滑动的时候底部布局自动隐藏和显示



主界面xml

<?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="com.mteeyu.behaviordemo.MainActivity">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/id_toolbar"
android:layout_width="match_parent"
android:minHeight="?attr/actionBarSize"
android:layout_height="wrap_content"
android:paddingTop="@dimen/toolbar_padding_top"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_scrollFlags="scroll|enterAlways"
/>

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

<include layout="@layout/content_main"/>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:background="?attr/colorPrimary"
android:gravity="center_vertical"
android:orientation="horizontal"
app:layout_behavior="com.mteeyu.behaviordemo.FootBehavior"
>

<android.support.v7.widget.AppCompatImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:background="@drawable/ic_mood"/>

<android.support.v7.widget.AppCompatEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
android:background="#FFF"/>

</LinearLayout>

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


为了协调Toolbar的动画效果content_main中添加适配代码,这是content_main的布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
>

<android.support.v7.widget.RecyclerView
android:id="@+id/id_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"/>
</LinearLayout>


可以看到,为了实现底部布局的滑动效果,我加上了

app:layout_behavior="com.mteeyu.behaviordemo.FootBehavior"


这就是我们自定义的Behavior了。贴出Behavior的代码,这份代码差不多是照搬了BottomNavigationBar源码中Behavior的实现,因为自己写的是在是没他写的好,而且兼容性也很好,动画效果也是杠杠的。用到了泛型所以理论上支持所有控件。

public class FootBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {

private int mTotalDyUnconsumed = 0;
private int mTotalDyConsumed = 0;
private int mTotalDy = 0;

private static final Interpolator INTERPOLATOR = new LinearOutSlowInInterpolator ();
private int mFootViewHeight;
private int mDefaultOffset;

private ViewPropertyAnimatorCompat mTranslationAnimator;
private boolean hidden = false;

@ScrollDirection
private int mScrollDirection = ScrollDirection.SCROLL_NONE;
@ScrollDirection
private int mPreScrollDirection = ScrollDirection.SCROLL_NONE;
@ScrollDirection
private int mConsumedScrollDirection = ScrollDirection.SCROLL_NONE;

public FootBehavior () {
super ();
}

public FootBehavior (Context context, AttributeSet attrs) {
super (context, attrs);
}

@Retention (RetentionPolicy.SOURCE)
@IntDef ({ScrollDirection.SCROLL_DIRECTION_UP, ScrollDirection.SCROLL_DIRECTION_DOWN})
public @interface ScrollDirection {
int SCROLL_DIRECTION_UP = 1;
int SCROLL_DIRECTION_DOWN = -1;
int SCROLL_NONE = 0;
}

@ScrollDirection
public int getScrollDirection () {
return mScrollDirection;
}

@ScrollDirection
public int getConsumedScrollDirection () {
return mConsumedScrollDirection;
}

@ScrollDirection
public int getPreScrollDirection () {
return mPreScrollDirection;
}

@Override
public boolean onLayoutChild (CoordinatorLayout parent, final V child, int layoutDirection) {
parent.onLayoutChild (child, layoutDirection);

child.post (new Runnable () {
@Override
public void run () {
mFootViewHeight = child.getHeight ();
}
});

mDefaultOffset = 0;

return super.onLayoutChild (parent, child, layoutDirection);
}

//判断是否接受后续滑动事件
@Override
public boolean onStartNestedScroll (CoordinatorLayout coordinatorLayout, V child,
View directTargetChild, View target, int nestedScrollAxes) {

return (nestedScrollAxes & View.SCROLL_AXIS_VERTICAL) != 0;
}

//进行滑动事件处理
@Override
public void onNestedScroll (CoordinatorLayout coordinatorLayout, V child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed) {
super.onNestedScroll (coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
if (dyUnconsumed > 0 && mTotalDyUnconsumed < 0) {
mTotalDyUnconsumed = 0;
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
onNestedVerticalScrollUnconsumed (coordinatorLayout, child, mScrollDirection,
dyConsumed, mTotalDyUnconsumed);
} else if (dyUnconsumed < 0 && mTotalDyUnconsumed > 0) {
mTotalDyUnconsumed = 0;
mScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
onNestedVerticalScrollUnconsumed (coordinatorLayout, child, mScrollDirection,
dyConsumed, mTotalDyUnconsumed);
}
mTotalDyUnconsumed += dyUnconsumed;

if (dyConsumed > 0 && mTotalDyConsumed < 0) {
mTotalDyConsumed = 0;
mConsumedScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
onNestedVerticalScrollConsumed (coordinatorLayout, child, mConsumedScrollDirection,
dyConsumed, mTotalDyConsumed);
} else if (dyConsumed < 0 && mTotalDyConsumed > 0) {
mTotalDyConsumed = 0;
mConsumedScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
onNestedVerticalScrollConsumed (coordinatorLayout, child, mConsumedScrollDirection,
dyConsumed, mTotalDyConsumed);
}
mTotalDyConsumed += dyConsumed;
}

@Override
public void onNestedPreScroll (CoordinatorLayout coordinatorLayout, V child, View target,
int dx, int dy, int[] consumed) {
super.onNestedPreScroll (coordinatorLayout, child, target, dx, dy, consumed);
if (dy > 0 && mTotalDy < 0) {
mTotalDy = 0;
mPreScrollDirection = ScrollDirection.SCROLL_DIRECTION_UP;
onNestedVerticalPreScroll (coordinatorLayout, child, target, dx, dy, consumed,
mPreScrollDirection);
} else if (dy < 0 && mTotalDy > 0) {
mTotalDy = 0;
mPreScrollDirection = ScrollDirection.SCROLL_DIRECTION_DOWN;
onNestedVerticalPreScroll (coordinatorLayout, child, target, dx, dy, consumed,
mPreScrollDirection);
}
mTotalDy += dy;
}

//当进行快速滑动
@Override
public boolean onNestedFling (CoordinatorLayout coordinatorLayout, V child, View target,
float velocityX, float velocityY, boolean consumed) {
super.onNestedFling (coordinatorLayout, child, target, velocityX, velocityY, consumed);
return onNestedDirectionFling (coordinatorLayout, child, target, velocityX, velocityY,
consumed,
velocityY > 0 ? ScrollDirection.SCROLL_DIRECTION_UP : ScrollDirection.SCROLL_DIRECTION_DOWN);
}

public void onNestedVerticalScrollUnconsumed (CoordinatorLayout coordinatorLayout, V child,
@ScrollDirection int scrollDirection,
int currentOverScroll, int totalScroll) {

}

public void onNestedVerticalScrollConsumed (CoordinatorLayout coordinatorLayout, V child,
@ScrollDirection int scrollDirection,
int currentOverScroll, int totalConsumedScroll) {
handleDirection (child, scrollDirection);
}

private void handleDirection (V child, int scrollDirection) {
if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_DOWN && hidden) {
hidden = false;
animateOffset (child, mDefaultOffset);
} else if (scrollDirection == ScrollDirection.SCROLL_DIRECTION_UP && !hidden) {
hidden = true;
animateOffset (child, mFootViewHeight + mDefaultOffset);
}
}

private void animateOffset (V child, int offset) {
ensureOrCancelAnimator (child);
mTranslationAnimator.translationY (offset).start ();
}

private void ensureOrCancelAnimator (V child) {
if (mTranslationAnimator == null) {
mTranslationAnimator = ViewCompat.animate (child);
mTranslationAnimator.setDuration (400);
mTranslationAnimator.setInterpolator (INTERPOLATOR);
} else {
mTranslationAnimator.cancel ();
}
}

public void onNestedVerticalPreScroll (CoordinatorLayout coordinatorLayout, V child,
View target, int dx, int dy, int[] consumed,
@ScrollDirection int scrollDirection) {

}

protected boolean onNestedDirectionFling (CoordinatorLayout coordinatorLayout, V child,
View target, float velocityX, float velocityY,
boolean consumed,
@ScrollDirection int scrollDirection) {
if (consumed) {
handleDirection (child, scrollDirection);
}
return consumed;
}

}


需要注意的是,FootBehavior继承的是CoordinatorLayout.Behavior这个类,所以需要使用的时候,布局文件最外层就必须是CoordinatorLayout。值得一提的是,CoordinatorLayout中填充RecyclerView或者NestedScrollingView可以触发动画,但是像我们很熟悉的ListView,GridView等是触发不了的,解决办法是有,比如说嵌套NestedScrollingView,但是这样又有ScrollingView嵌套问题,目前完美解决办法我还是没有找到,毕竟是个菜鸟,如果有朋友找到了还请告诉我一下啊,谢谢。

最后附上最终实现效果



项目地址

https://github.com/MteeYu/BehaviorDemo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐