针对 CoordinatorLayout 及 Behavior 的一次细节较真

2023-11-05 05:30

本文主要是介绍针对 CoordinatorLayout 及 Behavior 的一次细节较真,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我认真不是为了输赢,我就是认真。– 罗永浩

我一直对 Material Design 很感兴趣,每次在官网上阅读它的相关文档时,我总会有更进一步的体会。当然,Material Design 并不是仅仅针对 Android 而言的,它其实是一套普遍性的设计规范。而对于 Android 开发人员而言,我们涉及的往往是它的实现。也就是一个个个性鲜明的类。比如 RecyclerView 、CardView、Palette 等等。并且为了让开发者更轻松地开发出符合 Material Design 设计规范的界面,Google 开发人员直接提供了一个兼容包,它就是 Android Support Design Library。

引用这个包需要在 build.gradle 中添加依赖。

compile 'com.android.support:design:25.0.1'

在这个包中,最核心的一个类就是 CoordinatorLayout。因为其它的类都需要与它进行相关联才能互动。而今天的主题就是讨论这个类的一些细节。
这里写图片描述

上图中这种高大上的视觉和交互效果,第一次看的时候我心头就痒痒的,恨不得立马就去实现它。然后,就百度查找相关的博文,但是风格我都不是很喜欢。我不喜欢文章中放一个 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"android:fitsSystemWindows="true"tools:context="com.frank.supportdesigndemo.ScrollingActivity"><android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"android:layout_width="match_parent"android:layout_height="@dimen/app_bar_height"android:layout_marginTop="-28dp"android:fitsSystemWindows="true"android:theme="@style/AppTheme.AppBarOverlay"><android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"android:layout_width="match_parent"android:layout_height="match_parent"android:fitsSystemWindows="true"app:contentScrim="?attr/colorPrimary"app:layout_scrollFlags="scroll|exitUntilCollapsed"><ImageView
                android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"android:src="@drawable/test"app:layout_collapseMode="parallax"app:layout_collapseParallaxMultiplier="0.7"/><android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_collapseMode="pin"app:popupTheme="@style/AppTheme.PopupOverlay" /></android.support.design.widget.CollapsingToolbarLayout></android.support.design.widget.AppBarLayout><include layout="@layout/content_scrolling" /><android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="@dimen/fab_margin"app:layout_anchor="@id/app_bar"app:layout_anchorGravity="bottom|end"app:srcCompat="@android:drawable/ic_dialog_email" /></android.support.design.widget.CoordinatorLayout>

我照着完成了,效果也达到了,但是感觉有些虚。我不得劲,或者说我内心纠结吧。内心千头万绪,上面的布局文件中,除了 Toolbar 我认识外,其它的控件,我一个都不熟悉。

我不喜欢这种感觉。因为我有许许多多的疑惑。

CoordinatorLayout 是什么?
CoordinatorLayout 有什么作用?
AppBarLayout 是什么?
AppBarLayout 有什么作用?
……

接下来的文章篇幅会比较长,大家仔细阅读就好。如果时间不够,可以直接拖动到文章最后总结的那一节。不明白的地方再到文章中间部分阅读相关内容就可以了。但我希望读者还是顺序方式阅读,因为我相信如果你有许多疑惑,我的学习过程也许可以给你一些提示或者启迪。

更多的真相

在编程领域,学习一个陌生的事物,最好的途径可能就是阅读它的官方文档或者是源代码。带着心中的困惑,我前往 Android 官网,直接挑最显眼的 CoordinatorLayout 来进行研究。所以这篇文章我主讲 CoordinatorLayout。
这里写图片描述

官网解释 CoordinatorLayout 是一个超级 FrameLayout,然后可以作为一个容器指定与 child 的一些交互规则。

public class CoordinatorLayout extends ViewGroup implements NestedScrollingParent {}

这是 CoordinatorLayout 的声明。它本质上就是一个 ViewGroup,注意的是它并没有继承自 FrameLayout,然后实现了 NestedScrollingParent 接口,我们先不管这个 NestedScrollingParent,NestedScrolliingParent 在文章后面适当的地方我会给出解释。

