深入解析Android的自定义布局

原文链接:http://greenrobot.me/devpost/android-custom-layout/

写在前面的话:

这篇文章是前Firefox Android工程师(现在跳槽去Facebook了)Lucas
Rocha
所写,文中对Android中常用的四种自定义布局方案进行了很好地分析,并结合这四种Android自定义布局方案所写的示例项目讲解了它们各自的优劣以及四种方案之间的比较。看完这篇文章,也让我对Android 自定义布局有了进一步的了解,于是趁着兴头,我把它翻译成中文,原文链接在此

只要你写过Android程序,你肯定使用过Android平台内建的几个布局——RelativeLayout,LinearLayoutFrameLayout等等。 它们能帮助我们很好的构建Android UI。

这些内建的布局已经提供了很多方便的构件,但很多情况下你还是需要来定制自己的布局。

总结起来,自定义布局有两大优点:

  1. 通过减少view的使用和更快地遍历布局元素让你的UI显示更加有效率;
  2. 可以构建那些无法由已有的view实现的UI。

在这篇博文中,我将实现四种不同的自定义布局,并对它们的优缺点进行比较。它们分别是:composite view, custom
composite view, flat custom view, 和 async
custom views。

这些代码实现可以在我的github上的 android-layout-samples 项目里找到。这个app使用上面说到的四种自定义布局实现了相同的UI效果。它们使用 Picasso 来加载图片。这个app的UI只是twitter
timeline的简化版本——没有交互,只有布局。

好啦,我们先从最常见的自定义布局开始吧: composite view。

Composite View

Composite views (也被称为 compound views) 是众多将多个view结合成为一个可重用UI组件的方法中最简单的。这种方法的实现过程是这样的:

  1. 继承相关的内建的布局。
  2. 在构造函数里面填充一个 merge 布局。
  3. 初始化成员变量并通过 findViewById()指向内部view。
  4. 添加自定义的API来查询和更新view的状态。

TweetCompositeViewcode 就是一个
composite view。它继承于 RelativeLayout,并填充了 tweet_composite_layout.xmlcode 布局文件,最后向外界暴露了 update()方法来更新它在adaptercode里面的状态。

Custom Composite View

上面提到的TweetCompositeView 这种实现方式能满足大部分的情况。但是碰到某些情况就不灵了。假设你现在想要减少子视图的数量,让布局元素的便利更加有效。

这个时候我们可以回过头来看看,尽管 composite views 实现起来比较简单,但是使用这些内建的布局还是有不少的开销的——特别是 LinearLayout 和RelativeLayout这种比较复杂的容器。由于Android平台内建布局的实现,在一次布局元素遍历中,系统需要处理许多布局的结合和子视图的多次测量——LinearLayout的 layout_weight 的属性就是常见例子。

因此你可以为你的app量身定做一套子视图的计算和定位逻辑,这样的话你就可以极大的优化你的UI了。这种做法就是我接下来要介绍的 custom composite view.

顾名思义,一个 custom composite view 就是一个重写了onMeasure() 和onLayout() 方法的 composite view 。因此相比之前的composite view继承了 RelativeLayout,现在我们需要更进一步——继承更抽象的ViewGroup。

TweetLayoutViewcode 就是通过这种技术实现的。注意现在这个实现不像TweetComposiveView 继承了LinearLayout ,这也就避免了 layout_weightcode这个属性的使用了。

这个大费周折的过程通过ViewGroup’s 的measureChildWithMargins() 方法和背后的 getChildMeasureSpec() 方法计算出了每个子视图的 MeasureSpec 。

TweetLayoutView 不能正确地处理所有可能的 layout 组合但是它也不必这样。我们肯定需要根据特定需求来优化我们的自定义布局,这种方式可以让我们写出简单高效的布局代码。

Flat Custom View

如你所见,custom composite views 可以简单地通过使用ViewGroup 的API就可以实现了。大部分时候,这种实现是可以满足我们的需求的。

然而我们想更进一步的话——优化我们应用中的关键部分UI,比如 ListViews ,ViewPager等等。如果我们把所有的 TweetLayoutView 子视图合并成一个单一的自定义视图然后统一管理会怎么样呢?这就是我们接下来要讨论的 flat custom view——参看下面的图片。

左边为CUSTOM COMPOSITE VIEW ,右边是FLAT CUSTOM VIEW

flat custom view 就是一个完全自定义的 view ,它完全负责内部的子视图的计算,位置安排,绘制。所以它就直接继承了View 而不是 ViewGroup

