If you want to know what a man's like, take a good look at how he treats his inferiors, not his equals.
查看view层级:
1.Layout Inspector Androidstudio
- hierarchyviewer(sdk path\tools)
3.手机设置,开发人员选项-显示布局边界
- view层级越简单,应用的性能越好。
view中很多flag 运算,x&=~y, 取反再与。
算法技巧:& 一般用在判断是否整除。当然也可以直接用%,具体看用哪个方便,速度差不多。
比如:n和i都是正整数,n&(i-1):i必须为偶数。则n被i整除。
n&(4-1) ==0 说明n一定能被4整除。
n&(2-1) ==0 说明n一定能被2整除。
下面从几个常见viewgroup来研究view的布局。
(补充:几个比较好的ui库。http://www.androidchina.net/6531.html)
ConstraintLayout优点:
层级少,和relativelayout类似,但功能更强大,Androidstudio默认支持工具拖拽
使用方法
1.导包
a)确保maven.google.com在模块级build.gradle文件中声明了存储库 :
存储库{
google ()}
b)将库作为依赖项添加到同一build.gradle文件中,如下面的示例所示。请注意,最新版本可能与示例中显示的不同:
dependencies {
implementation'com.android.support.constraint :constraint-layout:1.1.2' }
- 如何创建
a)直接创建
1)在“ 项目”窗口中,单击模块文件夹,然后选择“ 文件”>“新建”>“XML”>“布局XML”。
2)输入布局文件的名称,并为根标签输入“android.support.constraint.ConstraintLayout” 。
3)单击完成。
b)将现有布局转换为约束布局
在Android Studio中打开布局,然后单击编辑器窗口底部的“ 设计”选项卡。
在“ 组件树”窗口中,右键单击布局,然后单击“ 将布局转换为ConstraintLayout”。
其他拖拽操作,见:https://developer.android.com/training/constraint-layout
注:支持,自动约束功能。
关键帧动画
ConstraintSet动画仅为子元素的大小和位置设置动画。它们不会为其他属性(例如颜色)设置动画。
通过[ConstraintSet]和[TransitionManager]即可设置。
衍生物,MotionLayout
专门用来处理约束动画,参考https://www.jianshu.com/p/54a6e2568cdd
开始分析ConstraintLayout与普通的布局,特别之处。
官方例子:https://github.com/googlesamples/android-ConstraintLayoutExamples
onLayout
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int widgetsCount = this.getChildCount();
boolean isInEditMode = this.isInEditMode();
for(int i = 0; i < widgetsCount; ++i) {
View child = this.getChildAt(i);
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams)child.getLayoutParams();
if (child.getVisibility() != 8 || params.isGuideline || isInEditMode) {
ConstraintWidget widget = params.widget;
int l = widget.getDrawX();
int t = widget.getDrawY();
int r = l + widget.getWidth();
int b = t + widget.getHeight();
child.layout(l, t, r, b);
}
}
}
onMeasure
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int paddingLeft = this.getPaddingLeft();
int paddingTop = this.getPaddingTop();
this.mLayoutWidget.setX(paddingLeft);
this.mLayoutWidget.setY(paddingTop);
this.setSelfDimensionBehaviour(widthMeasureSpec, heightMeasureSpec);
if (this.mDirtyHierarchy) {
this.mDirtyHierarchy = false;
this.updateHierarchy();
}
this.internalMeasureChildren(widthMeasureSpec, heightMeasureSpec);
if (this.getChildCount() > 0) {
this.solveLinearSystem();
}
int childState = 0;
int sizeDependentWidgetsCount = this.mVariableDimensionsWidgets.size();
int heightPadding = paddingTop + this.getPaddingBottom();
int widthPadding = paddingLeft + this.getPaddingRight();
int resolvedHeightSize;
if (sizeDependentWidgetsCount > 0) {
boolean needSolverPass = false;
boolean containerWrapWidth = this.mLayoutWidget.getHorizontalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT;
boolean containerWrapHeight = this.mLayoutWidget.getVerticalDimensionBehaviour() == DimensionBehaviour.WRAP_CONTENT;
for(resolvedHeightSize = 0; resolvedHeightSize < sizeDependentWidgetsCount; ++resolvedHeightSize) {
ConstraintWidget widget = (ConstraintWidget)this.mVariableDimensionsWidgets.get(resolvedHeightSize);
if (!(widget instanceof android.support.constraint.solver.widgets.Guideline)) {
View child = (View)widget.getCompanionWidget();
if (child != null && child.getVisibility() != 8) {
int widthSpec = false;
int heightSpec = false;
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams)child.getLayoutParams();
int widthSpec;
if (params.width == -2) {
widthSpec = getChildMeasureSpec(widthMeasureSpec, widthPadding, params.width);
} else {
widthSpec = MeasureSpec.makeMeasureSpec(widget.getWidth(), 1073741824);
}
int heightSpec;
if (params.height == -2) {
heightSpec = getChildMeasureSpec(heightMeasureSpec, heightPadding, params.height);
} else {
heightSpec = MeasureSpec.makeMeasureSpec(widget.getHeight(), 1073741824);
}
child.measure(widthSpec, heightSpec);
int measuredWidth = child.getMeasuredWidth();
int measuredHeight = child.getMeasuredHeight();
int baseline;
if (measuredWidth != widget.getWidth()) {
widget.setWidth(measuredWidth);
if (containerWrapWidth && widget.getRight() > this.mLayoutWidget.getWidth()) {
baseline = widget.getRight() + widget.getAnchor(Type.RIGHT).getMargin();
this.mLayoutWidget.setWidth(Math.max(this.mMinWidth, baseline));
}
needSolverPass = true;
}
if (measuredHeight != widget.getHeight()) {
widget.setHeight(measuredHeight);
if (containerWrapHeight && widget.getBottom() > this.mLayoutWidget.getHeight()) {
baseline = widget.getBottom() + widget.getAnchor(Type.BOTTOM).getMargin();
this.mLayoutWidget.setHeight(Math.max(this.mMinHeight, baseline));
}
needSolverPass = true;
}
if (params.needsBaseline) {
baseline = child.getBaseline();
if (baseline != -1 && baseline != widget.getBaselineDistance()) {
widget.setBaselineDistance(baseline);
needSolverPass = true;
}
}
if (android.os.Build.VERSION.SDK_INT >= 11) {
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
}
}
if (needSolverPass) {
this.solveLinearSystem();
}
}
int androidLayoutWidth = this.mLayoutWidget.getWidth() + widthPadding;
int androidLayoutHeight = this.mLayoutWidget.getHeight() + heightPadding;
if (android.os.Build.VERSION.SDK_INT >= 11) {
int resolvedWidthSize = resolveSizeAndState(androidLayoutWidth, widthMeasureSpec, childState);
resolvedHeightSize = resolveSizeAndState(androidLayoutHeight, heightMeasureSpec, childState << 16);
resolvedWidthSize = Math.min(this.mMaxWidth, resolvedWidthSize);
resolvedHeightSize = Math.min(this.mMaxHeight, resolvedHeightSize);
resolvedWidthSize &= 16777215;
resolvedHeightSize &= 16777215;
if (this.mLayoutWidget.isWidthMeasuredTooSmall()) {
resolvedWidthSize |= 16777216;
}
if (this.mLayoutWidget.isHeightMeasuredTooSmall()) {
resolvedHeightSize |= 16777216;
}
this.setMeasuredDimension(resolvedWidthSize, resolvedHeightSize);
} else {
this.setMeasuredDimension(androidLayoutWidth, androidLayoutHeight);
}
}
ConstraintLayout 用法:
1.相对定位
水平轴:左,右,起点和终点
垂直轴:顶部,底部和文本基线
两种写法,见下面代码
//希望按钮B的左侧被约束到按钮A的右侧。这样的位置约束意味着系统将尝试让双方共享相同的位置。
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintLeft_toRightOf="@+id/buttonA" />
//可以引用父容器,即ConstraintLayout,如:
<Button android:id="@+id/buttonB" ...
app:layout_constraintLeft_toLeftOf="parent" />
图形表示上面的说明:
relative-positioning-constraints.png relative-positioning.png
注:start,left 的区别,后面再分析。
2.边距:
如果设置了边距,则它们将应用于相应的约束(如果存在)(如图),将边距强制为目标和源边之间的空间。通常的布局边距属性同其他布局。
Note that a margin can only be positive or equals to zero, and takes a Dimension.
注:margin为非负数。
如:android:layout_marginLeft,android:layout_marginStart等
relative-positioning-margin.png
3.如果目标控件为gone,设置参考如下:
layout_goneMarginLeft
layout_goneMarginTop
4.居中以及偏移
下面为水平居中,垂直居中类似
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
</>
centering-positioning.png
偏移:
):
<android.support.constraint.ConstraintLayout ...>
<Button android:id="@+id/button" ...
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent/>
</>
5.循环定位(1.1中增加)
/您可以以角度和距离约束窗口小部件中心相对于另一个窗口小部件中心。这允许您将小部件放在圆上(参见图6)。可以使用以下属性:
layout_constraintCircle :引用另一个小部件ID
layout_constraintCircleRadius :到其他窗口小部件中心的距离
layout_constraintCircleAngle :小部件应该处于哪个角度(以度为单位,从0到360)
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintCircle="@+id/buttonA"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="45" />
circle2.png
circle1.png
6.可见性行为
特点:
对于布局传递,它们的尺寸将被视为零(基本上,它们将被解析为一个点)
如果他们对其他小部件有限制,他们仍然会受到尊重,但任何边距都会好像等于零
注意:使用的边距将是B在连接到A时定义的边距(例如,参见图7)。在某些情况下,这可能不是您想要的余量(例如A在其容器侧面有100dp的边距,B只有16dp到A,A标记为已消失,B将有16dp的余量到容器)
visibility-behavior.png7.尺寸限制
您可以为ConstraintLayout自身定义最小和最大尺寸:(高度类似)
android:minWidth 设置布局的最小宽度
android:maxWidth 设置布局的最大宽度
8.android:layout_width和 android:layout_height属性,三种方式
- 使用特定维度(文字值,例如123dp或Dimension参考)
- 使用WRAP_CONTENT,这将要求小部件计算自己的大小
- 使用0dp,相当于“ MATCH_CONSTRAINT”
a) WRAP_CONTENT,
b) MATCH_CONSTRAINT
如果设置了边距,则在计算中将考虑它们(图8,(c)中的0dp)。
dimension-match-constraints.png
注意:不建议使用MATCH_PARENT实现a的方式,
可以通过使用MATCH_CONSTRAINT设置为相应的左/右或上/下约束来定义类似的行为"parent"。
WRAP_CONTENT:强制约束(在1.1中添加
app:layout_constrainedWidth=”true|false”
app:layout_constrainedHeight=”true|false”
MATCH_CONSTRAINT尺寸(1.1中添加)
layout_constraintWidth_min和layout_constraintHeight_min:将设置此维度的最小大小
layout_constraintWidth_max和layout_constraintHeight_max:将设置此维度的最大大小
layout_constraintWidth_percent和layout_constraintHeight_percent:将此维度的大小设置为父级的百分比
9.Min and Max
设置xxdp
设置 "wrap"
10.比率
您还可以将窗口小部件的一个维度定义为另一个维度的比率。
您需要将至少一个约束维度设置为0dp(即MATCH_CONSTRAINT),并将该属性layout_constraintDimensionRatio设置为给定比率。例如:
//将按钮的高度设置为与其宽度相同。
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
//将按照16:9的比例设置按钮的高度,而按钮的宽度将匹配父项的约束。
<Button android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
链
如果一组小部件通过双向连接链接在一起,则它们被视为链(参见图9,显示最小链,具有两个小部件)。
链头:头部是水平链的最左侧小部件,垂直链的最顶部小部件。
chains-head.png链的样式
chains-styles.png
设置属性layout_constraintHorizontal_chainStyle或layout_constraintVertical_chainStyle链的第一个元素时,链的行为将根据指定的样式(默认值CHAIN_SPREAD)更改。
默认样式CHAIN_SPREAD - 元素将展开。
链的默认 是在可用空间中平均分布.
例如,在包含两个元素的链上使用MATCH_CONSTRAINT,第一个元素使用权重2,第二个元素使用权重1,第一个元素占用的空间将是第二个元素占用的空间的两倍。
链中的元素上使用边距时,边距是相加的
案例:在水平链上,如果一个元素定义了10dp的右边距,而下一个元素定义了5dp的左边距,则这两个元素之间产生的边距为15dp。
优化(1.1):
您可以通过将标记app:layout_optimizationLevel添加到ConstraintLayout元素来决定应用哪些优化
none:不应用任何优化
standard:默认。仅优化直接和屏障约束
direct:优化直接约束
barrier:优化障碍限制
chain:优化链约束(实验)
dimensions:优化维度度量(实验),减少匹配约束元素的度量数量
其他:Guideline, Barrier ,Group
使用参考:
https://github.com/googlesamples/android-ConstraintLayoutExamples
网友评论