Android CoordinatorLayout和Behavior的源码分析(一)

本文深入探讨了CoordinatorLayout及其核心组件Behavior的工作原理和技术细节。详细分析了Behavior的三种实例化方式,包括通过构造方法、XML配置及使用DefaultBehavior注解的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CoordinatorLayout布局,是“com.android.support:design”包中很重要的一个控件,通过为CoordinatorLayout(以下简称Col)的子view直接设置一个Behavior,可以定制出很棒的交互体验。本文从使用到源码解析来学习Col,源码基于23.3.0版本。

一、CoordinatorLayout和Behavior的源码分析

结合上面的类图,我们把Behavior理解成一个代理,他来代理子view的显示触摸滑动事件。

1. Behavior的实例化

1.1 通过构造方法实例,并在代码中设置到LayoutParams里
MyBehavior myBehavior = new MyBehavior();
CoordinatorLayout.LayoutParams params =(CoordinatorLayout.LayoutParams) yourView.getLayoutParams();
params.setBehavior(myBehavior)

很简单就不多说了~

1.2 XML方式实例化Behavior

在xml文件中给属性layout_behavior指定对应的behavior:

app:layout_behavior="com.aswifter.material.behavior.ScrollBehavior"

类ScrollBehavior中:

public class ScrollBehavior extends CoordinatorLayout.Behavior<View> {
    public ScrollBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        }
    ...
    }

看下CoordinatorLayout.Behavior

public static abstract class Behavior<V extends View> {
    public Behavior() {
    }
    public Behavior(Context context, AttributeSet attrs) {
    }
       ...
}

以上可以看到,调用了这个两个参数的构造方法,这里为空,那么怎么实例化ScrollBehavior类的呢?

Behavoir是CoordinatorLayout.LayoutParams的属性值:

public static class LayoutParams extends ViewGroup.MarginLayoutParams {
    Behavior mBehavior;
    boolean mBehaviorResolved = false;    
    ...

    LayoutParams(Context context, AttributeSet attrs) {
        super(context, attrs);

        mBehaviorResolved = a.hasValue(
             R.styleable.CoordinatorLayout_LayoutParams_layout_behavior);
        if (mBehaviorResolved) {
            mBehavior = parseBehavior(context, attrs, a.getString(
                   R.styleable.CoordinatorLayout_LayoutParams_layout_behavior));
        }
        ...
    }

}

在这里,先检查是否有layout_behavior,接下来通过调用parseBehavior方法得到Behavior的实例

parseBehavior方法:

static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
    ...

    //fullname指定behavior的完整路径,这里就不贴代码了,很简单~
    final String fullName;
    ....

    try {
        Map<String, Constructor<Behavior>> constructors = sConstructors.get();
        if (constructors == null) {
            constructors = new HashMap<>();
            sConstructors.set(constructors);
        }
        Constructor<Behavior> c = constructors.get(fullName);
        //利用反射机制实例化指定的Behavior,例如:ScollBehavior
        if (c == null) {
            final Class<Behavior> clazz = (Class<Behavior>) Class.forName(fullName, true,
                context.getClassLoader());
            c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
            c.setAccessible(true);
            constructors.put(fullName, c);
        }
        //指定了构造函数参数类型,所以在自定义的时候,必须要有含有这些参数的构造函数
        return c.newInstance(context, attrs);
    } catch (Exception e) {
        throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
        }
}

到这里就能够明白xml方式是如何实例化Behavior了~

1.3 通过DefaultBehavior注解

以AppBarLayout.Behavior为例:

@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
public class AppBarLayout extends LinearLayout {
    ...
}

进入DefaultBehavior

@Retention(RetentionPolicy.RUNTIME)
public @interface DefaultBehavior {
    Class<? extends Behavior> value();
}

发现这是一个接口,我们在看看哪里具体实现了这个接口:

LayoutParams getResolvedLayoutParams(View child) {
    final LayoutParams result = (LayoutParams) child.getLayoutParams();
    if (!result.mBehaviorResolved) {
        Class<?> childClass = child.getClass();
        DefaultBehavior defaultBehavior = null;
        while (childClass != null &&
                (defaultBehavior = childClass.getAnnotation(DefaultBehavior.class)) == null) {
            childClass = childClass.getSuperclass();
        }
        if (defaultBehavior != null) {
            try {
                //实例化Behavior
                result.setBehavior(defaultBehavior.value().newInstance());
            } catch (Exception e) {
                Log.e(TAG, "Default behavior class " + defaultBehavior.value().getName() +
                        " could not be instantiated. Did you forget a default constructor?", e);
            }
        }
        result.mBehaviorResolved = true;
    }
    return result;
}

那这个getResolvedLayoutParams方法在哪里被调用的,接着看,发现在prepareChildren方法中调用,而prepareChildren被onMeasure调用。

到这里就明白,注解方式实例化Behavoir是怎么实现的了~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值