如果你想找找现实生活中app是否存在这样的例子,很简单——开启你手机“开发者模式”里面的 “显示布局边界”选项,然后打开 Twitter, Gmail, 或者 Pocket这些app,它们在列表UI里面都采用了 flat custom view。

使用 flat custom view最主要的好处就是可以极大地压缩app 的视图层级,进而可以进行更快的布局元素遍历,最终可以减少内存占用。

Flat custom view 可以给你最大的自由,就好像你在一张白纸上面作画。但是这样的自由是有代价的:你不能使用已有的那些视图元素了,比如 TextView 和 ImageView。没错,在 Canvas 上面描绘文本的确很简单,但要你实现 ellipsizing(就是对过长的文本截断)呢?同样, 在 Canvas 上面 描绘图片确很简单,但是如何缩放呢?这些限制同样适用于touch
events, accessibility, keyboard navigation等等。

所以使用flat custom view的底线就是:只将flat custom view应用于你的app的UI核心部分,其他的就直接依赖Android平台提供的view了。

TweetElementViewcode 就是
flat custom view。为了更容易的实现它,我创建了一个小小的自定义视图框架叫做UIElement。你可以在  canvascode 这个包里找到它。

UIElement 提供了和Android平台类似的 measure/layout API 。它包含了没有图像界面的 TextView 和 ImageView ,这两个元素包含了几个必需的特性——分别参看 TextElementcodeImageElementcode 。它还拥有自己的
inflatercode ,帮助从
布局资源文件code里面实例化UIElement  。

注意: UIElement 还处于非常早期的开发阶段,所以还有很多缺陷,不过将来随着不断的改进UIElement 可能会变得非常有用。

你可能觉得TweetElementView 的代码看起来很简单,这是因为实际代码都在 TweetElementcode里面——实际上TweetElementView 扮演托管的角色code

TweetElement  里面的布局代码和TweetLayoutView‘非常类似,但是它使用 Picasso 请求图片时却不一样code ,因为TweetElement  没有使用ImageView

Async Custom View

总所周知,Android UI 框架时单线程
。 这样的单线程会带来一些限制。比如,你不能在主线程之外遍历布局元素——然而这对复杂、动态的UI是很有益处的。

假如你的app 在一个ListView 中很布局比较复杂的条目(就像大多数社交app一样),那么你在滑动ListView 就很有可能出现跳帧的现象,因为ListView 需要为列表中即将出现的新内容计算它们的视图大小code和布局code。同样的问题也会出现在GridViewsViewPagers等等。

如果我们可以在主线程之外的线程上面对那些还没有出现的子视图进行布局遍历是不是就可以解决上面的问题了?也就是说,在子视图上面调用 measure() 和layout() 方法都不会占用主线程的时间了。

所以 async custom view 就是一个允许子视图布局遍历过程发生在主线程之外的实验,这个idea是受到Facebook的Paperteam async
node framework
 这个视频激发所想到的。

既然我们在主线程之外永远接触不到Android平台的UI组件,因此我们需要一个API在不能直接接触到这个视图的前提下对这个视图的内容进行测量、布局。这恰恰就是 UIElement 框架提供给我的功能。

AsyncTweetViewcode 就是一个
async custom view。它使用了一个线程安全的 AsyncTweetElementcode 工厂类code 来定义它的内容。具体过程是一个 Smoothie 子项加载器code 在一个后台线程上对暂时不可见的AsyncTweetElement 进行创建、预测量和缓存(在内存里面,以便后来直接使用)。

当然在实现这个异步UI的过程中我还是妥协了一些,因为你不知道如何显示任意高度的布局占位符。比如,当布局异步传递过来的时候你只能在后台线程对它们的大小进行一次更改。因此当一个 AsyncTweetView 就要显示的时候却无法在内存里面找到合适的AsyncTweetElement ,这个时候框架就会强制在主线程上面创建一个AsyncTweetElement code

还有,预先加载的逻辑和内存缓存过期时间设置都需要比较好的实现来保证在主线程尽可能多地利用内存里面的缓存布局。比如,这个方案中使用 LRU 缓存code 就不是一个明智的选择。

尽管还存在这些限制,但是使用 async custom view 的得到的初步结果还是很有前途的。当然我也会通过重构这个UIElement  框架和使用其他类别的UI在这个领域继续探索。让我们静观其变吧。

总结

在我们涉及到布局的时候,我们自定义的越深,我们能从Android平台所能获得的依赖就越少。所以我们也要避免过早优化,只在确实能实实在在改善app质量和性能的区域进行完全的布局自定义。

