阿里Android开发规范:UI 与布局

本文摘自阿里巴巴Android开发手册,强调了UI和布局的规范,包括避免LinearLayout多重嵌套,推荐使用DialogFragment,提倡使用UTF-8编码,禁止非UI线程操作视图,以及避免ScrollView包裹ListView等,旨在提升应用性能和用户体验。

以下内容摘自 阿里巴巴Android开发手册

我们的目标是:

  • 防患未然,提升质量意识,降低故障率和维护成本;
  • 标准统一,提升协作效率;
  • 追求卓越的工匠精神,打磨精品代码。
  • 【强制】必须遵守,违反本约定或将会引起严重的后果;
  • 【推荐】尽量遵守,长期遵守有助于系统稳定性和合作效率的提升;
  • 【参考】充分理解,技术意识的引导,是个人学习、团队沟通、项目合作的方向。

阿里Android开发规范:资源文件命名与使用规范
阿里Android开发规范:四大基本组件
阿里Android开发规范:UI 与布局
阿里Android开发规范:进程、线程与消息通信
阿里Android开发规范:文件与数据库
阿里Android开发规范:Bitmap、Drawable 与动画
阿里Android开发规范:安全与其他

1、【强制】布局中不得不使用 ViewGroup 多重嵌套时,不要使用 LinearLayout 嵌套,改用 RelativeLayout,可以有效降低嵌套数。
说明:
Android 应用页面上任何一个 View 都需要经过 measure、layout、draw 三个步骤才能被正确的渲染。从 xml layout 的顶部节点开始进行 measure,每个子节点都需要向自己的父节点提供自己的尺寸来决定展示的位置,在此过程中可能还会重新measure(由此可能导致 measure 的时间消耗为原来的 2-3 倍)。节点所处位置越深,套嵌带来的 measure 越多,计算就会越费时。这就是为什么扁平的 View 结构会性能更好。同时,页面拥上的 View 越多,measure、layout、draw 所花费的时间就越久。要缩短这个时间,关键是保持 View 的树形结构尽量扁平,而且要移除所有不需要渲染的View。理想情况下,总共的 measure,layout,draw 时间应该被很好的控制在 16ms以内,以保证滑动屏幕时 UI 的流畅。要找到那些多余的 View(增加渲染延迟的 view),可以用 Android Studio Monitor里的 Hierarachy Viewer 工具,可视化的查看所有的 view。
正例:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout>
    <RelativeLayout>
        <TextView/>
        ...
        <ImageView/>
    </RelativeLayout>
</android.support.constraint.ConstraintLayout>

反例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
    <LinearLayout>
        <RelativeLayout>
            <TextView/>
            ...
            <ImageView/>
        </RelativeLayout>
    </LinearLayout>
</LinearLayout>

多重嵌套导致 measure 以及 layout 等步骤耗时过多。
扩展参考:

  1. https://developer.android.com/studio/profile/hierarchy-viewer.html
  2. http://mrpeak.cn/android/2016/01/11/android-performance-ui

2、【推荐】在 Activity 中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非Dialog/AlertDialog,这样便于随Activity生命周期管理对话框/弹出浮层的生命周期。
正例:

public void showPromptDialog(String text){
    DialogFragment promptDialog = new DialogFragment() {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
            View view = inflater.inflate(R.layout.fragment_prompt, container);
            return view;
        }
    };
    promptDialog.show(getFragmentManager(), text);
}

3、【推荐】源文件统一采用 UTF-8 的形式进行编码。
4、【强制】禁止在非 ui 线程进行 view 相关操作。
5、【推荐】文本大小使用单位 dp,view 大小使用单位 dp。对于 Textview,如果在文字大小确定的情况下推荐使用 wrap_content 布局避免出现文字显示不全的适配问题。
6 、【强制】禁止在设计布局时多次设置子 view 和父 view 中为同样的背景造成页面过度绘制,推荐将不需要显示的布局进行及时隐藏。
正例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="click it !"
        android:id="@+id/btn_mybuttom" />
    <ImageView
        android:id="@+id/img"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:visibility="gone"
        android:src="@drawable/youtube" />
    <TextView
        android:text="it is an example!"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

反例:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    int width = getWidth();
    int height = getHeight();
    mPaint.setColor(Color.GRAY);
    canvas.drawRect(0, 0, width, height, mPaint);
    mPaint.setColor(Color.CYAN);
    canvas.drawRect(0, height/4, width, height, mPaint);
    mPaint.setColor(Color.DKGRAY);
    canvas.drawRect(0, height/3, width, height, mPaint);
    mPaint.setColor(Color.LTGRAY);
    canvas.drawRect(0, height/2, width, height, mPaint);
}