官网又说通过给CoordinaotrLayout 中的 child 指定 Behavior,就可以和 child 进行交互,或者是 child 之间互相进行相关的交互。并且自定义 View 时,可以通过 DefaultBehavior 这个注解来指定它关联的 Behavior。这里出现了新的名词:Behavior。于是,中断对 CoordinatorLayout 的跟踪,转到 Behavior 细节上来。
这里写图片描述
Behavior 其实是 CoordinatorLayout 中的一个静态内部类,并且是个泛型,接受任何 View 类型。官方文档真是惜字如金,更多的细节需要去阅读代码,也就是要靠猜测。这点很不爽的。好吧,官方文档说 Behavior 是针对 CoordinatorLayout 中 child 的交互插件。记住这个词:插件。插件也就代表如果一个 child 需要某种交互,它就需要加载对应的 Behavior,否则它就是不具备这种交互能力的。而 Behavior 本身是一个抽象类,它的实现类都是为了能够让用户作用在一个 View 上进行拖拽、滑动、快速滑动等手势。如果自己要定制某个交互动作,就需要自己实现一个 Behavior。

但是,对于我们而言,我们要实现一个 Behavior,我们用来干嘛呢?

是的,问问自己吧,我们如果自定义一个 Behavior,我们想干嘛?

前面内容有讲过,CoordinatorLayout 可以定义与它 child 的交互或者是某些 child 之间的交互。

我们先看看 Behavior 的代码细节,代码有精简。

public static abstract class Behavior<V extends View> {public Behavior() { }public Behavior(Context context, AttributeSet attrs) {}public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { return false; }public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {  return false; }public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {}public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,V child, View directTargetChild, View target, int nestedScrollAxes) {return false;}public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child,View directTargetChild, View target, int nestedScrollAxes) {// Do nothing}public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {// Do nothing}public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {// Do nothing}public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,int dx, int dy, int[] consumed) {// Do nothing}public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target,float velocityX, float velocityY, boolean consumed) {return false;}public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,float velocityX, float velocityY) {return false;}
}

一般我们自定义一个 Behavior,目的有两个,一个是根据某些依赖的 View 的位置进行相应的操作。另外一个就是响应 CoordinatorLayout 中某些组件的滑动事件。
我们先看第一种情况。

两个 View 之间的依赖关系

如果一个 View 依赖于另外一个 View。那么它可能需要操作下面 3 个 API:

public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { return false; }public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {  return false; }public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {}

确定一个 View 对另外一个 View 是否依赖的时候,是通过 layoutDependsOn() 这个方法。注意参数,child 是要判断的主角,而 dependency 是宾角,如果 return true,表示依赖成立,反之不成立。当然,你可以复写这个方法对 dependency 进行类型判断否则是其它条件判断,然后再决定是否依赖。只有在 layoutDependsOn() 返回为 true 时,后面的 onDependentViewChanged() 和 onDependentViewRemoved() 才会被调用。

当依赖的那个 View 发生变化时,这个变化代码注释有解释,指的是 dependency 的尺寸和位置发生的变化,当有变化时 Behavior 的 onDependentViewChanged() 方法会被调用。如果复写这个方法时,改变了 child 的尺寸和位置参数,则需要返回 true,默认情况是返回 false。

onDependentView() 被调用时一般是指 dependency 被它的 parent 移除,或者是 child 设定了新的 anchor。

有了上面 3 个 API,我们就能应付在 CoordinatorLayout 中一个子 View 对别个一个子 View 的依赖情景了。

可能会有同学不明白,依赖是为何?或者说是何种依赖。为了避免概念过于空洞抽象。下面,我们用一个简单的例子来让大家感受一下,加深理解。

为了演示效果,我首先在屏幕上定义一个能够响应拖动的自定义 View,我叫它 DependencyView 好了。
这里写图片描述
它的代码很简单,主要是继承一个 TextView,然后在触摸事件中对自身位置进行位移。

public class DependencyView extends TextView {private final int mSlop;private float mLastX;private float mLastY;public DependencyView(Context context) {this(context,null);}public DependencyView(Context context, AttributeSet attrs) {this(context, attrs,0);}public DependencyView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);setClickable(true);mSlop = ViewConfiguration.getTouchSlop();}@Overridepublic boolean onTouchEvent(MotionEvent event) {// return super.onTouchEvent(event);int action = event.getAction();switch (action){case MotionEvent.ACTION_DOWN:mLastX = event.getX();mLastY = event.getY();break;case MotionEvent.ACTION_MOVE:int deltax = (int) (event.getX() - mLastX);int deltay = (int) (event.getY() - mLastY);if (Math.abs(deltax) > mSlop || Math.abs(deltay) > mSlop) {ViewCompat.offsetTopAndBottom(this,deltay);ViewCompat.offsetLeftAndRight(this,deltax);mLastX = event.getX();mLastY = event.getY();}break;case MotionEvent.ACTION_UP:mLastX = event.getX();mLastY = event.getY();break;default:break;}return true;}
}

