原文地址:http://blog.csdn.net/qq_17766199/article/details/52863741
今天分享一些layout布局书写中的一些技巧,希望看过之后你也一样可以写出性价比高的布局。我个人的目标是用最少的View写出一样效果的布局。因为我相信View的数量减少伴随着的就是层级的减少。从而达到结构清晰,渲染速度快的效果。顺着这个逻辑,我将优化分为重用、合并、按需载入。
重用< include/>
合并 减少嵌套
首先我们心中要有一个大原则:尽量保持布局层级的扁平化。在这个大原则下我们要知道:
在不影响层级深度的情况下,使用LinearLayout而不是RelativeLayout。因为RelativeLayout会让子View调用2次onMeasure,LinearLayout 在有weight时,才会让子View调用2次onMeasure。Measure的耗时越长那么绘制效率就低。
如果非要是嵌套,那么尽量避免RelativeLayout嵌套RelativeLayout。这简直就是恶性循环,丧心病狂。
实现方法就不细说了,大家都是明白人。
< merge/>
< merge/>主要用来去除不必要的FrameLayout。它的使用最理想的情况就是你的根布局是FrameLayout,同时没有使用background等属性。这时可以直接替换。因为我们布局外层就是FrameLayout,直接“合并”。
举例说明:比如上面用到的activity_main.xml文件,我们通过View Hierarchy工具看一下,如图:
可以看到,最外层是FrameLayout,下来我们修改一下。
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<merge
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent">
-
-
<include
-
layout= "@layout/title_bar"/>
-
-
</merge>
再次查看:
很明显少了一层RelativeLayout,当然运行效果是一样的。当然如果我们不需要title_bar.xml中的绿色背景,那么可以这样修改。
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<merge
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent">
-
-
<ImageView
-
android:layout_width= "wrap_content"
-
android:layout_height= "48dp"
-
android:paddingLeft= "15dp"
-
android:paddingRight= "15dp"
-
android:src= "@drawable/icon_back_1"/>
-
-
<TextView
-
android:layout_gravity= "center_horizontal"
-
android:text= "标题"
-
android:gravity= "center"
-
android:layout_width= "wrap_content"
-
android:layout_height= "48dp"
-
android:layout_centerInParent= "true"
-
android:textSize= "18sp"
-
android:textColor= "@color/black" />
-
-
<TextView
-
android:text= "确定"
-
android:layout_gravity= "right"
-
android:layout_width= "wrap_content"
-
android:gravity= "center"
-
android:layout_height= "48dp"
-
android:layout_alignParentRight= "true"
-
android:paddingLeft= "15dp"
-
android:paddingRight= "15dp"
-
android:textSize= "16sp"
-
android:textColor= "@color/black" />
-
-
</merge>
运行效果:
运行查看层级,如下图:
结果很明显。
用TextView同时显示图片和文字
这个我就不细说了,举一个我们项目中的一个例子,代码一看便知。
首先要完成的效果是如下图:
这种效果很常见,一般实现方法是这样。(貌似没人这样写吧,哈哈)
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent">
-
-
<LinearLayout
-
android:orientation= "horizontal"
-
android:background= "@color/white"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp">
-
-
<ImageView
-
android:layout_marginLeft= "10dp"
-
android:layout_width= "wrap_content"
-
android:src= "@drawable/icon_1"
-
android:layout_height= "match_parent" />
-
-
<TextView
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "我的卡券"
-
android:gravity= "center_vertical"
-
android:layout_width= "0dp"
-
android:layout_weight= "1"
-
android:layout_height= "match_parent" />
-
-
<ImageView
-
android:layout_marginRight= "10dp"
-
android:src= "@drawable/icon_4"
-
android:layout_width= "wrap_content"
-
android:layout_height= "match_parent"/>
-
</LinearLayout>
-
-
</LinearLayout>
效果图:
那么我们优化一下:
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent">
-
-
<TextView
-
android:drawableLeft= "@drawable/icon_1"
-
android:drawableRight= "@drawable/icon_4"
-
android:drawablePadding= "10dp"
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "我的卡券"
-
android:background= "@color/white"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp" />
-
-
</LinearLayout>
setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)
可以让我们动态去设置图片。
使用TextView的行间距
先上我们需要实现的效果图:
效果很简单,实现代码:
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_height= "100dp"
-
android:background= "@color/white"
-
android:layout_width= "match_parent"
-
xmlns:tools= "http://schemas.android.com/tools">
-
-
<ImageView
-
android:padding= "25dp"
-
android:src= "@drawable/kd_1"
-
android:layout_width= "100dp"
-
android:layout_height= "100dp"/>
-
-
<LinearLayout
-
android:layout_width= "match_parent"
-
android:orientation= "vertical"
-
android:layout_height= "100dp">
-
-
<TextView
-
tools:text= "揽件方式:上门取件"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "25dp"/>
-
-
<TextView
-
tools:text= "快递公司:顺丰快递"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "25dp"/>
-
-
<TextView
-
tools:text= "预约时间:9月6日 立即取件"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "25dp"/>
-
-
<TextView
-
tools:text= "快递费用:等待称重确定价格"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "25dp"/>
-
-
</LinearLayout>
-
-
</LinearLayout>
这里我偷懒了多嵌套了一层LinearLayout,但。。。这不重要,我先直接修改。
优化后代码:
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_height= "100dp"
-
android:background= "@color/white"
-
android:layout_width= "match_parent">
-
-
<ImageView
-
android:padding= "25dp"
-
android:src= "@drawable/kd_1"
-
android:layout_width= "100dp"
-
android:layout_height= "match_parent"/>
-
-
<TextView
-
android:textSize= "14dp"
-
android:lineSpacingExtra= "8dp"
-
android:gravity= "center_vertical"
-
android:text= "揽件方式:上门取件\n快递公司:顺丰快递\n预约时间:9月6日 立即取件\n快递费用:等待称重确定价格"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent" />
-
-
</LinearLayout>
老规矩,效果一样一样的。可以看到我们仅仅利用
Android:lineSpacingExtra="8dp"
这一行代码就省去了3个TextView,如果行数更多呢?是不是方便多了。
其中:lineSpacingExtra
属性代表的是行间距,他默认是0,是一个绝对高度值。同时还有lineSpacingMultiplier
属性,它代表行间距倍数,默认为1.0f,是一个相对高度值。我们来使用一下:
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_height= "100dp"
-
android:background= "@color/white"
-
android:layout_width= "match_parent">
-
-
<ImageView
-
android:padding= "25dp"
-
android:src= "@drawable/kd_1"
-
android:layout_width= "100dp"
-
android:layout_height= "100dp"/>
-
-
<TextView
-
android:textSize= "14dp"
-
android:lineSpacingMultiplier= "1.3"
-
android:gravity= "center_vertical"
-
android:text= "揽件方式:上门取件\n快递公司:顺丰快递\n预约时间:9月6日 立即取件\n快递费用:等待称重确定价格"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent" />
-
-
</LinearLayout>
当然了这两条属性可以同时使用,查看源码可以知道,他们的高度计算规则为mTextPaint.getFontMetricsInt(null) * 行间距倍数 + 行间距。
使用Spannable或Html.fromHtml
这里也是举例说明,比如下图效果:
如果实现上图红框中的效果,笨办法就是写三个TextView,“¥”,“价格”,“门市价”分别实现,其实用一个TextVIew就可以实现,类似如下代码:
-
String text = String.format( "¥%1$s 门市价:¥%2$s", 18.6, 22);
-
int z = text.lastIndexOf( "门");
-
SpannableStringBuilder style = new SpannableStringBuilder(text);
-
style.setSpan( new AbsoluteSizeSpan(DisplayUtil.dip2px(mContext, 14)), 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); //字号
-
style.setSpan( new ForegroundColorSpan(Color.parseColor( "#afafaf")), z, text.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); //颜色
-
style.setSpan( new AbsoluteSizeSpan(DisplayUtil.dip2px(mContext, 14)), z, text.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); //字号
-
tv.setText(style);
同样Html.fromHtml也可以实现。这样不就减少了两个TextView了。
按需载入
ViewStub
在开发中经常会遇到这样的情况,会在程序运行时动态根据条件来决定显示哪个View或某个布局。那么通常做法就是把用到的View都写在布局中,然后在代码中动态的更改它的可见性。但是它的这样仍然会创建View,会影响性能。
这时就可以用到ViewStub了,ViewStub是一个轻量级的View,不占布局位置,占用资源非常小。
例子:比如我们请求网络加载列表,如果网络异常或者加载失败我们可以显示一个提示View,上面可以点击重新加载。当然一直没有错误时,我们就不显示。
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<merge xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent" >
-
……
-
<ViewStub
-
android:layout_gravity= "center"
-
android:id= "@+id/hint_view"
-
android:layout_width= "match_parent"
-
android:inflatedId= "@+id/hint_view"
-
android:layout_height= "wrap_content"
-
android:layout= "@layout/hint_view"/>
-
</merge>
hint_view.xml就是这个提示View,可以根据情况自己写。
用法:
-
private View hintView;
-
-
if (网络异常。。。) {
-
if (hintView == null) {
-
ViewStub viewStub = (ViewStub) this.findViewById(R.id.hint_view);
-
hintView = viewStub.inflate();
-
TextView textView = (TextView) hintView.findViewById(R.id.tv);
-
textView.setText( "网络异常!");
-
}
-
hintView.setVisibility(View.VISIBLE);
-
} else{
-
if (hintView != null) {
-
hintView.setVisibility(View.GONE);
-
}
-
}
用法很简单,记得一旦ViewStub可见或是被inflate了,ViewStub就不存在了,取而代之的是被inflate的Layout。所以它也被称做惰性控件。
其他小技巧
用LinearLayout自带的分割线
还记得上文用TextView同时显示图片和文字中的例子吗?我们可以看到每个条目之间都是有一根分隔线的,那么怎么实现呢?别人我不知道,反正我原来是用一个View设置高度实现的。相信一定有人和我一样。
那么老办法我就不演示了,直接上代码:
-
<LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent"
-
android:orientation= "vertical"
-
<span style= "color:#FF0000;"> android:divider= "@drawable/divider"
-
android:showDividers= "middle"</span>>
-
-
<TextView
-
android:drawableLeft= "@drawable/icon_1"
-
android:drawableRight= "@drawable/icon_4"
-
android:drawablePadding= "10dp"
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "我的卡券"
-
android:background= "@color/white"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp" />
-
-
<TextView
-
android:drawableLeft= "@drawable/icon_2"
-
android:drawableRight= "@drawable/icon_4"
-
android:drawablePadding= "10dp"
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "地址管理"
-
android:background= "@color/white"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp" />
-
-
<TextView
-
android:drawableLeft= "@drawable/icon_3"
-
android:drawableRight= "@drawable/icon_4"
-
android:drawablePadding= "10dp"
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "检查更新"
-
android:background= "@color/white"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp" />
-
-
</LinearLayout>
效果图:
其中divider.xml是分隔线样式。
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<shape xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:shape= "rectangle">
-
-
<size android:width= "1dp"
-
android:height= "1dp"/>
-
-
<solid android:color= "#e1e1e1"/>
-
-
</shape
还有dividerPadding属性这里没有用到,意思很明确给divider添加padding。感兴趣可以试试。
Space控件
还是接着上面的例子,如果要给条目中间添加间距,怎么实现呢?当然也很简单,比如添加一个高10dp的View,或者使用android:layout_marginTop="10dp"
等方法。但是增加View违背了我们的初衷,并且影响性能。使用过多的margin其实会影响代码的可读性。
这时你就可以使用Space,他是一个轻量级的。我们可以看下源码:
可以看到在draw方法没有绘制任何东西,那么性能也就几乎没有影响。
-
<?xml version= "1.0" encoding= "utf-8"?>
-
<LinearLayout
-
xmlns:android= "http://schemas.android.com/apk/res/android"
-
android:layout_width= "match_parent"
-
android:layout_height= "match_parent"
-
android:orientation= "vertical"
-
android:divider= "@drawable/divider"
-
android:showDividers= "middle|beginning|end">
-
-
<TextView
-
android:drawableLeft= "@drawable/icon_1"
-
android:drawableRight= "@drawable/icon_4"
-
android:drawablePadding= "10dp"
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "我的卡券"
-
android:background= "@color/white"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp" />
-
-
<TextView
-
android:drawableLeft= "@drawable/icon_2"
-
android:drawableRight= "@drawable/icon_4"
-
android:drawablePadding= "10dp"
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "地址管理"
-
android:background= "@color/white"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp" />
-
-
<Space
-
android:layout_width= "match_parent"
-
android:layout_height= "15dp"/>
-
-
<TextView
-
android:drawableLeft= "@drawable/icon_3"
-
android:drawableRight= "@drawable/icon_4"
-
android:drawablePadding= "10dp"
-
android:paddingLeft= "10dp"
-
android:paddingRight= "10dp"
-
android:textSize= "16sp"
-
android:text= "检查更新"
-
android:background= "@color/white"
-
android:gravity= "center_vertical"
-
android:layout_width= "match_parent"
-
android:layout_height= "50dp" />
-
-
</LinearLayout>
防止过度绘制
这个完全可以看鸿洋大神这篇 《Android UI性能优化实战 识别绘制中的性能问题》:http://blog.csdn.net/lmj623565791/article/details/45556391
参考
总结
最后想想如果没有用这些技巧,我们要写多少代码,多少View?效果是不是杠杠的!其实上面说了这么多,具体的情景使用还是要看项目的具体情况,在性能面前有些还是要取舍的,但千万不能为了优化而优化。优化也是不断积累的过程,不要指望立竿见影。愿大家都能写出一手漂亮的布局。最后觉得不错的点个赞哈!