xml 布局文件看似好写,也的确好,但是大家总是百思不得其解,为啥我写的页面显示慢,效率低,扩展麻烦,有的手机会出兼容问题!这都是大家平时积累不够的锅啊,xml布局文件的书写是有规范的,你写得差一点有的手机的确没事,但是这并不代表所有都是如此,我本人就碰到过使用相对布局不规范,出现的兼容问题。还有一两层布局嵌套就能解决的问题,为啥还有人写嵌套写到让人奔溃呢,善用 xml,写好 xml,可是看一个 android 开发技术功底的事,这里记录一些我个人看到的,总结的小技巧。
目录导航
- 灵活使用 linearlayout 布局的权重
- textview 最大行数,固定显示几行,下划线,删除线
- 相对布局规范:能确定位置的,被依赖的控件写在前面
- EditView的自定义样式:光标,下划线,选中图标,选中文字颜色
- Scrollbar自定义样式
- clipChildren属性的使用:允许子View超出父View
- 点九图(.9.png)的使用
- 使用 tools:attribute 属性在 xml 中查看效果
- 快速生成 style 样式
灵活使用 linearlayout 布局的权重
- 将一个Button在一行中居中,并且Button的宽度占屏幕宽度的一半
解决方案:先看具体的xml,如下
<?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="wrap_content"
android:background="#708069"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:weightSum="1" >
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="测试按钮" />
</LinearLayout>
其中LinearLayout的android:gravity="center_horizontal"作用是让Button水平居中;LinearLayout的android:weightSum="1",Button设置android:layout_weight="0.5",则表示button占据父布局即LinearLayout一半的宽度,而LinearLayout为match_parent,因此Button会占据屏幕一半的宽度。
- 最外层是LinearLayout,里面水平从左到右放TextView A,TextView B和一个Button C,要求A居左,C居右,B紧靠A,且单行显示,如果文字过多则打点。
解决方案:为A设置android:layout_width="wrap_content",为C设置 android:layout_width="wrap_content",且 android:gravity="right|center_vertical",为B设置android:layout_width="0dp",android:layout_weight="1"和android:singleLine="true"。 很容易理解,A在左边,C在右边,然后B利用权重占据剩余的水平空间,由于设置了singleLine,因此文字如果超过一行就会打点。
-
实现下图的效果
解决方案:
(1)左右分别为TextView和ImageView(当然使用Button也可以,这不是重点),外层包一个水平的LinearLayout。使用shape为TextView和Button分别设置带圆角(左边的shape设置左上和左下圆角,右边的shape设置右上和右下圆角)和蓝色边框的背景。这样做的问题是TextView的右边框和Button的左边框都会存在,导致中间的线会是正常边框的两倍。因为shape不支持隐藏某一边的边框,因此只能另想他法,我采用了将右边Button左移动1dp(边框的宽度为1dp,或者TextView右移1dp),这样中间的两个线条就会重合,基本可以实现如图效果,但也有瑕疵,细看的话,中间的竖线由于是两条线的重合效果,会显得比边框亮一些。
(2)为外层LinearLayout设置带四个圆角和颜色的边框,LiearLayout里面水平放置2个控件,左边TextView,中间一个竖线View(width=1dp,跟边框相同宽度,高度为match_parent,颜色为边框颜色),右边是Button,然后再给TextView和Button设置shape背景,与方法(1)不同的是不要设置实体边框,因为LinearLayout已经设置了边框。这样实现的效果就比较完美了。值得注意的是需要给LinearLayout设置一个边框宽度大小的padding,不然TextView和Button的默认颜色会覆盖掉LinearLayout边框的颜色,导致看不到外层边框。
textview 最大行数,固定显示几行,下划线,删除线
- TextView文本最多显示两行,多则在末尾打点显示。
解决方案:为TextView添加如下属性:
android:maxLines="2"
android:ellipsize="end"
第一个属性表示该TextView最多可显示两行。第二个属性设为end,表示将会在末尾打点,还可以设为其他属性,如start、middle、none等,根据名字很容易就能理解了。
注意有一个android:ellipsize="marquee",即传说中的跑马灯效果,
若要让TextView里的文本滚动,必须满足以下几个因素:
(1)TextView里文本宽度超过TextView的宽度,一般需设置android:singleLine="true"
(2)android:ellipsize="marquee"
(3)只有在TextView获取到焦点时,才会滚动.所以加上android:focusableInTouchMode="true"
android:focusable="true"
滚动重复次数设置:
android:marqueeRepeatLimit="marquee_forever"
注意:有时候设置了android:maxLines="2",而文字只有一行的时候,会导致TextView跟有两行文字时高度不一致,因此常常同时设置
android:lines="2",强制占据两行的高度。
- 需求:给文字设置下划线、中划线
解决方案:如下代码分别设置了下划线和中划线
textView.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG );
textView.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
注意,所生成的线的颜色跟TextView文字的颜色是一致的,如果想把文字和下划线分别设为不同的颜色,貌似没有发现TextView有现成的方法或属性支持,我们可以采用其他策略实现,如文字和下划线使用两个不同的view来实现。
相对布局规范:能确定位置的,被依赖的控件写在前面
先来看一个布局
[图片上传失败...(image-75d901-1518453662209)]
这个布局的特点是按钮3底部对齐,按钮2在按钮3的上面,文本框水平充满剩余的区域,按钮1顶部对齐,列表框垂直充满剩余的区域。
下面我们会拆分为下面两个子问题:
水平充满剩余区域的问题:
水平方向上有两个组件,一个组件宽度为wrap_content(或者固定宽度),另外一个组件的需要充满剩余的宽度,效果如下:
[图片上传失败...(image-527c18-1518453662210)]
左侧一个文本框,右侧一个按钮
如果是嵌套一个LinearLayout布局肯定就十分简单了,如果用RelativeLayout也是可以的,如下:
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn3"
android:layout_alignParentRight="true"
android:text="按钮2"
/>
<EditText
android:id="@+id/et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/btn2"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/btn2"
/>
主要方法如下:(主要通过toRightOf和toLeftOf两个属性)
- 两个组件的布局宽度都是wrap_content(或者固定宽度)
- 左边的组件alignParentLeft,右边的组件alignParentRight(如果他们左右分别有一些固定宽度的组件,就用toRightOf(左侧)或者toLeftOf(右侧)指定的组件)
- 为了让左边的组件紧挨着右边的组件,也就是拉长左侧的文本框,设置toLeftOf="右边组件的ID" (这里是重点),这样,左侧文本框就会自动拉伸至和右侧按钮紧挨着。
- 当然,最后,两边组件要水平对齐,对左边组件用alignBaseline="右边组件的ID"
- 注意要把右侧按钮在代码中放到前面(否则编译时找不到对应的id,因为左侧EditText布局依赖右侧按钮)
垂直充满剩余空间的问题:
垂直方向上有两个组件,第一个组件的高度为wrap_content(或固定高度),另外一个组件的高度需要充满剩余的高度,效果如前面的ListView:
[图片上传失败...(image-10e696-1518453662210)]
这个时候也可以通过RelativeLayout实现,下面我贴出了整个布局的代码(为了更好的说明,我在上面和下面都加了一个组件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="固定宽度按钮"/>
<Button
android:id="@+id/btn3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="按钮3"/>
<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn3"
android:layout_alignParentRight="true"
android:text="按钮2"
/>
<EditText
android:id="@+id/et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/btn2"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/btn2"
/>
<ListView
android:id="@+id/lv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/btn2"
android:layout_below="@id/btn1"
/>
</RelativeLayout>
主要看ListView的布局,方法如下:
- 让ListView below=上面的组件
- 让ListView above=下面的组件
- 注意将ListView依赖的组件都放到ListView的上面
看前面我们给出的完整的布局文件代码,这里需要说明如下内容:
注意组件在代码中的前后摆放,不然会编译报错(前面一直在说),方法如下:
需要拉伸的组件放到后面,然后用另一个组件来进行整体的布局。也就是被依赖位置的都放到前面,那些固定长宽(指的是固定或者wrap_content且不依赖其他组件布局的)或者依赖父容器的组件都放到前面,其他组件放到后面,如前面的按钮1,按钮3,按钮2都放到了前面,因为按钮2依赖于按钮3(按钮2 above 按钮3),所以按钮2也放到了按钮3的后面。 (按钮1说的是最上面那个固定高度按钮)
让两个组件水平中线对齐的问题:
让一个组件和另外一个组件中线对齐:让其中一个组件足够高(或者我们知道哪个组件高度一定是高于顶一个组件的),然后让需要对齐的组件和这个没有内容的组件上下皆对齐(alignTop,alignBottom设置),然后将第二个组件的gravity为垂直居中即可。
如果是有 baseline 属性的 TextView,Button 这类显示文本的组件的话,可以通过 align_Baseline 来进行对齐,这里的 baseline指的是文本底部对其的,就像我们小时候拿一把尺子,然后比着尺子写字那种意思。
EditView的自定义样式:光标,下划线,选中图标,选中文字颜色
首先看看默认样式(SdkVersion=23,安卓6.0):
文字选择操作时:
文字选中时:
1.修改光标颜色
修改光标的颜色很简单,只需要使用android:textCursorDrawable="XXX" 属性。
首先我们自定义drawable,cursor.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#9bd435"/> <!--颜色设置为浅绿色-->
<size android:width="2dp"/>
</shape>
使用:
<EditText
android:layout_width="match_parent"
android:textCursorDrawable="@drawable/cursor"
android:layout_height="50dp" />
效果图:
2.修改选中图标
这个图标就是默认样式的2图与3图中的墨绿色水滴状图标。同样也很简单,直接上代码。
<EditText
android:layout_width="match_parent"
android:textCursorDrawable="@drawable/cursor"
android:textSelectHandleLeft="@drawable/icon"
android:textSelectHandleRight="@drawable/icon"
android:textSelectHandle="@drawable/icon"
android:layout_height="50dp" />
效果:
是不是还觉得有点别扭,文字的选中颜色与EditView默认的下划线还是墨绿色,其实改起来也很简单。加上下面两行代码。
android:backgroundTint="#9bd435" <!--下划线颜色-->
android:textColorHighlight="#9bd435" <!--选中文字背景色-->
最终自定义效果:
使用Material Design主题属性
首先了解一下Material Design 各个属性。这里有张在网上找来的图(感谢),此图一目了然。
那么其实就简单了,代码如下
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorAccent">#9bd435</item>
<item name="android:colorControlActivated">#9bd435</item>
<item name="android:colorControlNormal">#cccccc</item>
</style>
补充一下:android:colorControlActivated表示EditText、Switch、CheckBox、RadioButton等控件激活时候的颜色。android:colorAccent与android:colorControlActivated作用一样,但是它可以同时设置文字选中颜色。android:colorControlNormal表示EditText、Switch、CheckBox、RadioButton等控件默认时的颜色。兼容5.0以下必须使用Theme.AppCompat主题(兼容部分效果),并且Activity要继承AppCompatActivity,在兼容时候需要把前面的android:前缀去掉。
效果图:
我看了下自己手机中的部分应用,发现使用1、2方法去自定义的只有UC浏览器,其中微信和淘宝直接使用的默认样式。支付宝使用了3方法,毕竟简单,效果也不错。
Scrollbar自定义样式
首先看看默认样式(SdkVersion=23,安卓6.0):
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_height="wrap_content">
......
</ScrollView>
效果图:
自定义滚动条首先我们要自定义drawable,scrollbar.xml自定义代码:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#9bd435"/>
<corners android:radius="2dp" />
</shape>
使用scrollbarThumbVertical:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:scrollbarSize="4dp"
android:scrollbars="vertical"
android:scrollbarThumbVertical="@drawable/scrollbar"
android:layout_height="wrap_content">
......
</ScrollView>
效果图:
当然水平方向的滚动条也是可以自定义的,同时这些也都适用于ListView、RecyclerView。
android:scrollbars="horizontal"
android:scrollbarThumbHorizontal="xxx"
最后还有一个android:scrollbarStyle="xxx",可以设置滚动条的位置。默认是insideOverlay,下面我直接上相应设置对应的效果图。
insideInset:(位置在padding内,会插入在View后面,不会遮挡View)
outsideOverlay:(位置在padding外,覆盖在View上,如果滚动条比padding大会遮挡View)
outsideInset:(位置在padding外,会插入在View后面,不会遮挡View)
最后两张图可能乍一看是一样的,其实仔细看button距滚动条的位置其实是不一样的。
3.修改滑动尽头阴影颜色
默认阴影如图:(SdkVersion=23,安卓6.0)
修改颜色有一种简单方法,使用Material Design主题属性colorPrimary,代码效果如下:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#9bd435</item>
</style>
当然,如果你想去除阴影也非常简单,加上android:overScrollMode="never" 属性即可。
clipChildren属性的使用:允许子View超出父View
android:clipChildren的意思是是否允许子View超出父View。好像有点懵,那我们直接上例子。
图中是现在大多外卖app都会有的一个购物车效果。其中红框中的部分高度略高于旁边的View。那么这时就可以使用clipChildren来实现。首先在布局根节点设置android:clipChildren="false",在使用android:layout_gravity="xxx"控制超出部分。
代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"> <!--这里-->
<LinearLayout
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal">
<RelativeLayout
android:layout_gravity="bottom" <--这里
android:layout_marginLeft="10dp"
android:layout_width="48dp"
android:layout_height="60dp">
<ImageView
android:src="@drawable/icon_cart"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_marginTop="6dp"
android:layout_alignParentRight="true"
android:background="@drawable/icon_spot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="1"
android:textSize="12sp"
android:gravity="center"
android:textColor="#ffffff"/>
</RelativeLayout>
<TextView
android:layout_marginLeft="10dp"
android:textColor="#9bd435"
tools:text="¥5.00"
android:textStyle="bold"
android:textSize="18sp"
android:gravity="center_vertical"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
<TextView
android:layout_width="110dp"
android:textColor="#ffffff"
android:gravity="center"
android:textSize="16sp"
android:text="去购物车"
android:background="#9bd435"
android:layout_height="match_parent" />
</LinearLayout>
</RelativeLayout>
效果图:
点九图(.9.png)的使用
接着上面的购物车效果,在图中是不是有一个代表购买商品数量的数字。如果此时一个土豪一次买了上百份的外卖,上面的效果会如何?我就试了试,得到了下面的效果:
可以清楚地看到原本的圆形被横向拉伸了。。。那就说明这个圆形图标不是点九图。那么我们来制作张。
大家使用Studio可以很方便的去制作,首先右键图片,会弹出以下菜单:
点击Create 9-Patch file... 创建点九图片。
上图就是最终完成的图片,在上面我有标注各个位置的含义。
替换图片后现在再来看看效果:
使用 tools:attribute 属性在 xml 中查看效果
xml 中的 tools 属性是一个测试用的辅助属性,设置了 tools 的属性并不会真是的去给这个属性值赋值,而仅仅是用来在 xml 观察效果的
快速生成 style 样式
以前我们写 style 样式的样式是一样一样复制过去的,麻烦的要死,但是呢 AS 这个 IDE 开发工具是无比强大的,其实可以快速生成 style 样式的。右键打开菜单,依次选择Refactor -> Extract -> Style ,下面看我的图就明白了
我们先正常编写一个 view ,然后在这个 view 的位置点击右键,然后如上面的操作,就会出现一个生成 style 样式的确认页,可以选择相关的属性,给 style 命名。是不是很方面