先来看看最终的效果~~

本文同步至博主的私人博客wing的地方酒馆

嗯。。一个是头像上移的 另一个是模仿UC浏览器的。

(PД`q。)你不是说!有三款的吗,怎么只有两款!!!!

不要急嘛。。。 说了从简到难,第一款是介绍概念的啦。

关于CoordinatorLayout,以及系统预留ScrollBehavior使用网上以及有很多文章,这里就不阐述了,如果你还不了解,你可以查看[译]掌握CoordinatorLayout

基础概念

其实Behavior就是一个应用于View的观察者模式,一个View跟随者另一个View的变化而变化,或者说一个View监听另一个View。

在Behavior中,被观察View 也就是事件源被称为denpendcy,而观察View,则被称为child。

开始自定义 难度1 Button与TextView的爱恨情仇

首先在布局文件中跟布局设置为CoordinatorLayout,里面放一个Button和一个TextView。

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<TextView
app:layout_behavior=".EasyBehavior"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="观察者View child"
/>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="被观察View dependency"
/>
</android.support.design.widget.CoordinatorLayout>

这里我们在Activity中做一些手脚,让Button动起来(不要在意坐标这些细节)

  @Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_easy_behavior);
findViewById(R.id.btn).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;
}
}); }

此时,Button已经可以跟随手指移动了。

现在去自定义一个Behavior让TextView跟随Button一起动!

创建一个EasyBehavior类,继承于Behavior

public class EasyBehavior extends CoordinatorLayout.Behavior<TextView> {//这里的泛型是child的类型,也就是观察者View
public EasyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
} @Override
public boolean layoutDependsOn(CoordinatorLayout parent, TextView child, View dependency) {
//告知监听的dependency是Button
return dependency instanceof Button;
} @Override
//当 dependency(Button)变化的时候,可以对child(TextView)进行操作
public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child, View dependency) {
child.setX(dependency.getX()+200);
child.setY(dependency.getY()+200);
child.setText(dependency.getX()+","+dependency.getY()); return true;
}
}

注意两个方法

layoutDependsOn() 代表寻找被观察View

onDependentViewChanged() 被观察View变化的时候回调用的方法

在onDependentViewChanged中,我们让TextView跟随Button的移动而移动。代码比较简单,一看就懂。

Tip

必须重写带双参的构造器,因为从xml反射需要调用。

接下来,在xml中,给TextView设置我们的Behavior。

<TextView
app:layout_behavior=".EasyBehavior"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="观察者View child"
/>

运行效果如下:

这样一个最简单的behavior就做好了。

难度 2 仿UC折叠Behavior

这个效果布局嵌套比上一个例子些许复杂,如果看起来吃力,务必去补习CoordinatorLayout!!!!

先定义xml如下:

<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:ignore="RtlHardcoded"
> <android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:elevation="0dp"
> <android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
> <ImageView
android:layout_width="match_parent"
android:layout_height="300dp"
android:scaleType="centerCrop"
android:src="@drawable/bg"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.9"
/> <FrameLayout
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_gravity="bottom|center_horizontal"
android:background="@color/primary"
android:orientation="vertical"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.3"
> </FrameLayout>
</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"
android:scrollbars="none"
app:behavior_overlapTop="30dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior" > <include layout="@layout/layout_main"/>
</android.support.v4.widget.NestedScrollView> <android.support.v7.widget.Toolbar
android:id="@+id/main.toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primaryDark"
app:layout_anchor="@id/frameLayout"
app:theme="@style/ThemeOverlay.AppCompat.Dark"
>
</android.support.v7.widget.Toolbar> <TextView android:id="@+id/tv_title"
android:textColor="#fff"
android:textSize="18sp"
android:gravity="center"
android:text="头条"
app:layout_behavior=".DrawerBehavior"
android:background="@color/primaryDark"
android:layout_width="match_parent"
android:layout_height="50dp"
> </TextView>
</android.support.design.widget.CoordinatorLayout>

有一点值得注意的是,app:layout_anchor=”@id/frameLayout”这个属性,是附着的意思,这里我用作给了toolbar,代表toolbar附着在了frameLayout之上。会跟随frameLayout的scroll而变化Y的值。

思路分析

如何实现折叠呢,下半部分不用管了,AppBarLayout已经帮我们做好了,我们只要标注相应的scrollflags即可,所以,如上的布局,不做任何处理的话,作为标题的TextView是一直显示的,于是只要让TextView跟随Toolbar变化而变化就可以了。 接下来就创建一个Behavior类!

public class DrawerBehavior extends CoordinatorLayout.Behavior<TextView> {
private int mFrameMaxHeight = 100;
private int mStartY;
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, TextView child, View dependency) {
return dependency instanceof Toolbar;
} public DrawerBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
} @Override public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child,
View dependency) { }
}

现在你应该可以很轻易的看懂这个Behavior的结构了。我们主要大展身手的地方其实是在onDependentViewChanged方法。

@Override public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child,
View dependency) {
//记录开始的Y坐标 也就是toolbar起始Y坐标
if(mStartY == 0) {
mStartY = (int) dependency.getY();
} //计算toolbar从开始移动到最后的百分比
float percent = dependency.getY()/mStartY; //改变child的坐标(从消失,到可见)
child.setY(child.getHeight()*(1-percent) - child.getHeight());
return true;
}

里面监听了Toolbar的Y坐标变化,然后让TextView的Y坐标也跟着变化。达到如预览图效果。

难度3 头像缩放效果

相信有了以上两个例子,这个效果对你来说不难了,不就是让imageView监听Toolbar然后跟随Toolbar的唯一变化而进行位移以及缩放么。

所以具体的解析就不说了,直接上个Behavior代码

/泛型为child类型
public class CustomBehavior extends CoordinatorLayout.Behavior<CircleImageView> {
private Context mContext;
//头像的最终大小
private float mCustomFinalHeight; //最终头像的Y
private float mFinalAvatarY; private float mStartAvatarY; private float mStartAvatarX; private int mAvatarMaxHeight; private BounceInterpolator interpolator = new BounceInterpolator(); public CustomBehavior(Context context, AttributeSet attrs) {
mContext = context;
if (attrs != null) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomBehavior);
//获取缩小以后的大小
mCustomFinalHeight = a.getDimension(R.styleable.CustomBehavior_finalHeight, 0);
a.recycle();
}
} // 如果dependency为Toolbar
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, CircleImageView child, View dependency) {
return dependency instanceof Toolbar;
} //当dependency变化的时候调用
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, CircleImageView child, View dependency) {
//初始化属性
//init(child, dependency);
mFinalAvatarY = dependency.getHeight()/2;
if(mStartAvatarY == 0){
mStartAvatarY = dependency.getY();
}
if(mStartAvatarX == 0){
mStartAvatarX = child.getX();
} if(mAvatarMaxHeight == 0){
mAvatarMaxHeight = child.getHeight();
} //child.setY(dependency.getY()); //让ImageView跟随toolbar垂直移动 child.setY(dependency.getY()+dependency.getHeight()/2-mCustomFinalHeight/2); float percent = dependency.getY() / mStartAvatarY; //float x = mStartAvatarX*(1+percent);
float x = mStartAvatarX * (1+ interpolator.getInterpolation(percent)); //Log.e("wing","started x "+ mStartAvatarX + " currentX "+ x); //当toolbar 达到了位置,就不改变了。
if(dependency.getY() > dependency.getHeight()/2) {
child.setX(x);
}else {
child.setX(mStartAvatarX + ((mAvatarMaxHeight-mCustomFinalHeight))/2);
} CoordinatorLayout.LayoutParams layoutParams =
(CoordinatorLayout.LayoutParams) child.getLayoutParams();
layoutParams.height = (int) ((mAvatarMaxHeight-mCustomFinalHeight) * percent + mCustomFinalHeight);
layoutParams.width = (int) ((mAvatarMaxHeight-mCustomFinalHeight) * percent + mCustomFinalHeight);
child.setLayoutParams(layoutParams); return true;
} }

还是说说坐标计算相关的吧。举个例子。如何让ImageView处于Toolbar中心呢,我的代码如下

 //让ImageView跟随toolbar垂直移动

        child.setY(dependency.getY()+dependency.getHeight()/2-mCustomFinalHeight/2);

为什么是这样? 上个图就明白了

CoordinatorLayout 自定义Behavior并不难,由简到难手把手带你飞的更多相关文章

  1. CoordinatorLayout 自定义Behavior并不难,由简到难手把手带你撸三款!

    先来看看最终的效果~~ 本文同步至博主的私人博客wing的地方酒馆 嗯..一个是头像上移的 另一个是模仿UC浏览器的. (PД`q.)你不是说!有三款的吗,怎么只有两款!!!! 不要急嘛... 说了从 ...

  2. (转载)自定义CoordinatorLayout的Behavior(2):实现淘宝和QQ ToolBar透明渐变效果

    自定义CoordinatorLayout的Behavior(2):实现淘宝和QQ ToolBar透明渐变效果 作者 小武站台 关注 2016.02.19 11:34 字数 1244 阅读 3885评论 ...

  3. [置顶] 针对 CoordinatorLayout 及 Behavior 的一次细节较真

    我认真不是为了输赢,我就是认真.– 罗永浩 我一直对 Material Design 很感兴趣,每次在官网上阅读它的相关文档时,我总会有更进一步的体会.当然,Material Design 并不是仅仅 ...

  4. 一个神奇的控件——Android CoordinatorLayout与Behavior使用指南

    CoordinatorLayout是support.design包中的控件,它可以说是Design库中最重要的控件. 本文通过模仿知乎介绍了自定义Behavior,通过模仿百度地图介绍了BottomS ...

  5. 关于CoordinatorLayout与Behavior的一点分析

    Behavior是Android新出的Design库里新增的布局概念.Behavior只有是CoordinatorLayout的直接子View才有意义.可以为任何View添加一个Behavior.Be ...

  6. 自定义 behavior - 完美仿 QQ 浏览器首页,美团商家详情页

    使用CoordinatorLayout打造各种炫酷的效果 自定义Behavior -- 仿知乎,FloatActionButton隐藏与展示 NestedScrolling 机制深入解析 一步步带你读 ...

  7. CoordinatorLayout自定义Bahavior特效及其源码分析

    @[CoordinatorLayout, Bahavior] CoordinatorLayout是android support design包中可以算是最重要的一个东西,运用它可以做出一些不错的特效 ...

  8. 自定义Behavior 实现Listbox自动滚动到选中项

    原文:自定义Behavior 实现Listbox自动滚动到选中项 blend为我们提供方便的behavior来扩展我们的控件,写好之后就可以在blend中方便的使用了. 下面是自定义的behavior ...

  9. 手把手带你做一个超炫酷loading成功动画view Android自定义view

    写在前面: 本篇可能是手把手自定义view系列最后一篇了,实际上我也是一周前才开始真正接触自定义view,通过这一周的练习,基本上已经熟练自定义view,能够应对一般的view需要,那么就以本篇来结尾 ...

