CoordinatorLayout
简介
CoordinatorLayout是 com.android.support:design 包下用来协调子视图之间位置和动作的布局,遵循Material Design风格。CoordinatorLayout是一个增强版的FrameLayout,他拓展了Framelayout的功能,使得子视图之间可以更好地进行协调和交互。通过给Coordintorlayout的子视图指定Behavior,就可以实现它们之间的交互行为,这也是Coordinator的主要作用。
与AppBarLayout的联合使用
AppBarLayout继承于Linearlayout,方向垂直。AppBarLayout使用app:layout_scrollFlags属性设置子控件的滚动方式。
在XMl中设置scrollFlags的值:
app:layout_scrollFlags="scroll"
在java代码中设置scrollFlags的值
TextView text= ... //确保该View是被AppBarLayout的子视图
AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) text.getLayoutParams();
params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
先使用控件调用getLayoutParams方法。在调用setScrollFlags方法设置。
scrollFlags的可选值
-
scroll(SCROLL_FLAG_SCROLL):使用scroll可以让AppBarLayout跟随着向上滚动,直到AppBarLayout完全隐藏,再开始滚动ScrollView。向下滚动时先将ScrollView滚动到最顶端,再开始显示AppBarLayout。必须设置此标识才能使其他的任何标识生效。如果此时图之前的任何视图没有此标志,则此值无效。
-
enterAlways(SCROLL_FLAG_ENTER_ALWAYS):与scroll类似,向下滚动时一起滚动先将AppBarLayout完全显示,再滚动ScrollView。
-
enterAlwaysCollapsed(SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED):需要和enterAlways一起使用。向下滚动时,先将AppBarLayout滚动到最小高度,再滚动ScrollView,最后再滚动AppBarLayout直到完全显示。使用时要注意定义最小高度:android:minHeight="10dp"。
-
exitUntilCollapsed(SCROLL_FLAG_EXIT_UNTIL_COLLAPSED):向上滚动时,AppBarLayout先向上滚动直到最小高度,然后ScrollView开始滚动。AppBarLayout不会完全退出屏幕,保留一部分。
-
snap(SCROLL_FLAG_SNAP):在滚动结束时,如果视图仅部分可见,则他将自动滚到最近的边。滚动超过一半改变状态,完全消失或者完全显示。
先看一下布局的代码:
<androidx.coordinatorlayout.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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="CoordinatorLayout"
app:subtitle="AppBarLayout"
android:background="#636363"
app:titleTextColor="@color/white"
app:subtitleTextColor="@color/white"
app:layout_scrollFlags="scroll|enterAlways"
/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
<!-- app:layout_behavior="@string/appbar_scrolling_view_behavior"-->
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
运行效果:
可以看到AppBarLayout的子控件的滚动效果已经出现了,但是RecyclerView与AppBarLayout的内容有一些重叠。毕竟CoordinatorLayout也是一种FrameLayout,都是默认再布局的左上方。
为了布局协调,我们可以给Recycler加一个behavior。(上面的代码注释掉的)
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
这是一个android自带的behavior。它可以让实现NestedScrollingChild接口的子View与AppBarlayout联动,约束了子View与AppBarlayout的相对位置和滚动交互。RecyclerView就是实现了nestedScrollingChild接口的控件。
再看看效果:
关于CoordinatorLayout与Behavior的工作原理后面会简单介绍。
与CollapsingToolbarLayout的联合使用
CollapsingToolbarLayout是折叠的状态栏布局,对Toolbar进行包装。不能单独使用,要作为AppBarlayout的子View,也会用Toolbar作为它的子布局。
他也是一种FrameLayout。
常用属性:
app:titleEnabled="true" //是否显示标题
app:title="CollapsingToolbarLayout" //标题内容
app:expandedTitleGravity="left|bottom" //扩展后Title的显示位置
app:collapsedTitleGravity="left" //收缩后Title的显示位置
app:contentScrim ="@color/colorPrimary" //CollapsingToolbarLayout收缩后Toolbar的背景颜色
app:scrimAnimationDuration="1200" //CollapsingToolbarLayout收缩时颜色变化的持续时间
app:scrimVisibleHeightTrigger="150dp" //颜色从可见高度的什么位置开始变化
app:statusBarScrim="@color/colorAccent" //状态颜色变化(Android 5.0)
app:layout_scrollFlags="scroll|exitUntilCollapsed" //设置滑动组件与手势之间的关系
layout_collapseMode属性的可选值有三个。
none:默认值,视图不会折叠或收缩;
pin:当折叠空间折叠时,该View会固定在顶部或者底部,不会跟随页面折叠;
parallax:当可折叠空间折叠时,该View会随着页面的滚动而滚动,此时可以结合另一个属性layout_collapseParallaxMultiplier形成视差滚动的效果,即在滚动时会略微显示或隐藏一部分。
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="220dp"
android:background="@drawable/img"
app:contentScrim="@color/black" //收缩后的背景颜色
app:expandedTitleGravity="start|bottom" //扩展后的标题位置
app:collapsedTitleGravity="center" //收缩后的标题位置
app:expandedTitleTextColor="@color/white" //扩展后的标题的文本颜色
app:collapsedTitleTextColor="@color/white" //收缩后的标题的文本颜色
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" //滚动标志
>
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="CoordinatorLayout"
app:subtitle="AppBarLayout"
app:titleTextColor="@color/white"
app:subtitleTextColor="@color/white"
app:layout_collapseMode="none" //收缩模式
/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
用这段布局代码观察三种可选值。
none
因为Toolbar设置的收缩模式时none,表示不会收缩。所以最终折叠完成后,停留在顶部的是一个完整的Toolbar。
pin
当外面的CollapsingToolbarLayout逐渐收缩,到Toolbar准备收缩时,Toolbar会直接出现在顶部,不会跟随着收缩或隐藏。
parallax
这里改写一下代码
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="warp_content"
app:contentScrim="@color/black"
app:expandedTitleGravity="start|bottom"
app:collapsedTitleGravity="center"
app:expandedTitleTextColor="@color/white"
app:collapsedTitleTextColor="@color/white"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
android:minHeight="28dp"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="220dp"
android:src="@drawable/img"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.5"/>
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="CoordinatorLayout"
app:subtitle="AppBarLayout"
app:titleTextColor="@color/white"
app:subtitleTextColor="@color/white"
app:layout_collapseMode="pin"
/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
因为CollapsingToolbarLayout不能指定layout_collapseMode属性,所以使用了一个ImageView来充当背景。将ImageView的收缩模式设置成parallax,然后使用app:layout_collapseParallaxMultiplier属性。
解释:app:layout_collapseParallaxMultiplier属性会定义视图在可折叠空间中滚动时滚动速度的倍数。相对于空间的滚动速度。例如设置为1.0,该视图就会和滚动视图滚动的速度相同。设置为0.5,则该视图的滚动速度就是可折叠空间的滚动速度的一半。
可以观察到这里Toolbar的文本“Coordinatorlayout”上升的会比背景快一点。
这里下面RecyclerView的代码不改变,和AppBarLayout时一样。这里是完整的代码,可以看一下CollpaseingToolbarLayout使用的结构。
<androidx.coordinatorlayout.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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/home_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:contentScrim="@color/black"
app:expandedTitleGravity="start|bottom"
app:collapsedTitleGravity="center"
app:expandedTitleTextColor="@color/white"
app:collapsedTitleTextColor="@color/white"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
android:minHeight="28dp"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="220dp"
android:src="@drawable/img"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.5"/>
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="CoordinatorLayout"
app:subtitle="AppBarLayout"
app:titleTextColor="@color/white"
app:subtitleTextColor="@color/white"
app:layout_collapseMode="pin"
/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
CollapsingToolbarLayout的折叠、展开状态监听
通过调用AppBarLayout的addOnOffsetChangedListener方法监听AppBarLayout的偏移,从而判断CollapsingToolbarLayout的折叠和展开状态。
CoordinatorLayout与Behavior的使用
CoordinatorLayout的使用的核心其实是Behavior。
使用自定义的Behavior实现如下效果:
这个”拖动“是一个Button,它可以自由拖动改变位置,那个图片是随着Button的移动改变位置,水平方向移动与Button相反,竖直方向与Button移动相同。
实现自定义Behavior的使用
首先创建一个类,使它继承CoorinatorLayout.Behavior< T>,这里泛型填入要执行动作的View的子类。然后去重写Behavior的两个方法。
public class MyBehavior extends CoordinatorLayout.Behavior<ImageView> {
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}//重写构造器
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
return super.layoutDependsOn(parent, child, dependency);
}//该方法根据逻辑判断,若返回true表示child依赖于dependency,false表示不依赖。
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
return super.onDependentViewChanged(parent, child, dependency);
}//该方法当dependency发生改变时(位置、宽高等),执行该方法。
// 若该方法返回true表示child的位置或者宽高要发生改变,否则返回false。
}
在这两个重写方法中多次出现了child和dependency两个变量。
解释:child指的是要执行动作的CoordinatorLayout的子View,Dependency指的是child依赖的View。
也可以说denpendency是让child改变的原因,child是随着denpendency改变而改变的View。我们手动改变denpendency的位置和大小来让child进行相应的改变。
可以在onDependentViewChanged方法中自行指定二者移动或缩放的关系。
public class MyBehavior extends CoordinatorLayout.Behavior<ImageView> {
private int width;
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
//获取DisplayMetrics 是度量对象,该对象包含了与显示相关的信息,例如屏幕宽度、高度、密度等。
width = displayMetrics.widthPixels;//获取屏幕宽度的像素值
}//重写构造器
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
//如果dependency(被依赖的View)是Button的实例,说明就是我们所需要的dependency
return dependency instanceof Button;
//如果使用 return dependency.getId() == R.id.*; 去判断,也可以直接具体指定一个控件
}//该方法根据逻辑判断,若返回true表示child依赖于dependency,false表示不依赖。
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull ImageView child, @NonNull View dependency) {
//每次dependency的位置发生改变,都会执行onDependentViewChanged方法
//根据dependency的位置,设置child的位置
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) dependency.getLayoutParams();
float x = width - dependency.getX() - child.getWidth() + layoutParams.getMarginEnd();
float y = dependency.getY();
child.setX(x);
child.setY(y);
return true;
}//该方法当dependency发生改变时(位置、宽高等),执行该方法。
// 若该方法返回true表示child的位置或者宽高要发生改变,否则返回false。
}
之后在xml文件里给作为child的视图添加layout_behavior属性。
<androidx.coordinatorlayout.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=".MainActivity">
<ImageView
android:layout_width="120dp"
android:layout_height="100dp"
android:src="@drawable/img"
android:scaleType="centerCrop"
android:layout_gravity="start|center_vertical"
android:layout_marginStart="30dp"
app:layout_behavior=".MyBehavior"
/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="拖动"
android:layout_marginEnd="30dp"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
之后在Activity中给Button设置触摸监听,让Button可以跟随手指的拖动移动。
public class MainActivity extends AppCompatActivity {
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnTouchListener(((view, event) -> {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
view.setX(event.getRawX()-view.getWidth()/2);
view.setY(event.getRawY()-view.getHeight()/2);
} return true;
}));
}
}
完成了。
学习参考博客:
CoordinatorLayout的使用如此简单_coordinatelayout-CSDN博客
转载:https://blog.csdn.net/m0_74000465/article/details/134633584