下图为上图中选中的第一个条目UI的布局代码:
看第一张图,我们发现, 这个界面中有五个大致相同的UI,而从上段代码中我们发现,单单其中一个UI的布局代码还是有点多的,试想,如果五个这样的UI的布局代码都写在一个xml文件中,那么这个xml文件看起来就很冗长,而且代码的复用性很不好。遇到这种情况,我们就要想办法把这些重复的代码抽取出来,让需要的UI复用即可,这就涉及到自定义组合式控件(这里的组合式控件由一个TextView和一个ImageView构成)了。
一.自定义组合式控件步骤:
1.新建公共的xml布局文件
项目layout目录下新建一个布局xml文件:view_setting_item.xml(注意:因为我们的组合式控件最外层的容器是RelativeLayout,所以新建的这个xml布局文件的根容器也要是RelativeLayout,其他同理),将上述重复的代码段复制到该xml文件中,如下:
<?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="wrap_content"
android:paddingBottom="8dp"
android:paddingLeft="15dp"
android:paddingRight="15dp"
android:paddingTop="8dp" >
<TextView
android:id="@+id/view_tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="自动更新"
android:textSize="18sp" />
<ImageView
android:id="@+id/view_iv_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/on" />
</RelativeLayout>
2.新建加载公共xml布局文件的view类
创建一个Java类:SettingItemView,该类继承我们的组合式控件最外层的布局容器,这里是RelativeLayout,创建两个构造方法,在有两个参数的构造器中将我们新建的包含重复布局代码的xml加载出来:
3.copy qualified name
将该SettingItemView类copy qualified name给xml中相关的组合式控件:
从图中可见,该界面的布局文件清爽多了。
4.渲染结果
到这里,自定义组合式控件就实现得差不多了,现在要解决的就是怎么样实现各个自定义组合式控件中的差异化显示了。从第一张图中我们发现,前面两个组合式控件之间主要是文字显示和控件的形状这两个属性不同,要实现自定义组合式控件中的差异化显示,这就涉及到接下来的自定义属性了。
二.自定义属性
1.实现自定义组合式控件显示不同的文字步骤
1.1,在项目res中的value目录下新建attrs.xml文档,文档中申明declare-styleable节点,在这个节点下创建attr节点,这里一个attr节点就代表我们的一个自定义属性,eg:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SettingItemView">
<!-- 显示不同的title内容 -->
<attr name="title" format="string" />
</declare-styleable>
</resources>
注意:declare-styleable节点上的name属性值一般就是我们自定义的控件类名。
attr中又有两个属性,其中,name的值就是我们这个自定义属性的名字,format的值就是我们这个自定义属性的值的数据类型
关于自定义属性的值的数据类型,常用的有以下几种:
(1)color: 颜色
(2) float: 小数–> 10.5
(3) boolean
(4) reference: 引用类型
(5) dimension: 尺寸类型 10.5dp
(6)string: 文本类型
我们这里要实现的是不同的自定义组合式控件显示不同的文字,即自定义的属性值是文字,所以 format=“string”。
**1.2 **定义好了自定义属性后,接下来就是为自定义的组合式控件添加该自定义属性了:
1.3 在view类中获取显示自定义属性值的控件(下图中的步骤A),然后从自定义属性集合set中通过declare-styeable的name值获取为该view类定义的自定义属attr 性数组(下图中的步骤B),再通过declare-styeable的name值加下划线加attr的name值(即SettingItemView_title)从上步的自定义属性数组中获得该自定义属性 attr的属性值(即1.2步中赋的值),然后将该值赋值给相应的控件即可(下图中的步骤C).
这一步主要注意的是上图中是如何指定要获取的那个自定义属性的,见蓝色框框内。
2.自定义属性详解
2.1.文字大小
<declare-styleable name="ViewGroupName">
<attr name="text_size" format="dimension|reference"/>
</declare-styleable>
获取设置的大小并显示
int textSize = typedArray.getDimensionPixelSize(R.styleable
.ViewGroupName_text_size, dp2px(14));
.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public static int dp2px(float dpValue) {
return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
}
2.2.文字颜色
<declare-styleable name="ViewGroupName">
<attr name="text_color" format="color"/>
</declare-styleable>
获取并显示
int textColor = typedArray.getColor(R.styleable.ViewGroupName_text_color, Color.parseColor
("#ff000000"));
.setTextColor(textColor);
.setHintTextColor(textColor);
2.3.文字内容
<declare-styleable name="ViewGroupName">
<attr name="text_content" format="string"/>
</declare-styleable>
获取并显示
String textContent = typedArray.getString(R.styleable.ViewGroupName_text_content);
mLeftTv.setText(textContent);
2.4.输入框输入类型
<declare-styleable name="ViewGroupName">
<attr name="edit_input_type">
<!--数字,不带小数-->
<flag name="TYPE_CLASS_NUMBER" value="0x00000002"/>
<!--数字,可带小数-->
<flag name="TYPE_NUMBER_FLAG_DECIMAL" value="0x00002000"/>
<!--移动号码-->
<flag name="TYPE_CLASS_PHONE" value="0x00000003"/>
</attr>
</declare-styleable>
获取并显示
int inputType = typedArray.getInteger(R.styleable.ViewGroupName_edit_input_type, InputType
.TYPE_CLASS_TEXT);
.setInputType(inputType);
2.5.输入框最大输入长度
<declare-styleable name="ViewGroupName">
<attr name="input_maxlength" format="integer"/>
</declare-styleable>
获取并显示
int inputMaxLength = typedArray.getInteger(R.styleable.ViewGroupName_input_maxlength, Integer
.MAX_VALUE);
.setFilters(new InputFilter[]{new InputFilter.LengthFilter(inputMaxLength)});
题外话:限制输入框中的小数点位数
//限制两位小数
mEtMoney.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(2)});
public class DecimalDigitsInputFilter implements InputFilter {
private final int decimalDigits;
/**
* Constructor.
*
* @param decimalDigits maximum decimal digits
*/
public DecimalDigitsInputFilter(int decimalDigits) {
this.decimalDigits = decimalDigits;
}
@Override
public CharSequence filter(CharSequence source,
int start,
int end,
Spanned dest,
int dstart,
int dend) {
int dotPos = -1;
int len = dest.length();
for (int i = 0; i < len; i++) {
char c = dest.charAt(i);
if (c == '.' || c == ',') {
dotPos = i;
break;
}
}
if (dotPos >= 0) {
// protects against many dots
if (source.equals(".") || source.equals(",")) {
return "";
}
// if the text is entered before the dot
if (dend <= dotPos) {
return null;
}
if (len - dotPos > decimalDigits) {
return "";
}
}
return null;
}
}
2.6.子控件是否可见
<declare-styleable name="ViewGroupName">
<attr name="is_visable" format="boolean"/>
</declare-styleable>
获取并显示
boolean isVisable = typedArray.getBoolean(R.styleable.ViewGroupName_is_visable, true);
if (isVisable){
.setVisibility(VISIBLE);
}else {
.setVisibility(GONE);
}
2.7.显示图片资源
<declare-styleable name="ViewGroupName">
<attr name="image_drawable" format="reference|integer"/>
</declare-styleable>
获取并显示
int drawableId = typedArray.getResourceId(R.styleable.ViewGroupName_image_drawable, R
.mipmap.ic_launcher);
.setImageResource(drawableId);