前置条件已经确定好了,现在我们要向目标 Behavior 出发了。

做一个跟屁虫

实现一个 Behavior,让它支配一个 View 去紧紧跟随所依赖的 View。在这

这篇关于针对 CoordinatorLayout 及 Behavior 的一次细节较真的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/347729

相关文章

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

vscode中使用go环境配置细节

1、在docker容器中下载了go的sdk 2、在/etc/profile.d/go.sh里填入如下内容: #!/bin/bashexport GOROOT=/home/ud_dev/goexport PATH=$GOROOT/bin:$PATH  3、设置go env go env -w GOPROXY=https://goproxy.cn,directgo env -w GO

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位 一、背景二、定位问题三、解决方法 一、背景 flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案在博主上面这篇文章的基础上,在机器内存、cpu资源、flume agent资源都足够的情况下,flume agent又出现了tmp文件无法关闭的情况 二、

使用WebP解决网站加载速度问题,这些细节你需要了解

说到网页的图片格式,大家最常想到的可能是JPEG、PNG,毕竟这些老牌格式陪伴我们这么多年。然而,近几年,有一个格式悄悄崭露头角,那就是WebP。很多人可能听说过,但到底它好在哪?你的网站或者项目是不是也应该用WebP呢?别着急,今天咱们就来好好聊聊WebP这个图片格式的前世今生,以及它值不值得你花时间去用。 为什么会有WebP? 你有没有遇到过这样的情况?网页加载特别慢,尤其是那

一些数学经验总结——关于将原一元二次函数增加一些限制条件后最优结果的对比(主要针对公平关切相关的建模)

1.没有分段的情况 原函数为一元二次凹函数(开口向下),如下: 因为要使得其存在正解,必须满足,那么。 上述函数的最优结果为:,。 对应的mathematica代码如下: Clear["Global`*"]f0[x_, a_, b_, c_, d_] := (a*x - b)*(d - c*x);(*(b c+a d)/(2 a c)*)Maximize[{f0[x, a, b,

jmeter之仅一次控制器

仅一次控制器作用: 不管线程组设置多少次循环,它下面的组件都只会执行一次 Tips:很多情况下需要登录才能访问其他接口,比如:商品列表、添加商品到购物车、购物车列表等,在多场景下,登录只需要1次,我们期望的是重复执行登陆后面的接口来做压测,这就和事务相关,例如 事务1: 登录—>添加购物车 事务2: 登录—>购物车列表 事务3: 登录—>商品列表—>添加购物车 … 一、仅一次控制器案例 在

分享MSSQL、MySql、Oracle的大数据批量导入方法及编程手法细节

1:MSSQL SQL语法篇: BULK INSERT      [ database_name . [ schema_name ] . | schema_name . ] [ table_name | view_name ]         FROM 'data_file'        [ WITH       (      [ [ , ] BATCHSIZE = batch_siz

【LVI-SAM】激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节

激光雷达点云处理特征提取LIO-SAM 之FeatureExtraction实现细节 1. 特征提取实现过程总结1.0 特征提取过程小结1.1 类 `FeatureExtraction` 的整体结构与作用1.2 详细特征提取的过程1. 平滑度计算(`calculateSmoothness()`)2. 标记遮挡点(`markOccludedPoints()`)3. 特征提取(`extractF

一次生产环境大量CLOSE_WAIT导致服务无法访问的定位过程

1.症状 生产环境的一个服务突然无法访问,服务的交互过程如下所示: 所有的请求都是通过网关进入,之后分发到后端服务。 现在的情况是用户服务无法访问商旅服务,网关有大量java.net.SocketTimeoutException: Read timed out报错日志,商旅服务也不断有日志打印,大多是回调和定时任务日志,所以故障点在网关和商旅服务,大概率是商旅服务无法访问导致网关超时。 后

关于一次速度优化的往事

来自:hfghfghfg, 时间:2003-11-13 16:32, ID:2292221你最初的代码 Button1 34540毫秒 5638毫秒  Button2 我的代码 这个不是重点,重点是这个  来自:hfghfghfg, 时间:2003-11-13 16:54, ID:22923085528毫秒 不会吧,我是赛杨1.1G  128M内存  w2000, delphi6  128M