Github地址(完整Demo,欢迎下载)
https://github.com/zhouxu88/ItemGroup
效果图
在很多APP当中,有些页面需要填写很多资料,比如个人中心,还有一些页面,点击每个Item都要跳转页面或者弹框选择。这样的组合控件,相信大家都见过,说到这里,可能有人不服了,不就是一个线性布局或者相对布局吗,我分分钟写出来。是的,我承认你可以搞定,因为我以前也是一个个布局嵌套写出来的,但是我想说,这么多相似的重复布局,你难道真的要一个个去写吗?复制粘贴一个个改,而且有些功能是重复的,比如输入框那个清除输入内容按钮,你也去写,你累吗?这样的代码看上去可读性高吗?所以,我今天就是来和大家一起解决这个问题的,我们完全可以把这种组合控件封装起来,然后专注于处理我们的业务逻辑就好。
实现步骤
1、分析布局(以组合控件1为例子来分析)
从上面图我们可以看出,每一个Item的布局从左往右依次是:标题,输入框,清除输入的按钮,向右的箭头。前2个Item,我们点击中间输入框的时候,直接跳出来软键盘,直接输入文字内容,当有内容的时候,显示清除按钮,没有内容的时候,隐藏清除按钮。后2个Item,是带箭头的,是不可以输入的,提示我们这一项是用来跳转或者点击弹出选择框等。
上面这些个Item的布局很简单,大致结构是这样的
然后每一项都这么写一个相似的LinearLayout,然后如果没有右边的箭头,我们又把它去掉,如果我们的项目中也有这种类似的item,而且有很多,那么这个界面的layout的代码会显得很臃肿,维护起来也不清晰。
所以我根据我的项目把这种组合控件封装了一个,可能扩展性不是那么好,但是代码少,思路清晰,符合我的项目需求,你拿过去做适当调整也能用。
下面来介绍怎样封装成为一个通用的控件
2、Item的布局 item_group_layout.xml
3.首先新建一个类,继承自FrameLayout,实现对应的构造方法
我们引入刚才Item的布局,把它添加到这个FrameLayout
public class ItemGroup extends FrameLayout implements View.OnClickListener {
private LinearLayout itemGroupLayout; //组合控件的布局
private TextView titleTv; //标题
private EditText contentEdt; //输入框
private ImageView clearIv; //清楚输入内容
private ImageView jtRightIv; //向右的箭头
private ItemOnClickListener itemOnClickListener; //Item的点击事件
public ItemGroup(@NonNull Context context) {
super(context);
initView(context);
}
public ItemGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public ItemGroup(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
//初始化View
private void initView(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.item_group_layout, null);
itemGroupLayout = (LinearLayout) view.findViewById(R.id.item_group_layout);
titleTv = (TextView) view.findViewById(R.id.title_tv);
contentEdt = (EditText) view.findViewById(R.id.content_edt);
clearIv = (ImageView) view.findViewById(R.id.clear_iv);
jtRightIv = (ImageView) view.findViewById(R.id.jt_right_iv);
addView(view); //把自定义的这个组合控件的布局加入到当前FramLayout
}
}
4.属性的定义,在完成类的创建后,来自定义相关属性
首先需要在values目录下面新建一个attrs.xml文件 , 自定义相关属性,
其定义格式如下:
属性类型主要包括:
reference 引用
color 颜色
boolean 布尔值
dimension 尺寸值
float 浮点值
integer 整型值
string 字符串
enum 枚举值
5.属性的引入,在定义完属性后,接下来将定义的属性值引入到类中
先获取到各属性,然后将引入的属性值设置到布局控件上。
/**
* 初始化相关属性,引入相关属性
*
* @param context
* @param attrs
*/
private void initAttrs(Context context, AttributeSet attrs) {
//标题的默认字体颜色
int defaultTitleColor = context.getResources().getColor(R.color.item_group_title);
//输入框的默认字体颜色
int defaultEdtColor = context.getResources().getColor(R.color.item_group_edt);
//输入框的默认的提示内容的字体颜色
int defaultHintColor = context.getResources().getColor(R.color.item_group_edt);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ItemGroup);
String title = typedArray.getString(R.styleable.ItemGroup_title);
float paddingLeft = typedArray.getDimension(R.styleable.ItemGroup_paddingLeft, 15);
float paddingRight = typedArray.getDimension(R.styleable.ItemGroup_paddingRight, 15);
float paddingTop = typedArray.getDimension(R.styleable.ItemGroup_paddingTop, 5);
float paddingBottom = typedArray.getDimension(R.styleable.ItemGroup_paddingTop, 5);
float titleSize = typedArray.getDimension(R.styleable.ItemGroup_title_size, 15);
int titleColor = typedArray.getColor(R.styleable.ItemGroup_title_color, defaultTitleColor);
String content = typedArray.getString(R.styleable.ItemGroup_edt_content);
float contentSize = typedArray.getDimension(R.styleable.ItemGroup_edt_text_size, 13);
int contentColor = typedArray.getColor(R.styleable.ItemGroup_edt_text_color, defaultEdtColor);
String hintContent = typedArray.getString(R.styleable.ItemGroup_edt_hint_content);
int hintColor = typedArray.getColor(R.styleable.ItemGroup_edt_hint_text_color, defaultHintColor);
//默认输入框可以编辑
boolean isEditable = typedArray.getBoolean(R.styleable.ItemGroup_isEditable, true);
//向右的箭头图标是否可见,默认可见
boolean showJtIcon = typedArray.getBoolean(R.styleable.ItemGroup_jt_visible, true);
typedArray.recycle();
//设置数据
//设置item的内边距
itemGroupLayout.setPadding((int) paddingLeft, (int) paddingTop, (int) paddingRight, (int) paddingBottom);
titleTv.setText(title);
titleTv.setTextSize(titleSize);
titleTv.setTextColor(titleColor);
contentEdt.setText(content);
contentEdt.setTextSize(contentSize);
contentEdt.setTextColor(contentColor);
contentEdt.setHint(hintContent);
contentEdt.setHintTextColor(hintColor);
contentEdt.setFocusableInTouchMode(isEditable); //设置输入框是否可以编辑
contentEdt.setLongClickable(false); //输入框不允许长按
jtRightIv.setVisibility(showJtIcon ? View.VISIBLE : View.GONE); //设置向右的箭头图标是否可见
}
这里主要通过obtainStyledAttributes方法获取到一个TypedArray对象,然后通过TypedArray对象就可以获取到相对应定义的属性值,相关自定义View的内容,自行查阅
6、在完成上面的步骤以后,就算大体上封装好了,就可以在布局文件里面使用了
调用的xml布局文件
调用的activity
/**
* 组合控件封装(提交信息及编辑信息界面及功能)
*/
public class MainActivity extends AppCompatActivity {
private Context mContext;
private ItemGroup nameIG, idCardIG, birthdayIG, cityIG;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
initView();
}
private void initView() {
nameIG = (ItemGroup) findViewById(R.id.name_ig);
idCardIG = (ItemGroup) findViewById(R.id.id_card_ig);
birthdayIG = (ItemGroup) findViewById(R.id.select_birthday_ig);
cityIG = (ItemGroup) findViewById(R.id.select_city_ig);
birthdayIG.setItemOnClickListener(new ItemGroup.ItemOnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "点击了选择出生日期", Toast.LENGTH_SHORT).show();
}
});
cityIG.setItemOnClickListener(new ItemGroup.ItemOnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "点击了选择城市", Toast.LENGTH_SHORT).show();
}
});
}
}
扫描下面二维码或者公众号搜索Android老鸟开发经验谈,即可关注【公众号】,持续推出优秀文章