目錄
1.事件分發(fā)介紹
2.Down雁刷、up事件的分發(fā)過(guò)程
3.onTouchListener士骤、onClickListener調(diào)用時(shí)機(jī)
4.事件攔截應(yīng)用
5.NestedScrollingParent
6.Behavior的使用
7.NestedScrollingChild接口來(lái)源
NestedScrollingParent相關(guān)接口雖然能實(shí)現(xiàn)滑動(dòng)的效果割捅,但是每次我們都需要自定義View并實(shí)現(xiàn)相關(guān)接口酸纲,還是有些麻煩组哩,因此Android又給出了Behavior供我們使用咖楣。使用時(shí)只需要先實(shí)現(xiàn)Behavior類(用于滑動(dòng)事件的消費(fèi))督笆,然后在需要消耗的布局文件中聲名,或者在類名前使用注解進(jìn)行標(biāo)注诱贿,就可實(shí)現(xiàn)娃肿,比之前的自定義View看起來(lái)更加簡(jiǎn)單,耦合更小珠十,但是前提是你只能CoordinatorLayout中使用咸作,下面就介紹下Behavior的使用及原理。
大家最熟悉的behavior使用場(chǎng)景應(yīng)該是下面的
<android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout>
<ImageView />
</android.support.design.widget.CollapsingToolbarLayout>
<TextView />
</android.support.design.widget.AppBarLayout>
<com.example.irecyclerlib.IRecyclerView
app:layout_behavior="@stringappbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
但是Behavior到底是怎么起作用的呢宵睦,以及他都能實(shí)現(xiàn)什么樣的功能呢?
下面先介紹下自定義behavior的實(shí)例墅诡,然后分析下它的原理壳嚎。
本節(jié)實(shí)現(xiàn)以下效果:
首先看下Behavior的接口:
該動(dòng)作分為兩步,首先頂部TextView移動(dòng)末早,然后RecyclerView跟隨TextView移動(dòng)烟馅,滑動(dòng)到頂端后只有RecyclerView移動(dòng),下面介紹通過(guò)Behavior來(lái)實(shí)現(xiàn)然磷。
behavior提供了兩種功能:
1.view1監(jiān)聽(tīng)另一個(gè)view2的狀態(tài)變化(大小郑趁、位置、顯示狀態(tài)等),從而使自身也進(jìn)行相應(yīng)變化姿搜,由layoutDependsOn()寡润、onDependentViewChanged()來(lái)實(shí)現(xiàn)該功能。
首先是RecyclerView的位置根據(jù)TextView的位置變化而變化舅柜。
public class RecyclerViewBehavior extends CoordinatorLayout.Behavior<RecyclerView> {
...
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, RecyclerView child, View dependency) {
//所以來(lái)的對(duì)象是TextView
return dependency instanceof TextView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
//計(jì)算列表y坐標(biāo)梭纹,最小為0
float y = dependency.getHeight() + dependency.getTranslationY();
if (y < 0) {
y = 0;
}
child.setY(y);//recyclerView位置進(jìn)行移動(dòng)
return true;
}
}
2.實(shí)現(xiàn)onNestedScroll()等接口實(shí)現(xiàn)事件的傳遞,跟我們之前講的接口NestedScrollingParent接口相似致份。
public class SampleHeaderBehavior extends CoordinatorLayout.Behavior<TextView> {
...
@Override
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull TextView textView, @NonNull View recyclerView, int dx, int dy,
@NonNull int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, textView, recyclerView, dx, dy, consumed, type);
if (recyclerView instanceof RecyclerView) {
//recyclerView是否可以滑動(dòng)
if(canScroll(textView,dy,recyclerView)){
float finalY = textView.getTranslationY() - dy;//dy為移動(dòng)分量
Log.d(TAG, "onNestedPreScroll: can finalY = "+finalY);
if (dy < -textView.getHeight()) {
dy = -textView.getHeight();
} else if (dy > textView.getHeight()) {
dy = textView.getHeight();
}
textView.setTranslationY(finalY);
consumed[1] = dy;
}
}
}
private boolean canScroll(View child ,int dy,View recyclerView){
if(dy > 0){
//上滑
Log.d(TAG, "canScroll->up:dy = "+dy+" getY = "+recyclerView.getY());
if(recyclerView.getY() > 0){
return true;
}else {
return false;
}
}else {
//下滑
Log.d(TAG, "canScroll->down:dy = "+dy+" getY = "+recyclerView.getY());
if(recyclerView.getY() < child.getHeight()){
return true;
}else {
return false;
}
}
}
}
1.下面介紹下layoutDependsOn實(shí)現(xiàn)原理:
他的原理很簡(jiǎn)單变抽,就是在layoutDependsOn中設(shè)置依賴的對(duì)象(這里是TextView),然后TextView移動(dòng)時(shí)氮块,在onDependentViewChanged方法中移動(dòng)child recyclerView绍载。但是兩個(gè)方法都是在什么地方調(diào)用的呢?
(1)首先是layoutDependsOn方法的調(diào)用
該方法在CoordinatorLayout的onMeasure方法中調(diào)用滔蝉,然后存儲(chǔ)依賴關(guān)系mDependencySortedChildren击儡,供onDependentViewChanged使用
private void prepareChildren() {
...
for (int i = 0, count = getChildCount(); i < count; i++) {
final View view = getChildAt(i);
final LayoutParams lp = getResolvedLayoutParams(view);
lp.findAnchorView(this, view);
mChildDag.addNode(view);
// Now iterate again over the other children, adding any dependencies to the graph
for (int j = 0; j < count; j++) {
if (j == i) {
continue;
}
final View other = getChildAt(j);
//調(diào)用layoutParams的dependsOn方法,下面
if (lp.dependsOn(this, view, other)) {
if (!mChildDag.contains(other)) {
// Make sure that the other node is added
mChildDag.addNode(other);
}
// Now add the dependency to the graph
mChildDag.addEdge(other, view);
}
}
}
// Finally add the sorted graph list to our list
//然后將依賴關(guān)系進(jìn)行存儲(chǔ)
mDependencySortedChildren.addAll(mChildDag.getSortedList());
}
boolean dependsOn(CoordinatorLayout parent, View child, View dependency) {
//調(diào)用behavior的layoutDependsOn方法
return dependency == mAnchorDirectChild
|| shouldDodge(dependency, ViewCompat.getLayoutDirection(parent))
|| (mBehavior != null && mBehavior.layoutDependsOn(parent, child, dependency));
}
(2)onDependentViewChanged()的調(diào)用
該方法是在CoordinatorLayout類的onLayout方法中調(diào)用锰提。
2.behavior的onNestedPreScroll()方法的調(diào)用
recyclerView滑動(dòng)時(shí)曙痘,首先調(diào)用NestedScrollingChild2接口芳悲,通過(guò)NestedScrollingChildHelper傳遞到CoordinatorLayout的NestedScrollingParent2接口的onNestedPreScroll()方法,在該方法中再去調(diào)用behavior的onNestedPreScroll方法边坤,由此最終將事件傳遞到behavior中進(jìn)行消費(fèi)名扛。可以看出使用behavior也是在NestedScrollingParent茧痒、NestedScrollingChild接口的基礎(chǔ)上多了一層調(diào)用肮韧,將滑動(dòng)事件委托給behavior處理。
如果CoordinatorLayout與Behavior之間有其他布局旺订,還能實(shí)現(xiàn)效果么弄企?
從圖上看出,CoordinatorLayout只會(huì)尋找他的一層子布局区拳,不會(huì)再去深入獲取拘领,因此behavior不能被其他布局包裹。
上面分析完后樱调,下面對(duì)CoordinatorLayout的經(jīng)典組合進(jìn)行簡(jiǎn)單分析調(diào)用過(guò)程:
1.NestedScrollView滑動(dòng)時(shí)將Move事件傳遞給CoordinatorLayout约素,CoordinatorLayout將其傳遞給AppBarLayout的Behavior,調(diào)用其onNestedPreScroll方法進(jìn)行AppBar的滑動(dòng)笆凌。
2.AppBar滑動(dòng)時(shí)圣猎,CoordinatorLayout調(diào)用其onLayout(),onLayout時(shí)獲取view之間的依賴關(guān)系,然后將移動(dòng)距離傳遞給ScrollingViewBehavior的onDependentViewChanged()乞而,實(shí)現(xiàn)NestedScrollView跟隨AppBar的移動(dòng)送悔。
代碼詳見(jiàn) https://github.com/yanglele/AndroidSample/blob/master/app/src/main/java/com/example/yangl/androidsample/touchEvent/myCoordinatorLayout/MyCoordinatorActivity.java