7、【推荐】灵活使用布局,推荐 Merge、ViewStub 来优化布局,尽可能多的减少 UI布局层级,推荐使用 FrameLayout,LinearLayout、RelativeLayout 次之。
8、 【推荐】在需要时刻刷新某一区域的组件时,建议通过以下方式避免引发全局 layout刷新:

  1. 设置固定的 view 大小的高宽,如倒计时组件等;
  2. 调用 view 的 layout 方式修改位置,如弹幕组件等;
  3. 通过修改 canvas 位置并且调用 invalidate(int l, int t, int r, int b)等方式限定刷新区域;
  4. 通过设置一个是否允许 requestLayout 的变量,然后重写控件的 requestlayout、onSizeChanged 方法 , 判 断 控 件 的大小 没 有 改 变 的 情况下 , 当 进 入requestLayout 的时候,直接返回而不调用 super 的 requestLayout 方法。

9、【推荐】不能在 Activity 没有完全显示时显示 PopupWindow 和 Dialog。
10、【推荐】尽量不要使用 AnimationDrawable,它在初始化的时候就将所有图片加载到内存中,特别占内存,并且还不能释放,释放之后下次进入再次加载时会报错。
说明:
Android 的帧动画可以使用 AnimationDrawable 实现,但是如果你的帧动画中如果包含过多帧图片,一次性加载所有帧图片所导致的内存消耗会使低端机发生 OOM异常。帧动画所使用的图片要注意降低内存消耗,当图片比较大时,容易出现 OOM。
正例:
图片数量较少的 AnimationDrawable 还是可以接受的。

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
    <item android:duration="500" android:drawable="@drawable/ic_heart_100"/>
    <item android:duration="500" android:drawable="@drawable/ic_heart_75"/>
    <item android:duration="500" android:drawable="@drawable/ic_heart_50"/>
    <item android:duration="500" android:drawable="@drawable/ic_heart_25"/>
    <item android:duration="500" android:drawable="@drawable/ic_heart_0"/>
</animation-list>

反例:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
    <item android:drawable="@drawable/soundwave_new_1_40" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_41" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_42" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_43" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_44" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_45" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_46" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_47" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_48" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_49" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_50" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_51" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_52" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_53" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_54" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_55" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_56" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_57" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_58" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_59" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_60" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_61" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_62" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_63" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_64" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_65" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_66" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_67" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_68" android:duration="100" />
    <item android:drawable="@drawable/soundwave_new_1_69" android:duration="100" />
</animation-list>

上述如此多图片的动画就不建议使用 AnimationDrawable 了。
扩展参考:

  1. https://stackoverflow.com/questions/8692328/causing-outofmemoryerror-in-frame-by-frame-animation-in-android
  2. http://blog.youkuaiyun.com/wanmeilang123/article/details/53929484
  3. https://segmentfault.com/a/1190000005987659
  4. https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

11、【强制】不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew;因为这样会把 ListView 的所有 Item 都加载到内存中,要消耗巨大的内存和 cpu 去绘制图面。
说明:
ScrollView 中嵌套 List 或 RecyclerView 的做法官方明确禁止。除了开发过程中遇到的各种视觉和交互问题,这种做法对性能也有较大损耗。ListView 等 UI 组件自身有垂直滚动功能,也没有必要在嵌套一层 ScrollView。目前为了较好的 UI 体验,更贴近 Material Design 的设计,推荐使用 NestedScrollView。
正例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
    <android.support.v4.widget.NestedScrollView>
        <LinearLayout>
            <ImageView/>
            ...
            <android.support.v7.widget.RecyclerView/>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</LinearLayout>

反例:

<ScrollView>
    <LinearLayout>
        <TextView/>
        ...
        <ListView/>
        <TextView />
    </LinearLayout>
</ScrollView>

扩展参考:

  1. https://developer.android.com/reference/android/widget/ScrollView.html
  2. https://developer.android.com/reference/android/support/v4/widget/NestedScrollView.html

阿里Android开发规范:资源文件命名与使用规范
阿里Android开发规范:四大基本组件
阿里Android开发规范:UI 与布局
阿里Android开发规范:进程、线程与消息通信
阿里Android开发规范:文件与数据库
阿里Android开发规范:Bitmap、Drawable 与动画
阿里Android开发规范:安全与其他

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值