前段时间hongyang大神发布了一个库,AndroidAutoLayout。该库的使用,是用户(该库的使用者,即,猿们)告诉app,设计图的宽和高为多少像素,然后在UI布局里直接使用px作为单位,该库会自动将填写的px值转换为屏幕的百分比值,以此来完成适配。

该库的使用方式有两种,一种是直接将Activity extends AutoLayoutActivity,另一种是在布局文件里使用该库提供的三个代替LinearLayout, FrameLayout, RelativeLayout的AutoLinearLayout, AutoFrameLayout, AutoRelativeLayout控件.

如果是使用直接继承AutoLayoutActivity的方法,这个相当省事。其实现是:在AutoLayoutActivity类内部重写了onCreateView(),这个方法会在Activity的onCreate()之后调用,onCreateView调用完毕才调用Activity生命周期函数onStart().

这是它在Activity源码中的说明,默认实现是返回null,它是LayoutInflater.Factory#onCreateView()的实现。

/**

* Standard implementation of

* {@link android.view.LayoutInflater.Factory#onCreateView} used when

* inflating with the LayoutInflater returned by {@link #getSystemService}.

* This implementation does nothing and is for

* pre-{@link android.os.Build.VERSION_CODES#HONEYCOMB} apps. Newer apps

* should use {@link #onCreateView(View, String, Context, AttributeSet)}.

*

* @see android.view.LayoutInflater#createView

* @see android.view.Window#getLayoutInflater

*/

@Nullable

public View onCreateView(String name, Context context, AttributeSet attrs) {

return null;

}

而Factory是LayoutInflater中的一个接口

public interface Factory {

/**

* Hook you can supply that is called when inflating from a LayoutInflater.

* You can use this to customize the tag names available in your XML

* layout files.

*

*

* Note that it is good practice to prefix these custom names with your

* package (i.e., com.coolcompany.apps) to avoid conflicts with system

* names.

*

* @param name Tag name to be inflated.

* @param context The context the view is being created in.

* @param attrs Inflation attributes as specified in XML file.

*

* @return View Newly created view. Return null for the default

* behavior.

*/

public View onCreateView(String name, Context context, AttributeSet attrs);

}

在这个方法传进来的参数中的name是每一个View(放在布局文件中的View)的名字,比如LinearLayout, FrameLayout,TextView。

在Activity中onCreateView()会被调用多次,顺序是LinearLayout , ViewStub , FrameLayout(这个就是ContentFrameLayout),之后就是我们写在xml里的View了,所以在这里我们能拿到写在xml文件里的控件名字。

LayoutInflater创建View的方法是createView(),其流程是:从xml中解析出来信息后,判断是否带包名,如果没带包名就给name拼接上前缀(也就是系统的包名,例如TextView变成android.widget.TextView),带包名的(使用自定义View,扩展包View的时候要带包名)就不需要加前缀。拿到了这个View的包名+类名,类加载器加载class,取得构造方法,将View new出来。

而如果onCreateView返回的不是null,而是一个View,就不会走createView()。

作者就是在AutoLayoutActivity中重写了该方法,并判断如果是LinearLayout,就返回该库里的AutoLinearLayout。RelativeLayout,FrameLayout同上。如此便做到了偷梁换柱的效果,我们在xml里使用的是LinearLayout,但实际生成的AutoLinearLayout。

public View onCreateView(String name, Context context, AttributeSet attrs) {

Object view = null;

if(name.equals("FrameLayout")) {

view = new AutoFrameLayout(context, attrs);

}

if(name.equals("LinearLayout")) {

view = new AutoLinearLayout(context, attrs);

}

if(name.equals("RelativeLayout")) {

view = new AutoRelativeLayout(context, attrs);

}

return (View)(view != null?view:super.onCreateView(name, context, attrs));

}

而用户不是选择使用Activity extends AutoLayoutActivity这种方式,而是在xml文件里直接写上包名+AutoXXXLayout,额。。。这里好像没什么好说的。

接下来,我们来看该库的自动布局类,这里分析AutoFrameLayout,其余两个类似。

先说下背景,由于我们已经告诉app我们设计图的大小了(该库的使用条件之一,在Manifest.xml中添加meta,两个字段design_width,design_height),程序就能拿到设计图的宽高(单位为像素),而设备的屏幕宽高我们也是可以拿到的。当我们在布局文件里将View的属性值设为像素值时,自然就可以通过换算得到实际上在不同的屏幕上该占多少百分比的像素了。以宽为例:

(edit_width / design_width)* screen_width 这个结果就是实际的像素值。edit_width是用户填写的px值。这部分代码就不分析了。

既然已经把FrameLayout偷梁换柱成我们自己的View(AutoFrameLayout)了,那么就可以拿到布局文件时写的各个值(包括自定义属性,系统属性)。

