Android之嵌套联动<四>:自定义Behavio
在前两篇文章中,都使用了默认的
Behavior
,那么默认的Behavior
是什么呢?
首先看一下布局代码:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:background="#cccccc">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimaryDark"
app:title="CollapsingToolbarLayout演示"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:collapsedTitleGravity="center"
app:expandedTitleGravity="bottom"
app:scrimAnimationDuration="500"
app:toolbarId="@+id/toolbar">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/che4"
android:visibility="visible" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@android:color/transparent"
app:title="我是Toolbar"
app:navigationIcon="@mipmap/back"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
在RecyclerView标签中有一个配置
app:layout_behavior="@string/appbar_scrolling_view_behavior"
该string的值为:
<string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>
可以发现,默认的Behavior
其实就是AppBarLayout
控件的内部类ScrollingViewBehavior
,ScrollingViewBehavior
也有父类,它的顶级父类是CoordinatorLayout.Behavior
,本章将详细说明抽象类CoordinatorLayout.Behavior
,其源码如下:
public abstract static class Behavior<V extends View> {
public Behavior() {
}
public Behavior(Context context, AttributeSet attrs) {
}
public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
}
public void onDetachedFromLayoutParams() {
}
public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) {
return false;
}
public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) {
return false;
}
@ColorInt
public int getScrimColor(@NonNull CoordinatorLayout parent, @NonNull V child) {
return -16777216;
}
@FloatRange(
from = 0.0D,
to = 1.0D
)
public float getScrimOpacity(@NonNull CoordinatorLayout parent, @NonNull V child) {
return 0.0F;
}
public boolean blocksInteractionBelow(@NonNull CoordinatorLayout parent, @NonNull V child) {
return this.getScrimOpacity(parent, child) > 0.0F;
}
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) {
return false;
}
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) {
return false;
}
public void onDependentViewRemoved(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) {
}
public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
return false;
}
public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) {
return false;
}
public static void setTag(@NonNull View child, @Nullable Object tag) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)child.getLayoutParams();
lp.mBehaviorTag = tag;
}
@Nullable
public static Object getTag(@NonNull View child) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams)child.getLayoutParams();
return lp.mBehaviorTag;
}
/** @deprecated */
@Deprecated
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes) {
return false;
}
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
return type == 0 ? this.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes) : false;
}
/** @deprecated */
@Deprecated
public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes) {
}
public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
if (type == 0) {
this.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes);
}
}
/** @deprecated */
@Deprecated
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target) {
}
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int type) {
if (type == 0) {
this.onStopNestedScroll(coordinatorLayout, child, target);
}
}
/** @deprecated */
@Deprecated
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
}
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
if (type == 0) {
this.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
}
}
/** @deprecated */
@Deprecated
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed) {
}
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
if (type == 0) {
this.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
}
}
public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY, boolean consumed) {
return false;
}
public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY) {
return false;
}
@NonNull
public WindowInsetsCompat onApplyWindowInsets(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull WindowInsetsCompat insets) {
return insets;
}
public boolean onRequestChildRectangleOnScreen(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull Rect rectangle, boolean immediate) {
return false;
}
public void onRestoreInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Parcelable state) {
}
@Nullable
public Parcelable onSaveInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child) {
return BaseSavedState.EMPTY_STATE;
}
public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Rect rect) {
return false;
}
}
由于自定义Behavior,总是直接或间接集成CoordinatorLayout的内部类Behavior,所以CoordinatorLayout+Behavior就可以实现嵌套联动
。
(1)Behavior的基本构造方法
当自定义Behavior时,必须写全两个基本构造方法,否则Behavior的初始化可能会失败,代码如下:
public class MyCustomBehavior extends CoordinatorLayout.Behavior {
public MyCustomBehavior() {
}
public MyCustomBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
(2)整理下布局
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:background="#cccccc">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:background="@mipmap/che4"
android:visibility="visible"
app:layout_behavior="@string/mycustombehavior"/>
<Button
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="请让我上下移动"
android:layout_marginTop="400dp"
android:textSize="20sp"
android:background="#00ffff"
android:gravity="center" />
</android.support.design.widget.CoordinatorLayout>
其中,mycustombehavior
已在string资源文件中配置好值了
<resources>
<string name="mycustombehavior">com.xxx.ooo.recycleview.MyCustomBehavior</string>
</resources>
当然,也可以直接应用对应的类
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:background="@mipmap/che4"
android:visibility="visible"
app:layout_behavior=".MyCustomBehavior"/>
(3)layoutDependsOn和onDependentViewChanged实现简单联动
- layoutDependsOn:确定使用Behavior的View要依赖的View的类型
- onDependentViewChanged:当被依赖的View状态改变时回调
实现简单联动需要满足几个前提:
[第一]
Behavior必须在CoordinatorLayout下才能生效
[第二]
必须确定Behavior的View要依赖的View的类型(layoutDependsOn)
[第三]
被依赖的View状态必须不停的改变才能使联动生效。
本例中,Button是依赖View,ImageView配置layout_behavior与Button形成依赖关系,通过触摸移动来保证Button的状态不停的改变
findViewById(R.id.textview).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
//v.setX(event.getRawX()-v.getWidth()/2);
v.setY(event.getRawY()-v.getHeight()/2);
break;
}
return false;
}
});
MyCustomBehavior
代码如下:
public class MyCustomBehavior extends CoordinatorLayout.Behavior {
private float buttonY;
public MyCustomBehavior() {
}
public MyCustomBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof Button;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (buttonY == 0) {
//按钮的初始高度
buttonY = dependency.getY();
}
child.setTranslationY(dependency.getY() - buttonY);
return true;
}
}
效果如下:
134.gif以上效果不是很友好,接下来的例子中,将结合AppBarLayout+CollapsingToolbarLayout再演示一遍效果
135.gif代码如下:
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity"
android:background="#cccccc">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentScrim="#00ffffff"
app:title="CollapsingToolbarLayout演示"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:collapsedTitleGravity="center"
app:expandedTitleGravity="bottom">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/che4"
android:visibility="visible" />
<!--<android.support.v7.widget.Toolbar-->
<!--android:id="@+id/toolbar"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="?attr/actionBarSize"-->
<!--android:background="@android:color/transparent"-->
<!--app:title="我是Toolbar"-->
<!--app:navigationIcon="@mipmap/back"-->
<!--app:layout_collapseMode="parallax"-->
<!--app:layout_collapseParallaxMultiplier="0.7" />-->
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="visible"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<TextView
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#E66C6C"
android:text="我是一个文本"
android:gravity="center"
android:textColor="#ffffff"
android:textSize="20sp"
app:layout_behavior=".MyCustomBehavior"/>
</android.support.design.widget.CoordinatorLayout>
public class MyCustomBehavior extends CoordinatorLayout.Behavior {
private float deltaY;
public MyCustomBehavior() {
}
public MyCustomBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof RecyclerView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (deltaY == 0) {
deltaY = dependency.getY() - child.getHeight();
}
float dy = dependency.getY() - child.getHeight();
dy = dy < 0 ? 0 : dy;
float alpha = 1 - (dy / deltaY);
child.setAlpha(alpha);
return true;
}
}
[本章完...]