随机推荐

  1. text-indent:-9999px 字体隐藏问题

    为什么要字体隐藏? 通常为了传达更好的视觉效果,我们常用图片替代掉字体.但是为了html语义化,常常要给内容模块加上一些标题来让页面更有意义,在抛开css裸奔的情况下也能很顺利的汲取到页面信息.为此我 ...

  2. 四种读写方案IO流 (JAVA)

    File类用于访问文件或目录的属性 流:指一连串流动的字符,是以先进先出的方式发送信息的通道.程序和数据源之间是通过流联系起来的. 第一套:字节流读取写入方案 FileInputStream :字节流 ...

  3. Odoo 9 PDF不显示中文字体

    在Ubuntu中安装中文字体:    $ sudo apt-get install ttf-wqy-zenhei    $ sudo apt-get install ttf-wqy-microhei

  4. linux中,常用的账号管理命令

    创建新用户:adduser 用户名创建新用户并将其加入一个现有组中:adduser 用户名 -G 组名创建新用户并使其只属于该组:adduser 用户名 -g 组名创建用户密码:passwd 用户名创 ...

  5. linux卸载mysql,apache,php

    卸载Mysql 1.查找以前是否装有mysql 命令:rpm -qa|grep -i mysql 可以看到mysql的包: mysql-3.23.58-9php-mysql-4.3.4-11mod_a ...

  6. BizTalk开发系列(五) 属性字段

    在根据消息内容进行路由的时候经常使用的是可分辨字段和属性字段.属性字段可以在各个 BizTalk Server 组件(包括管道和业务流程)中进行访问.属性字段还可用于消息路由.如果需要在上下文(而不是 ...

  7. 深入分析 Java 中的中文编码问题

    登录 (或注册) 中文 IBM 技术主题 软件下载 社区 技术讲座 打印本页面 用电子邮件发送本页面 新浪微博 人人网 腾讯微博 搜狐微博 网易微博 Digg Facebook Twitter Del ...

  8. php://input,php://filter,data URI schema的那些事

    一.php://input一句话木马 在调研dedecms的历史漏洞时,发现了dedecms安装文件曾经出过被植入后门的漏洞(SSV-ID站点include目录下shopcar.class.php文件 ...

  9. win8.1蓝屏解决

    按Windows 徽标键 +X键,点击“命令提示符(管理员)”,复制以下命令并运行: SFC /SCANNOW (此命令需要一段时间完成,不要关闭它,即使进度看上去停止不动.) reg add &qu ...

  10. Unit05 - 抽象类、接口和内部类(上)

    Unit05 - 抽象类.接口和内部类(上) 1.static final常量:  1)必须声明同时初始化  2)通过类名来访问,不能被改变  3)建议:常量名所有字母都大写(多个单词间用_)  4) ...