作者在AutoFrameLayout中主要的代码是:

public AutoFrameLayout.LayoutParams generateLayoutParams(AttributeSet attrs) {

return new AutoFrameLayout.LayoutParams(this.getContext(), attrs);

}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if(!this.isInEditMode()) {

this.mHelper.adjustChildren();

}

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

重写generateLayoutParams(),让该ViewGroup中的每一个View的LayoutParams变成该类的静态内部类LayoutParams。

而在onMeasure()方法中借由AutoLayoutHelper来调整每一个子View的属性(该库支持9个属性)。

而在AutoFrameLayout.LayoutParams类中会把该库现有两个自定义属性(basewidth,baseheight),和9个支持的系统属性的值拿出来,然后将2个自定义属性和9个属性值放在AutoFrameLayout.LayoutParams的成员变量AutoLayoutInfo中,并提供getter。

public static class LayoutParams extends android.widget.FrameLayout.LayoutParams implements AutoLayoutParams {

private AutoLayoutInfo mAutoLayoutInfo;

public LayoutParams(Context c, AttributeSet attrs) {

super(c, attrs);

this.mAutoLayoutInfo = AutoLayoutHelper.getAutoLayoutInfo(c, attrs);

}

//...这处代码省略

public AutoLayoutInfo getAutoLayoutInfo() {

return this.mAutoLayoutInfo;

}

}

这里有一点需要提一下,在记录过程中,只有属性是以“px”结尾的属性才会被记录下来。也就是说,如果你在AutoFrameLayout中某个View某个属性不想使用这个百分比自动布局的功能,只要不把属性写成px的就行了。(我在用这个库的时候,有一个View的宽属性无法事前确定,只能wrap_content。还在想该库是不是可以提供一个自定义属性ignore可以让该View免于自动布局,看了代码才知道作者早就设计好了)

下面是adjustChildren()的代码:

public void adjustChildren() {

AutoLayoutConifg.getInstance().checkParams();

int i = 0;

for(int n = this.mHost.getChildCount(); i < n; ++i) {

View view = this.mHost.getChildAt(i);

LayoutParams params = view.getLayoutParams();

if(params instanceof AutoLayoutHelper.AutoLayoutParams) {

AutoLayoutInfo info = ((AutoLayoutHelper.AutoLayoutParams)params).getAutoLayoutInfo();

if(info != null) {

info.fillAttrs(view);

}

}

}

}

作者先做了一次检查,checkParams(),如果用户没有在Manifest.xml中提供design_width,design_height会报错。

之后就是拿到mHost(AutoXXXLayout)中的每一个View的LayoutParams,取出之前记录了自定义属性和属性值的AutoLayoutInfo,然后将里面的每一个属性作用到View上。这样就完成了View属性的更改。

该库提供两个自定义属性layout_auto_basewidth, layout_auto_baseheight。由于手机屏幕的碎片化(Android手机你懂的),比如想让一个ImageView的宽高一致时,写宽20px,高20px,经过百分比换算屏幕大小,很可能就不一致了。所以需要一个单位基准,比如宽20px,高20px,layout_auto_basewidth=height,那么高度就会以width为基准了,其换算公式是(edit_height/design_width) * screen_width。如此,这个ImageView的宽高就一样大小了,正方形。

AutoAttr这个类作者用来封装每一个属性值的信息,一般就是多少px,是哪个方向(宽,高)为基准。如果我们在xml里没有指定某属性的基准,就会使用该属性的默认基准。默认基准是,竖直方向上以高为基准,水平方向以宽为基准。即:marginTop以height为基准,marginLeft以width为基准,textSize以height为基准,其他的同上。

AutoAttr的apply()方法代码如下:

public void apply(View view) {

boolean log = view.getTag() != null && view.getTag().toString().equals("auto");

if(log) {

L.e(" pxVal = " + this.pxVal + " ," + this.getClass().getSimpleName());

}

int val;

if(this.useDefault()) {

val = this.defaultBaseWidth()?this.getPercentWidthSize():this.getPercentHeightSize();

if(log) {

L.e(" useDefault val= " + val);

}

} else if(this.baseWidth()) {

val = this.getPercentWidthSize();

if(log) {

L.e(" baseWidth val= " + val);

}

} else {

val = this.getPercentHeightSize();

if(log) {

L.e(" baseHeight val= " + val);

}

}

val = Math.max(val, 1);

this.execute(view, val);

}

以上是我对AndroidAutoLayout的理解,如果错了,还请指正,谢谢。