这不是一个非黑即白的决定。在使用平台提供的UI元素和完全自定义的两种极端之间还有很多方案——从简单的composite views 到复杂的 async views。实际项目中,你可能会结合文中的几种方案写出优秀的app。

时间: 2024-10-01 07:58:39

深入解析Android的自定义布局的相关文章

深入解析_Android的自定义布局

前言: 这篇文章是前Firefox Android工程师(现在跳槽去Facebook了) Lucas Rocha所写,文中对Android中常用的四种自定义布局方案进行了很好地分析,并结合这四种Android自定义布局方案所写的示例项目讲解了它们各自的优劣以及四种方案之间的比较.看完这篇文章,也让我对Android 自定义布局有了进一步的了解,于是趁着兴头,我把它翻译成中文,原文链接在此. 只要你写过Android程序,你肯定使用过Android平台内建的几个布局——RelativeLayout

Android RecycleView自定义布局的使用

自定义布局的RecycleView需要自己实现Adapter,ViewHolder和布局: 自定义Adapter继承RecycleView.Adapter,重写getItemCount(),onBindViewHolder()和onCreateViewHolder(): 自定义ViewHolder继承于RecycleView.ViewHolder: getItemCount()返回RecycleView中Item的个数,onBindViewHolder()主要实现数据和布局的绑定,onCreat

【转】Android AlertDialog自定义布局

原文网址:https://blog.csdn.net/u010694658/article/details/53022294 由于开发中经常使用弹框,然而系统自带的弹框太局限,也不太美观,经常不能满足开发需求,所以就只能自定义布局.其实自定义布局很简单,没不要写出来,但是如果不写一遍的,后面遇到的话就感觉又会忘记,所以在次记一小笔,仅记一个最简单的例子,可以举一反三.  直接上代码 public class MainActivity extends Activity implements OnC

Android开发学习之路--UI之自定义布局和控件

新的一年已经开始了,今天已经是初二了,两天没有学习了,还是要来继续学习下.一般手机的title都是actionbar,就像iphone一样可以后退,可以编辑.这里自定义布局就来实现下这个功能,首先准备下三张图片,一张用来当作背景,两张分别表示后退和编辑.新建工程UICostomViewsTest,然后自动创建工程后,新建title.xml,编写代码如下: <?xml version="1.0" encoding="utf-8"?> <LinearL

Android:创建可穿戴应用 - 自定义布局

创建自定义布局(Creating Custom Layouts) 本文将介绍如何创建自定义通知以及使用可穿戴UI库来创建自定义布局你同时还需要了解可穿戴设计准则(Wear Design Principles)除了屏幕尺寸和瞬读能力(Glance ability)外,为可穿戴应用创建布局大体和普通手机一样. 创建自定义通知(Custom Notifications) 通常,你应该在手机应用上创建通知然后自动同步到可穿戴应用.这让你只需要构建通知一次,就可以呈现于多种设备(不只是可穿戴,最终还包括汽

源码解析Android中View的layout布局过程

Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文 < Android中View的布局及绘图机制>.量算是布局的基础,如果想了解量算的细节,可参见博文<源码解析Android中View的measure量算过程>.本文将从源码角度解析View的布局layout过程,本文会详细介绍View布局过程中的关键方法,并对源码加上了注释以进行说明. 对View进行布局的目的是计算

Android自定义布局

1.首先新建属性文件 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="topbar"> <attr name="title" format="string"/> <attr name="titleTextSize" format=&

Android自定义布局的三种实现方式

在毕设项目中多处用到自定义布局,一直打算总结一下自定义布局的实现方式,今天就来总结一下吧.在此之前学习了郭霖大神博客上面关于自定义View的几篇博文,感觉受益良多,本文中就参考了其中的一些内容. 总结来说,自定义布局的实现有三种方式,分别是:组合控件.自绘控件和继承控件.下面将分别对这三种方式进行介绍. (一)组合控件 组合控件,顾名思义就是将一些小的控件组合起来形成一个新的控件,这些小的控件多是系统自带的控件.比如很多应用中普遍使用的标题栏控件,其实用的就是组合控件,那么下面将通过实现一个简单

Android 自定义布局 性能问题 初探

大家在写android 代码的时候,基本上都使用过如下几种布局 RelativeLayout,LinearLayout, FrameLayout 但是很多时候 这几种布局 也无法满足我们的使用.于是我们会考虑用自定义布局,使用自定义布局会有几个优点 比如可以减少view的使用啊,让ui显示的更加有效率啊,以及实现一些原生控件无法实现的效果. 我们首先去github上 下载一个开源项目 https://github.com/lucasr/android-layout-samples 注意这个项目是