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

先来看看最终的效果~~

bbae17c7ac3bf3652832a72bd4336045.gif

本文同步至博主的私人博客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:layout_height="match_parent"

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

>

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="观察者View child"

/>

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center"

android:text="被观察View dependency"

/>

这里我们在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类,继承于Behaviorpublic class EasyBehavior extends CoordinatorLayout.Behavior {//这里的泛型是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。

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="观察者View child"

/>

运行效果如下:

143af0f3180893b8f466120ca66c1d06.gif

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

难度 2 仿UC折叠Behavior

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

先定义xml如下:

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:layout_width="match_parent"

android:layout_height="wrap_content"

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

app:elevation="0dp"

>

android:layout_width="match_parent"

android:layout_height="wrap_content"

app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"

>

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"

/>

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"

>

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"

>

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: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"

>

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

思路分析

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

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坐标也跟着变化。达到如预览图效果。

7e23eb54dc1b7cbecb2a5782eacc02a9.gif

难度3 头像缩放效果

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

所以具体的解析就不说了,直接上个Behavior代码/泛型为child类型public class CustomBehavior extends CoordinatorLayout.Behavior {    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);

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

549f9a62b1ce0be7e1c8846f58bfef07.png

怎么样,不难吧,哈 喜欢的点个赞 给个star哦~~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优化此布局<com.google.android.material.textfield.TextInputLayout android:id="@+id/usernameLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="24dp" android:hint="用户名" layout_height="" layout_width="" xmlns:android="http://schemas.android.com/apk/res/android"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/usernameEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="text" android:maxLines="1" /> <com.google.android.material.textfield.TextInputLayout android:id="@+id/passwordLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:hint="密码"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/passwordEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:maxLines="1" /> <com.google.android.material.textfield.TextInputLayout android:id="@+id/phoneLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:hint="密码"> <com.google.android.material.textfield.TextInputEditText android:id="@+id/phoneEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textPassword" android:maxLines="1" /> </com.google.android.material.textfield.TextInputLayout> <Button android:id="@+id/registerButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="注册" /> </com.google.android.material.textfield.TextInputLayout>
05-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值