自定义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
相关文章推荐
- Android自定义View之实现流行的新浪微博底部菜单:高仿“咸鱼APP”的底部菜单动画效果。
- 实现高端的自定义View属性动画自定义圆改变颜色进行下平移
- 利用ViewFlipper实现View间的动画效果(平移/渐变...)
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu
- 2014-11-3Android学习------关于自定义视图View的时候需要调用onMeasure--------GIF动画实现
- android自定义SurfaceView实现跑男动画
- 千变万化的ViewPager切换动画(2)--自定义ViewPager的实现方法
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu
- iOS开发导航控制器下不同视图控制器之间切换:利用CATrasition和view的layer层来实现自定义的动画效果
- Android 自定义View修炼-自定义可动画展开收缩View的实现
- Android高手进阶篇3-自定义ListView实现底部View自动隐藏和消失的功能
- 实现一个view从顶部移到底部的动画 and 将RGB值转化为颜色
- viewController自定义转场动画的实现
- Silverlight实用窍门系列:60.Silverlight中自定义Behavior,实现图片动画渐变Behavior
- (转)android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu
- android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]
- Android高手进阶篇3-自定义ListView实现底部View自动隐藏和消失的功能
- 底部导航栏(自定义View+ViewPager实现) android项目详解
- 自定义View -- 实现字符串一个一个显示的动态动画