第一ToolBar
这个就省略了,网上关于他的资料太多了
第二AppbarLayout
MD中,有很多控件已经封装了Behavior,它的behavior已经写好了,比如AppBarLayout已经封装了behavior,只需要通过setScollFlags()传入的参数,就可以控制它里面的behavior到底执行什么样的动作。
<?xml version="1.0" encoding="utf-8"?>
<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/appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="300dp"
android:minHeight="50dp"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
app:title="首页"
android:gravity="bottom"
android:background="@color/colorAccent"
/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
滚动ScrollView的时候,toolBar也会跟着做出改变,我们看下这个属性的一些参数的含义
app:layout_scrollFlags=“scroll|enterAlways|enterAlwaysCollapsed”
scroll:AppbarLayout中的内容(本例子中的toolbar)也是ScrollView的一部分,随着NestScrollView的滚动而滚动。就好比AppbarLayout中的内容长在了ScrollView的头部。
scroll|enterAlways:注意,后一个属性依据前一个属性。如果只有后一个属性,而没有前一个属性那这个属性不会起任何作用。 这两个属性我们会发现向下滑动的时候跟scroll没有任何区别,但向下滑动的时候我们会发现,只要你向下滑动,AppbarLayout中的内容就会滑出来
scroll|enterAlways|enterAlwaysCollapsed:这个属性要设置最小高度。上滑的时候没有区别,但是向下滑动的时候会先滑出来最小高度。当滑动到劲头的时候,再把剩余的滑动出来。
scroll|exitUntilCollapsed:向下滑动的时候与scroll没什么区别,向上滑动的时候会先滑到最小高度,直到ScrollView滑动完之后,最小高度也保留在屏幕上。最小高度永远不会滑出屏幕。
scroll|snap:向下滑动的时候,如果滑动的隐藏范围大于显示范围会出现回弹的效果。
AppbarLayout的监听事件
appbar_layout = findViewById(R.id.appbar_layout);
//当AppbarLayout 的偏移发生改变的时候回调,也就是子View滑动
appbar_layout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
}
});
//返回子View的可滑动距离
appbar_layout.getTotalScrollRange();
//移除偏移监听器
appbar_layout.removeOnOffsetChangedListener(null);
三CollapsingToolbarLayout
可折叠的Toolbar
看下布局
<?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">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapse_layout"
android:layout_width="match_parent"
android:layout_height="250dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@mipmap/timg"
app:layout_collapseMode="parallax"
/>
<android.support.v7.widget.Toolbar
android:id="@+id/appbar_layout_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="AppbarLayout"
app:titleTextColor="@android:color/white"
app:navigationIcon="@mipmap/more"
app:layout_collapseMode="pin"
/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="222"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="333"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="444"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="555"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="666"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="777"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
看下代码
package com.dongnao.dn_vip_ui_15_2;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import com.dongnao.dn_vip_ui_15_2.utils.StatusBarUtils;
/**
* 折叠控件
*/
public class CollapsingToolbarLayoutActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collapsing_toolbar);
initView();
}
private void initView(){
final Toolbar toolbar = (Toolbar) findViewById(R.id.appbar_layout_toolbar);
//设置沉浸式状态栏
StatusBarUtils.setTranslucentImageHeader(this,0,toolbar);
//设置标题颜色
toolbar.setTitleTextColor(Color.TRANSPARENT);
//加载动作菜单
toolbar.inflateMenu(R.menu.layout_toolbar_menu);
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);
final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapse_layout);
collapsingToolbarLayout.setTitle("");
collapsingToolbarLayout.setCollapsedTitleTextColor(getResources().getColor(R.color.white));
collapsingToolbarLayout.setExpandedTitleColor(getResources().getColor(R.color.white));
collapsingToolbarLayout.setExpandedTitleColor(Color.TRANSPARENT);
//设置纱布
collapsingToolbarLayout.setContentScrimColor((getResources().getColor(R.color.colorAccent)));
//监听appBarLayout的偏移
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if(Math.abs(verticalOffset) >= appBarLayout.getTotalScrollRange()){
toolbar.setTitleTextColor(getResources().getColor(R.color.white));
collapsingToolbarLayout.setTitle("AppbarLayout");
}else{
collapsingToolbarLayout.setTitle("");
}
}
});
}
}
CollapsingToolbarLayout
CollapsingToolbarLayout是一个折叠的Toolbar。
推荐CoordinatorLayout + AppBarLayout + CollapsingToolbarLayout一起使用
1.Collapsing title–>折叠标题
2.Content scrim–>内容纱布
3.Status bar scrim–>状态栏纱布
4.Parallax scrolling children–>有视差地滚动子View
5.Pinned position children–>固定子View的位置
四自定义的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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BehaviorActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="#00ffffff"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@mipmap/meizhi"
android:fitsSystemWindows="true"
android:scaleType="fitXY"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:id="@+id/scollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="111"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="222"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="333"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="444"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="555"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="666"/>
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="777"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
behaivor常用的方法详解
package com.dongnao.dn_vip_ui_15_2;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.v4.widget.NestedScrollView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* BeHavior常用的方法详解
*/
public class MyBeHavior extends CoordinatorLayout.Behavior {
//列表顶部和textView之间的距离
private float deltaY;
public MyBeHavior(Context context, AttributeSet attributeSet){
super(context,attributeSet);
}
/**
* 表示是否给应用了Behavior 的View 指定一个观察的布局,通常,当观察的View 布局发生变化时
* 不管被观察View 的顺序怎样,被观察的View也会重新布局
* @param parent
* @param child 绑定behavior 的View 观察者
* @param dependency 被观察者的view
* @return 如果child 是观察者观察的View 返回true,否则返回false
*/
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof NestedScrollView;
}
/**
* 当被观察者的View 状态(如:位置、大小)发生变化时,这个方法被调用
* @param parent
* @param child
* @param dependency
* @return
*/
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if(deltaY == 0){
deltaY = dependency.getY() - child.getHeight();
}
//被观察者View的Y坐标 - 观察者的高度 得到的就是两者之间的距离
float dy = dependency.getY() - child.getHeight();
//如果两者之间的距离小于0 就赋值为0 如果两者之间的距离不小于0 就将两者之间的实际距离赋值给它
dy = dy<0?0 : dy;
//计算Y轴每次偏移的距离
float y = -(dy/deltaY) * child.getHeight();
Log.e("BEHAVIOR----->",y+"-------------");
//将编译距离设置给观察者
child.setTranslationY(y);
return false;
}
/**
* 当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明
* coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true
* 的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等)
* 这个方法有个重要的参数nestedScrollAxes,表明处理的滑动的方向。
*
* @param coordinatorLayout 和Behavior 绑定的View的父CoordinatorLayout
* @param child 和Behavior 绑定的View 观察者
* @param directTargetChild
* @param target
* @param nestedScrollAxes 嵌套滑动 应用的滑动方向
* @param type
*
* @return
*/
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int nestedScrollAxes, int type) {
child.setVisibility(View.GONE);
return false;
}
/**
* 嵌套滚动发生之前被调用
* 在nested scroll child 消费掉自己的滚动距离之前,嵌套滚动每次被nested scroll child
* 更新都会调用onNestedPreScroll。注意有个重要的参数consumed,可以修改这个数组表示你消费
* 了多少距离。假设用户滑动了100px,child 做了90px 的位移,你需要把 consumed[1]的值改成90,
* 这样coordinatorLayout就能知道只处理剩下的10px的滚动。
* @param coordinatorLayout
* @param child
* @param target
* @param dx 用户水平方向的滚动距离
* @param dy 用户竖直方向的滚动距离
* @param consumed
*/
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed, int type) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed,type);
}
/**
* 进行嵌套滚动时被调用
* @param coordinatorLayout
* @param child
* @param target
* @param dxConsumed target 已经消费的x方向的距离
* @param dyConsumed target 已经消费的y方向的距离
* @param dxUnconsumed x 方向剩下的滚动距离
* @param dyUnconsumed y 方向剩下的滚动距离
*/
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,type);
}
/**
* 嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。
* @param coordinatorLayout
* @param child
* @param target
*/
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int type) {
super.onStopNestedScroll(coordinatorLayout, child, target,type);
}
/**
* onStartNestedScroll返回true才会触发这个方法,接受滚动处理后回调,可以在这个
* 方法里做一些准备工作,如一些状态的重置等。
* @param coordinatorLayout
* @param child
* @param directTargetChild
* @param target
* @param nestedScrollAxes
*/
@Override
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int nestedScrollAxes,int type) {
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes,type);
}
/**
* 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息
* 决定最终状态,比如滚动Header,是让Header处于展开状态还是折叠状态。返回true 表
* 示消费了fling.
*
* @param coordinatorLayout
* @param child
* @param target
* @param velocityX x 方向的速度
* @param velocityY y 方向的速度
* @return
*/
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY) {
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
//可以重写这个方法对子View 进行重新布局
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
return super.onLayoutChild(parent, child, layoutDirection);
}
/**
* 是否拦截触摸
* @param parent
* @param child
* @param ev
* @return
*/
@Override
public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull MotionEvent ev) {
return super.onInterceptTouchEvent(parent, child, ev);
}
}
嵌套滑动的方法详解
public interface NestedScrollingParent2 extends NestedScrollingParent {
/**
* 这个是嵌套滑动控制事件分发的控制方法,只有返回true才能接收到事件分发
* @param child 包含target的ViewParent的直接子View
* @param target 发起滑动事件的View
* @param axes 滑动的方向,数值和水平方向{@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
* {@link ViewCompat#SCROLL_AXIS_VERTICAL}
* @return true 表示父View接受嵌套滑动监听,否则不接受
*/
boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes,@NestedScrollType int type);
/**
* 这个方法在onStartNestedScroll返回true之后在正式滑动之前回调
* @param child 包含target的父View的直接子View
* @param target 发起嵌套滑动的View
* @param axes 滑动的方向,数值和水平方向{@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
* {@link ViewCompat#SCROLL_AXIS_VERTICAL} or both
*/
void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes,@NestedScrollType int type);
/**
*
* @param target View that initiated the nested scroll
*/
void onStopNestedScroll(@NonNull View target);
/**
* 在子View滑动过程中会分发这个嵌套滑动的方法,要想这里收到嵌套滑动事件必须在onStartNestedScroll返回true
* @param dxConsumed 子View在水平方向已经消耗的距离
* @param dyConsumed 子View在垂直方法已经消耗的距离
* @param dxUnconsumed 子View在水平方向剩下的未消耗的距离
* @param dyUnconsumed 子View在垂直方法剩下的未消耗的距离
* @param type 发起嵌套事件的类型 分为触摸(ViewParent.TYPE_TOUCH)和非触摸(ViewParent.TYPE_NON_TOUCH)
*/
void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type);
/**
* 在子View开始滑动之前让父View有机会先进行滑动处理
* @param dx 水平方向将要滑动的距离
* @param dy 竖直方向将要滑动的距离
* @param consumed Output. 父View在水平和垂直方向要消费的距离,consumed[0]表示水平方向的消耗,consumed[1]表示垂直方向的消耗,
*/
void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
@NestedScrollType int type);
}
public interface NestedScrollingChild2 extends NestedScrollingChild {
//返回值true表示找到了嵌套交互的ViewParent,type表示引起滑动事件的类型,这个事件和parent中的onStartNestedScroll是对应的
boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type);
//停止嵌套滑动的回调
void stopNestedScroll(@NestedScrollType int type);
//表示有实现了NestedScrollingParent2接口的父类
boolean hasNestedScrollingParent(@NestedScrollType int type);
//分发嵌套滑动事件的过程
boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow,
@NestedScrollType int type);
//在嵌套滑动之前分发事件
boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
@Nullable int[] offsetInWindow, @NestedScrollType int type);
}
Android嵌套滑动讲解简书:https://www.jianshu.com/p/f4763bf8f9ba
方法总结
startNestedScroll : 起始方法, 主要作用是找到接收滑动距离信息的外控件.
dispatchNestedPreScroll : 在内控件处理滑动前把滑动信息分发给外控件.
dispatchNestedScroll : 在内控件处理完滑动后把剩下的滑动距离信息分发给外控件.
stopNestedScroll : 结束方法, 主要作用就是清空嵌套滑动的相关状态
setNestedScrollingEnabled和isNestedScrollingEnabled : 一对get&set方法, 用来判断控件是否支持嵌套滑动.
dispatchNestedPreFling和dispatchNestedFling : 跟Scroll的对应方法作用类似
NestedScrollingParent
on
StartNestedScroll : 对应startNestedScroll, 内控件通过调用外控件的这个方法来确定外控件是否接收滑动信息.
onNestedScrollAccepted : 当外控件确定接收滑动信息后该方法被回调, 可以让外控件针对嵌套滑动做一些前期工作.
onNestedPreScroll : 关键方法, 接收内控件处理滑动前的滑动距离信息, 在这里外控件可以优先响应滑动操作, 消耗部分或者全部滑动距离.
onNestedScroll : 关键方法, 接收内控件处理完滑动后的滑动距离信息, 在这里外控件可以选择是否处理剩余的滑动距离.
onStopNestedScroll : 对应stopNestedScroll, 用来做一些收尾工作.
onNestedPreFling和onNestedFling : 同上略