本来今天早上起来就想写这篇文章的,但是看着心爱的骑士队正在打比赛,想着詹皇的2:0。。。想着猛龙的东部第一,今天真的是给猛龙打成“朦胧”了。。。猛龙的球迷不会打我吧!!!哈哈
昨天有小伙伴说要我说一下Behavior的使用,对于这个东西我也不是很了解,但是不了解可以学吗!!!其实作为程序员,我们在日常开发中会接触到很多新东西,不是我们每一个都要去了解的,我们也不会有那么多的精力。但是为什么有的人可以会的那么多呢?其实有的时候我总在想这个问题,后来我发现一件很有意思的事情,他们也不见得什么都懂,但是以往的经历让他们知道怎么去接触一个新的东西,怎么去快速上手这个东西。其实我们应该培养的是解决问题的能力,而不是什么都会。。。好了,闲话就扯到这里吧!下面开始今天的内容。其实我挺喜欢詹皇的!哈哈。。。
本文知识点:
- Behavior是什么东东:
- 已有的Behavior有哪些:
- BottomSheetBehavior的使用
- SwipeDismissBehavior的使用
- Behavior里面回调的说明
- 几个常见的Behavior的案例:
1. Behavior是什么东东
关于Behavior的描述是这样的
Interaction behavior plugin for child views of CoordinatorLayout.
A Behavior implements one or more interactions that a user can take on a child view. These interactions may include drags, swipes, flings, or any other gestures.
简单的翻译一下:CoordinatorLayout中子View的交互行为,可以在CoordinatorLayout的子类中实现一个或多个交互,这些交互可能是拖动,滑动,闪动或任何其他手势。其实就是实现CoordinatorLayout内部控件的交互行为,可以在非侵入的方式实现相应的交互!他能做什么呢?看了后面就知道了!!!哈哈。。。
2. 已有的Behavior有哪些:
关于这个问题,我去google找了找,下面这张图说明一切:
但其实,开发中常用的就BottomSheetBehavior、SwipeDismissBehavior剩下的就是自定义了。
2.1 BottomSheetBehavior的使用
BottomSheetBehavior主要是实现从底部弹出内容的Behavior。其实这个里面包含很多内容,像BottomSheet、BottomSheetDialog、BottomSheetDialogFragment我们这里一个一个说明一下:
2.1.1 BottomSheet的使用
这个一般是使用在相应的布局中的!为什么这么说呢,因为它可以直接在布局中使用,就酱紫啊!!!先介绍一下里面比较重要的概念,否则我怕你吓啥么不知道啥么啥(原谅我的一口东北话)!
- app:layout_behavior="@string/bottom_sheet_behavior" 最重要的一句,没有它都是耍流氓!
- app:behavior_peekHeight="0dp" 可见的部分高度(我发现如果不设置这个东西,底部的内容会一直在上面不会相应事件,所以如果你不想看见它就设置成0否则随意)
- app:behavior_hideable="true" 是否能通过下滑手势收起
- static <V extends View> BottomSheetBehavior<V> from(V view) 获取相应的BottomSheetBehavior对象
- setBottomSheetCallback(BottomSheetCallback callback) 相应的监听
- onStateChanged(@NonNull View bottomSheet, int newState) 状态改变的回调
- onSlide(@NonNull View bottomSheet, float slideOffset) 滑动的时候调用
- getState() 获取相应的状态
- setState(final @State int state) 设置相应的状态
- STATE_COLLAPSED: 默认的折叠状态
- STATE_DRAGGING : 过渡状态
- STATE_SETTLING: 视图从脱离手指自由滑动到最终停下的这一小段时间
- STATE_EXPANDED: bottom sheet 处于完全展开的状态
- STATE_HIDDEN : 默认无此状态(可通过app:behavior_hideable 启用此状态),启用后用户将能通过向下滑动完全隐藏
根布局是CoordinatorLayout,这个是重点啊!!!
这里注意几点问题:
- 获取BottomSheetBehavior对象的时候,使用的是设置有
app:layout_behavior="@string/bottom_sheet_behavior"
的布局 - app:behavior_peekHeight="0dp"一定要设置,否则你会在界面上一直看到它,并无法响应你的手势
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.jinlong.newmaterialdesign.behavior.BehaviorActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="bottomSheet"
android:text="展示bottomSheet" />
<LinearLayout
android:id="@+id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:behavior_peekHeight="50dp"
app:layout_behavior="@string/bottom_sheet_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#f25b41"
android:gravity="center"
android:text="底部还有内容啊!!!" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#009988"
android:gravity="center"
android:text="标签1" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#002288"
android:gravity="center"
android:text="标签2" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#009922"
android:gravity="center"
android:text="标签3" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#00aa88"
android:gravity="center"
android:text="标签4" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#999988"
android:gravity="center"
android:text="标签5" />
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
主页面的逻辑
BottomSheetBehavior<LinearLayout> bottomSheetBehavior = BottomSheetBehavior.from(mLlBottomSheet);
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
//展开状态,隐藏
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
//其他的状态展开
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
2.1.2 BottomSheetDialog的使用
其实这个东西的使用和对话框的使用基本上是一样的。你setContentView()进去一个布局,然后调用show()方法展示一下就可以了,但是这里有一个特别需要注意的地方,如果你在对话框中设置的布局超过整个屏幕的话(这里不是说你设置了match就是全屏了,是有效内容。这里建议你试试就知道了),整个内容不会铺满全屏,顶部会留出一段空间,和peek的效果类似,这里注意一下就可以了!其他的使用和对话框的使用一样,这里直接贴一下主要代码!!!
BottomSheetDialog sheetDialog = new BottomSheetDialog(this); sheetDialog.setContentView(R.layout.sheet_dialog);
sheetDialog.show();
2.1.3 BottomSheetDialogFragment的使用
其实这个和写一个Fragment是一样的,但是也存在和上面弹出对话框的那种问题,就是当你布局过大的情况下会留出一段空间。
这里主要说明两点问题:
- 使用这个获取相应的BottomSheetBehavior
BottomSheetBehavior.from((View) view.getParent());
- 使用
show(getSupportFragmentManager(), "dialog");
显示。
2.2 SwipeDismissBehavior的使用
这个是滑动消失和滑动关闭,很多情况下都是和5.0新出的Snackbar。一个和Toast类似的东西,因为不是本文重点,所以关于Snackbar就不展开说了!其实除了Snackbar使用到这个,基本上没有那个APP想把自己的页面划没了吧!!!其实用法还是很简单的,主要创建一个对象,设置一些像一个的参数就可以了!直接上代码:
TextView tvTitle = findViewById(R.id.tv_title);
SwipeDismissBehavior<View> mSwipe = new SwipeDismissBehavior();
/*
* SWIPE_DIRECTION_START_TO_END 只能从左向右滑动
* SWIPE_DIRECTION_END_TO_START 只能从右向左滑动
* SWIPE_DIRECTION_ANY 左右滑动都可以
*/
mSwipe.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_ANY);
mSwipe.setListener(new SwipeDismissBehavior.OnDismissListener() {
@Override
public void onDismiss(View view) {
//View消失的回调
}
@Override
public void onDragStateChanged(int state) {
/*
* STATE_IDLE 空闲状态
* STATE_DRAGGING 滑动中
* STATE_SETTLING 消失
*/
}
});
注释已经很详细了,这里注意一点啊,如果设置了滑动删除功能,这个页面就存在滑动删除的功能了,是页面存在这个功能,里门的大多数控件都能存在滑动删除功能,但是我尝试了,AppBarLayout等一些相应的控件不能,估计是设置了behavior的控件不能滑动删除,其他的都可以,但是这个只是我的猜测,没有验证!!!
3. Behavior里面回调的说明:
这里先明确一个概念,behavior的嵌套滚动都是依照一个相应的参考物,所以在自定义的时候一定要区分哪个是依照的View哪个是被观察的View,只有区分了这些才能更好的理解下面的内容,下面出现的所有child都是被观察的View,也就是xml中定义behavior的View。
-
layoutDependsOn(CoordinatorLayout parent, View child, View dependency) 表示是否给应用了Behavior 的View 指定一个依赖的布局
- 参数1:coordinatorlayout对象
- 参数2:child 被观察的View
- 参数3:依赖变化的View(被观察的View)
onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) 当依赖的View发生变化的时候hi掉的方法
-
onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) 当用户手指按下的时候,你是否要处理这次操作。当你确定要处理这次操作的时候,返回true;如果返回false的时候,就不会去响应后面的回调事件了。你想怎么滑就怎么话,我都不做处理。这里的(axes)滚动方向很重要,可以通过此参数判断滚动方向!
- 参数3:直接目标,相当于能滑动的控件
- 参数4:观察的View
- 参数5:这个可以简单理解为滚动方向
- ViewCompat#SCROLL_AXIS_HORIZONTAL 水平方向
- ViewCompat#SCROLL_AXIS_VERTICAL 竖直方向
- 参数6:这个参数是之后有的,如果你输入的类型不是TYPE_TOUCH那么就不会相应这个滚动
onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, @ScrollAxis int axes, @NestedScrollType int type) 当onStartNestedScroll准备处理这次滑动的时候(返回true的时候),回调这个方法。可以在这个方法中做一些响应的准备工作!
-
onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, @NestedScrollType int type) 当滚动开始执行的时候回调这个方法。
- 参数4/参数5:用户x/y轴滚动的距离(注意这里是每一次都回调的啊!!!)
- 参数6:处理滚动的距离的参数,内部维护着输出距离,假设用户滑动了100px,child 做了90px的位移,你需要把consumed[1]的值改成90,这样coordinatorLayout就能知道只处理剩下的10px的滚动。其中consumed[0]代表x轴、consumed[1]代表y轴。可能你不理解这个问题,换个形象点的比喻,比如你开发某一个功能,但是你只会其中的90%那么怎么办呢?不能就不管了。好你找到了你的同事或者老大,让他去完成剩下的10%。这样问题就完美的解决了,是一个概念的!
-
onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) 上面这个方法结束的时候,coordinatorLayout处理剩下的距离,比如还剩10px。但是coordinatorLayout发现滚动2px的时候就已经到头了。那么结束其滚动,调用该方法,并将coordinatorLayout处理剩下的像素数作为参
(dxUnconsumed、dyUnconsumed)
传过来,这里传过来的就是 8px。参数中还会有coordinatorLayout处理过的像素数(dxConsumed、dyConsumed)。老大开始处理剩下的距离了!这个方法主要处理一些越界后的滚动。还是不懂对吧!还拿你们老大做比喻:比如上面还剩 10%的工作,这时老大处理了2%后发现已经可以上线了,于是老大结束了工作,并将处理剩下的内容(dxUnconsumed、dyUnconsumed)纪录下来,告诉你。老大处理了的内容(dxConsumed、dyConsumed)也告诉了你。- 参数4/参数5:当没有滚动到顶部或者底部的时候,x/y轴的滚动距离
- 参数6/参数7:当滚动到顶部或者底部的时候,x/y轴的滚动距离
if (dyConsumed > 0 && dyUnconsumed == 0) {
System.out.println("上滑中。。。");
}
if (dyConsumed == 0 && dyUnconsumed > 0) {
System.out.println("到边界了还在上滑。。。");
}
if (dyConsumed < 0 && dyUnconsumed == 0) {
System.out.println("下滑中。。。");
}
if (dyConsumed == 0 && dyUnconsumed < 0) {
System.out.println("到边界了,还在下滑。。。");
}
-
onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, float velocityX, float velocityY) 当手指松开发生惯性动作之前调用,这里提供了响应的速度,你可以根据速度判断是否需要进行折叠等一系列的操作,你要确定响应这个方法的话,返回true。
- 参数4/参数5:代表相应的速度
onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) 停止滚动的时候回调的方法。当你不去响应Fling的时候会直接回调这个方法。在这里可以做一些清理工作。或者其他的内容。。。
-
onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) 确定子View位置的方法,这个方法可以重新定义子View的位置(这里明确是设置behavior的那个View哦),例如下面这样
- ViewCompat#LAYOUT_DIRECTION_LTR 视图方向从左到右
- ViewCompat#LAYOUT_DIRECTION_RTL 视图方向从优到左
基本上能用到的API就这么多,但是这里面的内容很多,先好好理解一下,我其实都不怎么理解,没事不理解没事,后面几个例子就ok了!
4. 几个常见的Behavior的案例:
这里说明一下自定义Behavior分为两种类型,一种是依赖相应的View变化而变化、一种是依赖滚动变化。
4.1 依赖于某个View的变化而变化的Behavior
这个最经典的案例就是底栏跟随AppBarLayout移动给移动,其实代码很简单,只要算出AppBarLayout的移动距离,动态的设置给相应的依赖控件就可以了。一波代码走起!!!先上一张效果图。
public class TwoBehavior extends CoordinatorLayout.Behavior<View> {
private String TAG = TwoBehavior.class.getSimpleName();
//这个千万要写,否则会出异常
public TwoBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
//依赖于AppBarLayout的
return dependency instanceof AppBarLayout;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
//计算出AppBarLayout移动的距离
float top = Math.abs(dependency.getTop());
Log.e(TAG, "AppBarLayout移动的距离" + top);
child.setTranslationY(top);
return true;
}
}
然后代码里使用:app:layout_behavior="全路径"
就可以实现了,因为CoordinatorLayout里面的内容怕你不知道,所以这里我还是贴一下相应的清单文件,要不你又该把实现不了的锅让我背了,这个锅我不背。。。
<?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.jinlong.newmaterialdesign.behavior.TwoBehaviorActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:title="底部联动的Behavior"
app:titleTextColor="@android:color/white" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#009988"
android:gravity="center"
android:text="标签1" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#002288"
android:gravity="center"
android:text="标签2" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#009922"
android:gravity="center"
android:text="标签3" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#00aa88"
android:gravity="center"
android:text="标签4" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#999988"
android:gravity="center"
android:text="标签5" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<TextView
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:background="@color/colorPrimary"
android:gravity="center"
android:text="这个一个底栏"
android:textColor="@android:color/white"
app:layout_behavior="com.jinlong.newmaterialdesign.behavior.TwoBehavior" />
</android.support.design.widget.CoordinatorLayout>
这里面处理View的变化才是难点,反正我是这么认为的,不懂的同学可以补充一下相应的知识,什么View变化,获取相应位置的方法等些许内容。网上还是挺多的,依赖的View基本上都是这么实现的。都是实现这两个方法的,玩转了就好了,多写写自然就熟了。
4.2 依赖于滚动变化而变化的Behavior
这个就比较难了,因为涉及到相应的滚动计算什么的,只有多写多看才能熟,先来一个简单的例子吧。剩下的就靠大家多多练习了!!!
4.2.1 首先是对位置的确定
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
//设置了behavior的布局
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
if(params!=null && params.height == CoordinatorLayout.LayoutParams.MATCH_PARENT){
child.layout(0,0,parent.getWidth(),parent.getHeight());
child.setTranslationY(getHeaderHeight());
return true;
}
return super.onLayoutChild(parent, child, layoutDirection);
}
/**
* 这里是Header的高度,可以设置成任何你想的高度
*/
public int getHeaderHeight(){
// 当你设置到相应的清单文件的时候,你就这么弄
// return Context.getResources().getDimensionPixelOffset(R.dimen.header_height);
return 500;
}
这里的位置确定主要用到了一个View.setTranslationY()的方法,这个方法我查了查,和View.getTop()的方法有些类似,是相对于父控件左上角的偏移量。那么就很好理解了,设置Behavior的View便宜到指定的位置下面,因为这里设置了一个相应的图片高度,所以这里就是在图片的下面。
4.2.2 处理相应的滚动事件(到难点了。这里要好好看哦)
- 首先处理相应的滚动方向,因为这里处理的滚动方向为竖直方向,所以代码是这个样子滴!
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
//如果是竖直移动的话才能有后面的响应的事件
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
}
- 处理滚动距离等一系列的计算
首先点你手指开始滑动的时候,会执行下面这个方法。
@Override
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
// 在这个方法里面只处理向上滑动
if(dy < 0){
return;
}
//计算每次移动的距离
float transY = child.getTranslationY() - dy;
if(transY > 0){
child.setTranslationY(transY);
consumed[1]= dy;
}
}
说明一下:dy<0 代表的是向下滑动。child.getTranslationY()获取的是设置behavior的View距离CoordinatorLayout顶部的偏移量。dy代表的是每一次移动的距离。所以transY计算的就是每一次移动后应该距离顶部的距离设置给相应的View。这里consumed[1]= dy表示的是你处理的距离(后面会用到的)。所以就不难理解了,当你每次向上滑动的时候,会计算相应的数值,设置给child,使它逐渐的向上移动,当到达顶部之后就不进行改变相应的竖直了。就酱紫了...
每次上面的方法执行完成的时候会调用下面这个方法
@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
// 在这个方法里只处理向下滑动
if(dyUnconsumed >0){
return;
}
float transY = child.getTranslationY() - dyUnconsumed;
Log.i(TAG,"------>transY:"+transY+"****** child.getTranslationY():"+child.getTranslationY()+"--->dyUnconsumed"+dyUnconsumed);
if(transY > 0 && transY < getHeaderHeight()){
child.setTranslationY(transY);
}
}
还记的我上面说的那个老大的问题吧?当你consumed[1]= dy的时候,就会传递过来相应的参数dxUnconsumed/dyUnconsumed代表处理剩下的参数,但是这里要注意一点。针对于上面这个例子当你上一个方法return的时候,那么这个dxUnconsumed/dyUnconsumed就会有值的!那就不难理解了,当你向下滑动的时候会之间改变child的位置,直到child全部显示出来为止!
4.2.3 全部的内容是酱紫的
public class OneBehavior extends CoordinatorLayout.Behavior {
private String TAG = OneBehavior.class.getSimpleName();
public OneBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) {
super.onStopNestedScroll(coordinatorLayout, child, target, type);
}
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
//如果是水平移动的话响应响应的事件
return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
}
@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
// 在这个方法里只处理向下滑动
if(dyUnconsumed >0){
return;
}
float transY = child.getTranslationY() - dyUnconsumed;
Log.i(TAG,"------>transY:"+transY+"****** child.getTranslationY():"+child.getTranslationY()+"--->dyUnconsumed"+dyUnconsumed);
if(transY > 0 && transY < getHeaderHeight()){
child.setTranslationY(transY);
}
}
@Override
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
// 在这个方法里面只处理向上滑动
if(dy < 0){
return;
}
float transY = child.getTranslationY() - dy;
Log.i(TAG,"transY:"+transY+"++++child.getTranslationY():"+child.getTranslationY()+"---->dy:"+dy);
if(transY > 0){
child.setTranslationY(transY);
consumed[1]= dy;
}
}
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
//设置了behavior的布局
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
if(params!=null && params.height == CoordinatorLayout.LayoutParams.MATCH_PARENT){
child.layout(0,0,parent.getWidth(),parent.getHeight());
child.setTranslationY(getHeaderHeight());
return true;
}
return super.onLayoutChild(parent, child, layoutDirection);
}
/**
* 这里是Header的高度,可以设置成任何你想的高度
*/
public int getHeaderHeight(){
// 当你设置到相应的清单文件的时候,你就这么弄
// return Context.getResources().getDimensionPixelOffset(R.dimen.header_height);
return 500;
}
}
4.2.3 布局的内容
布局的内容就简单多了。直接引入一个behavior就可以了。有一个问题注意下,因为上面面我是写死的高度,所以布局里面就是一个写死的高度了。如果你想做好适配的话,就直接从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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="500px"
android:scaleType="centerCrop"
android:src="@mipmap/heard_1" />
<!--这是一张图片-->
<android.support.v4.widget.NestedScrollView
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:layout_behavior="com.jinlong.newmaterialdesign.behavior.OneBehavior">
<!--这是是全路径-->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/large_text" />
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
基本上就这么多了,其实我在算法上面真的很渣、很渣一个很渣已经不能形容我了。我已经很努力的给你们讲明白了。不懂的可以给我留言,其实自定义Behavior真的是看见一个你练一个,慢慢的你就能开车了!其实我也是看了别人分享的东西之后总结的。有什么不好的地方还请多多指教!关于Behavior原理感兴趣的同学可以看看HelloCsld的这篇文章讲的也是挺透彻的!希望今天讲的这些能帮到你!今天就到这里吧!拜拜。。。
代码在gitHub上的地址感兴趣的同学可以去看看!!!