autolayout教程Android,AndroidAutoLayout的简单阅读相关推荐

  1. Android TTS实现简单阅读器

    本文原创版权归 51CTO winorlose2000 所有,转载请按如下方式于文章显示位置详细标明原创作者及出处,以示尊重!! 作者:winorlose2000 原文:http://vaero.bl ...

  2. android导航使用教程,android BottomNavigationView的简单使用教程

    每个android app都有BottomNavigationView导航,本人开发中刚刚使用到了BottomNavigationView,于是按照android developer官网特意做了一个符 ...

  3. android教程 - android ui 介绍,多图详解 “Android UI”设计官方教程

    我们曾经给大家一个<MeeGo移动终端设备开发UI设计基础教程>,同时很多朋友都在寻找Android UI开发的教程,我们从Android的官方开发者博客找了一份幻灯片,介绍了一些Andr ...

  4. android实现计算器功能吗,利用Android实现一个简单的计算器功能

    利用Android实现一个简单的计算器功能 发布时间:2020-11-20 16:25:01 来源:亿速云 阅读:90 作者:Leah 今天就跟大家聊聊有关利用Android实现一个简单的计算器功能, ...

  5. Android开发环境简单配置

    为什么80%的码农都做不了架构师?>>>    ·         Android开发环境简单配置 写这个系列的原因也是因为自己对android比较感兴趣,而网上多数教程都是直接参照 ...

  6. 做简单的android 软件推荐,Android_适用于Android开发的简单聊天软件,适用于android 开发。是一个简 - phpStudy...

    适用于Android开发的简单聊天软件 适用于android 开发.是一个简单的聊天软件,包括知识点,各个控件的运用(ExpandableListView,ViewPager,Spinner,Line ...

  7. Android 相机教程,Android 相机教程

    Android相机教程 相机主要用于捕获图片和视频.我们可以通过使用相机API的方法来控制相机. Android通过以下两种方式提供了在相机上工作的功能: 通过相机意图 通过相机API 了解相机意图和 ...

  8. android 购物车实现,Android Studio实现简单购物车功能

    Android Studio实现简单购物车功能 发布时间:2020-08-30 17:23:56 来源:脚本之家 阅读:241 作者:攀岩嘉 本文实例为大家分享了Android九宫格图片展示的具体代码 ...

  9. 视频教程-按键精灵手机版解放您的双手自动化教程-Android

    按键精灵手机版解放您的双手自动化教程 从事游戏开发已经13年以上,软件开发管理10年以上. 段安 ¥29.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 APP订阅课程 ...

  10. android 阅读器自动滚动,Android编程实现小说阅读器滑动效果的方法

    本文实例讲述了Android编程实现小说阅读器滑动效果的方法.分享给大家供大家参考,具体如下: 看过小说都知道小说阅读器翻页有好多种效果,比如仿真翻页,滑动翻页,等等.由于某种原因,突然想写一个简单点 ...

最新文章

  1. Java项目:网上电商项目(前后端分离+java+vue+Springboot+ssm+mysql+maven+redis)
  2. 分享十款免费数据恢复软件
  3. python绘制笑脸-用python绘图
  4. string 类的实现
  5. 数据库原理学习笔记(一)关系完整性以及数据库完整性
  6. 使用 HttpWebRequest 向网站提交数据
  7. 如何运行网页html,如何在网页中运行html代码
  8. 12. nc/netcat 用法举例
  9. android camera实例
  10. 2d isometric 坐标变换
  11. 扁平卡通风毕业论文答辩PPT模板
  12. 调试经验——使用VBA下载网络资源
  13. Multisim仿真:验证性实验-单管共射放大电路
  14. mysql创建唯一非聚集索引_创建聚集索引、非聚集索引、唯一索引、唯一键约束...
  15. 51单片机蓝牙遥控风扇期末设计报告
  16. 这个寒冷的冬天 是谁的机会?
  17. 搞定HTML\CSS之background属性
  18. 对英雄难过美人关这个千古难题 高僧这样破解
  19. Java使用freemarker导出word试卷
  20. 【K8S】k8s pv,pvc无法删除问题

热门文章

  1. linux内核不识别分区,ubuntu14.04无法识别树莓派SD卡问题
  2. plsql 破解|oracle plsql 破解
  3. java面向对象程序设计实验指导答案,Java面向对象程序设计实验指导与习题解答...
  4. 易语言-VB keypress事件中键盘上每个键的KeyAscii值
  5. 通过python让打印出来的字体看起来像手写
  6. 【教程向】如何用L298N电机驱动模块与Arduino实现PWM调速
  7. 测试用例管理工具-TestLink
  8. plc编程入门视频教程
  9. 网站数据分析-Google Analytics与Webtrends数据误差
  10. 微软鼠标测试软件,微软sculpt鼠标评测 | 微软sculpt人体工学无线鼠标评测_什么值得买...