Android自定义控件之自定义组合控件
前言:
前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一)、自定义属性Android自定义控件之自定义属性(二)。今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发成本,以及维护成本。
自定义控件相关文章地址:
使用自定义组合控件的好处?
我们在项目开发中经常会遇见很多相似或者相同的布局,比如APP的标题栏,我们从三种方式实现标题栏来对比自定义组件带来的好处,毕竟好的东西还是以提高开发效率,降低开发成本为导向的。
1.)第一种方式:直接在每个xml布局中写相同的标题栏布局代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:lee="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <RelativeLayout
android:layout_width="match_parent"
android:background="@color/green"
android:layout_height="45dp"> <Button
android:id="@+id/title_bar_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:background="@mipmap/titlebar_back_icon"
android:minHeight="45dp"
android:minWidth="45dp"
android:textSize="14sp" /> <TextView
android:id="@+id/title_bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="登录"
android:singleLine="true"
android:textSize="17sp" /> <Button
android:id="@+id/title_bar_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="7dp"
android:text="提交"
android:textColor="@android:color/white"
android:background="@null"
android:minHeight="45dp"
android:minWidth="45dp"
android:textSize="14sp" />
</RelativeLayout> </LinearLayout>
这种方式没有任何布局复用的概念,同时也让当前的布局变得臃肿难以维护,开发效率低下,而且这个还需要要求每个开发人员必须细心否则有可能会做出参差不齐的标题栏,所以这种方式是最不推荐使用的。
2.)第二种方式:使用include标签
首先定义标题栏布局
<RelativeLayout
android:layout_width="match_parent"
android:background="@color/green"
android:layout_height="45dp"> <Button
android:id="@+id/title_bar_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:minHeight="45dp"
android:minWidth="45dp"
android:textSize="14sp" /> <TextView
android:id="@+id/title_bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:singleLine="true"
android:textSize="17sp" /> <Button
android:id="@+id/title_bar_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="7dp"
android:background="@null"
android:minHeight="45dp"
android:minWidth="45dp"
android:textSize="14sp" />
</RelativeLayout>
然后在需要的地方通过include标签实现引用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:lee="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <include layout="@layout/view_title_bar" /> </LinearLayout>
通过上面的布局代码,我们可以使用上面这种方式确实实现了布局的复用,而且也避免了开发人员开发出参差不齐标题栏的问题,但是同时也引入了新的问题,比如更加降低了开发效率,加大了开发成本,问题就在我们该如何为每个布局文件定义标题栏?只有通过代码的方式来设置标题问题,左右按钮等其他的属性,导致布局属性和Activity代码耦合性比较高,所以这种方式也不是推荐的方式。
3.)第三种方式:通过自定义组合控件
这里先不具体介绍如何实现一个自定义组合控件,这里先介绍一下自定义组合控件带来的好处。
- 提高布局文件开发效率
- 降低布局文件维护成本
- 降低布局文件和Activity代码耦合性
- 容易扩展
- 简单易用
如何实现一个自定义组合控件
1.)先定义一个布局文件
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:id="@+id/title_bar_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:background="@null"
android:minHeight="45dp"
android:minWidth="45dp"
android:textSize="14sp" />
<TextView
android:id="@+id/title_bar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:singleLine="true"
android:textSize="17sp" />
<Button
android:id="@+id/title_bar_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="7dp"
android:background="@null"
android:minHeight="45dp"
android:minWidth="45dp"
android:textSize="14sp" />
</merge>
注意:这里为何要使用merge标签,自定义组合控件时会继承RelativeLayout、LinearLayout等控件,这样导致布局的层级无形中增加了一层,如下是对比:
未使用merge标签

使用merge标签

2.)定义自定义属性
比如标题文字、标题栏左边按钮图标等。
<declare-styleable name="CustomTitleBar">
<attr name="title_background_color" format="reference|integer" />
<attr name="left_button_visible" format="boolean" />
<attr name="right_button_visible" format="boolean" />
<attr name="title_text" format="string" />
<attr name="title_text_color" format="color" />
<attr name="title_text_drawable" format="reference|integer" />
<attr name="right_button_text" format="string" />
<attr name="right_button_text_color" format="color" />
<attr name="right_button_drawable" format="reference|integer" />
<attr name="left_button_text" format="string" />
<attr name="left_button_text_color" format="color" />
<attr name="left_button_drawable" format="reference|integer" />
</declare-styleable>
3.)自定义一个View根据需求继承不同的ViewGroup子类,比如:RelativeLayout、LinearLayout等,我们这里继承RelativeLayout。
public class CustomTitleBar extends RelativeLayout {
private Button titleBarLeftBtn;
private Button titleBarRightBtn;
private TextView titleBarTitle;
public CustomTitleBar(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.custom_title_bar, this, true);
titleBarLeftBtn = (Button) findViewById(R.id.title_bar_left);
titleBarRightBtn = (Button) findViewById(R.id.title_bar_right);
titleBarTitle = (TextView) findViewById(R.id.title_bar_title);
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.CustomTitleBar);
if (attributes != null) {
//处理titleBar背景色
int titleBarBackGround = attributes.getResourceId(R.styleable.CustomTitleBar_tlb_title_background_color, Color.GREEN);
setBackgroundResource(titleBarBackGround);
//先处理左边按钮
//获取是否要显示左边按钮
boolean leftButtonVisible = attributes.getBoolean(R.styleable.CustomTitleBar_tlb_left_button_visible, true);
if (leftButtonVisible) {
titleBarLeftBtn.setVisibility(View.VISIBLE);
} else {
titleBarLeftBtn.setVisibility(View.INVISIBLE);
}
//设置左边按钮的文字
String leftButtonText = attributes.getString(R.styleable.CustomTitleBar_tlb_left_button_text);
if (!TextUtils.isEmpty(leftButtonText)) {
titleBarLeftBtn.setText(leftButtonText);
//设置左边按钮文字颜色
int leftButtonTextColor = attributes.getColor(R.styleable.CustomTitleBar_tlb_left_button_text_color, Color.WHITE);
titleBarLeftBtn.setTextColor(leftButtonTextColor);
}
//设置左边图片icon 这里是二选一 要么只能是文字 要么只能是图片
int leftButtonDrawable = attributes.getResourceId(R.styleable.CustomTitleBar_tlb_left_button_drawable, R.mipmap.titlebar_back_icon);
if (leftButtonDrawable != -1) {
titleBarLeftBtn.setCompoundDrawablesWithIntrinsicBounds(leftButtonDrawable, 0, 0, 0); //设置到哪个控件的位置()
}
//处理标题
//先获取标题是否要显示图片icon
int titleTextDrawable = attributes.getResourceId(R.styleable.CustomTitleBar_tlb_title_text_drawable, -1);
if (titleTextDrawable != -1) {
titleBarTitle.setBackgroundResource(titleTextDrawable);
} else {
//如果不是图片标题 则获取文字标题
String titleText = attributes.getString(R.styleable.CustomTitleBar_tlb_title_text);
if (!TextUtils.isEmpty(titleText)) {
titleBarTitle.setText(titleText);
}
//获取标题显示颜色
int titleTextColor = attributes.getColor(R.styleable.CustomTitleBar_tlb_title_text_color, Color.WHITE);
titleBarTitle.setTextColor(titleTextColor);
}
//先处理右边按钮
//获取是否要显示右边按钮
boolean rightButtonVisible = attributes.getBoolean(R.styleable.CustomTitleBar_tlb_right_button_visible, true);
if (rightButtonVisible) {
titleBarRightBtn.setVisibility(View.VISIBLE);
} else {
titleBarRightBtn.setVisibility(View.INVISIBLE);
}
//设置右边按钮的文字
String rightButtonText = attributes.getString(R.styleable.CustomTitleBar_tlb_right_button_text);
if (!TextUtils.isEmpty(rightButtonText)) {
titleBarRightBtn.setText(rightButtonText);
//设置右边按钮文字颜色
int rightButtonTextColor = attributes.getColor(R.styleable.CustomTitleBar_tlb_right_button_text_color, Color.WHITE);
titleBarRightBtn.setTextColor(rightButtonTextColor);
}
//设置右边图片icon 这里是二选一 要么只能是文字 要么只能是图片
int rightButtonDrawable = attributes.getResourceId(R.styleable.CustomTitleBar_tlb_right_button_drawable, -1);
if (rightButtonDrawable != -1) {
titleBarRightBtn.setCompoundDrawablesWithIntrinsicBounds(0, 0, rightButtonDrawable, 0); //设置到哪个控件的位置()
}
attributes.recycle();
}
}
public void setTitleClickListener(OnClickListener onClickListener) {
if (onClickListener != null) {
titleBarLeftBtn.setOnClickListener(onClickListener);
titleBarRightBtn.setOnClickListener(onClickListener);
}
}
public Button getTitleBarLeftBtn() {
return titleBarLeftBtn;
}
public Button getTitleBarRightBtn() {
return titleBarRightBtn;
}
public TextView getTitleBarTitle() {
return titleBarTitle;
}
}
4.)在不同的XML布局中引用
关于如何使用自定义属性这里就不再说明了,为了更加直观的查看效果,我这里在一个布局文件中实现不同要求的标题栏
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:lee="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <com.whoislcj.views.CustomTitleBar
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
lee:right_button_drawable="@mipmap/titlebar_add_icon"
lee:title_background_color="@color/green"
lee:title_text="标题1" /> <com.whoislcj.views.CustomTitleBar
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
lee:right_button_visible="false"
lee:title_background_color="@color/green"
lee:title_text="标题2" /> <com.whoislcj.views.CustomTitleBar
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
lee:left_button_text="左边"
lee:right_button_text="右边"
lee:title_background_color="@color/red"
lee:title_text="标题3" /> <com.whoislcj.views.CustomTitleBar
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
lee:left_button_text="左边"
lee:right_button_drawable="@mipmap/titlebar_add_icon"
lee:title_background_color="@color/red"
lee:title_text="标题4" /> <com.whoislcj.views.CustomTitleBar
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
lee:left_button_text="左边"
lee:left_button_text_color="@color/red"
lee:right_button_drawable="@mipmap/titlebar_add_icon"
lee:title_background_color="@color/blue"
lee:title_text="标题5" /> <com.whoislcj.views.CustomTitleBar
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="10dp"
lee:left_button_text="左边"
lee:left_button_text_color="@color/red"
lee:right_button_drawable="@mipmap/titlebar_add_icon"
lee:title_background_color="@color/blue"
lee:title_text="标题6"
lee:title_text_color="@color/black" /> </LinearLayout>
显示效果

总结:
通过本篇文章我们得知,通过自定义组合控件确实能够提高开发效率,降低维护成本,但是也需要UI设计风格保持高度一致,不然的话只能呵呵哒了!所以想要做好一个app需要一个有共识的团队才行。本篇介绍到此为止,下一篇要更新什么我还没有想好!有可能是自定义控件的事件回调,也有可能自定义ViewGroup实现流式布局。
Android自定义控件之自定义组合控件的更多相关文章
- Android自定义控件之自定义组合控件(三)
前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...
- android自定义控件(五) 自定义组合控件
转自http://www.cnblogs.com/hdjjun/archive/2011/10/12/2209467.html 代码为自己编写 目标:实现textview和ImageButton组合, ...
- Android 手机卫士--自定义组合控件构件布局结构
由于设置中心条目中的布局都很类似,所以可以考虑使用自定义组合控件来简化实现 本文地址:http://www.cnblogs.com/wuyudong/p/5909043.html,转载请注明源地址. ...
- Android开发之自定义组合控件
自定义组合控件的步骤1.自定义一个View,继承ViewGroup,比如RelativeLayout2.编写组合控件的布局文件,在自定义的view中加载(使用View.inflate())3.自定义属 ...
- [android] 手机卫士自定义组合控件
设置中心 新建SettingActivity 设置GridView条目的点击事件 调用GridView对象的setOnItemClickListenner()方法,参数:OnItemClickList ...
- Android自定义组合控件详细示例 (附完整源码)
在我们平时的Android开发中,有时候原生的控件无法满足我们的需求,或者经常用到几个控件组合在一起来使用.这个时候,我们就可以根据自己的需求创建自定义的控件了,一般通过继承View或其子类来实现. ...
- Android开发学习笔记-自定义组合控件的过程
自定义组合控件的过程 1.自定义一个View 一般来说,继承相对布局,或者线性布局 ViewGroup:2.实现父类的构造方法.一般来说,需要在构造方法里初始化自定义的布局文件:3.根据一些需要或者需 ...
- Android中自定义组合控件
Android中自定义控件的情况非常多,一般自定义控件可以分为两种:继承控件及组合控件.前者是通过继承View或其子类,重写方法实现自定义的显示及事件处理方式:后者是通过组合已有的控件,来实现结构的简 ...
- 014 Android 自定义组合控件
1.需求介绍 将已经编写好的布局文件,抽取到一个类中去做管理,下次还需要使用类似布局时,直接使用该组合控件的对象. 优点:可复用. 例如要重复利用以下布局: <RelativeLayout an ...
随机推荐
- [C#] C# 知识回顾 - 你真的懂异常(Exception)吗?
你真的懂异常(Exception)吗? 目录 异常介绍 异常的特点 怎样使用异常 处理异常的 try-catch-finally 捕获异常的 Catch 块 释放资源的 Finally 块 一.异常介 ...
- 独立开发 一个社交 APP 的架构分享 (已实现)
(本博客为原创:http://www.cnblogs.com/linguanh/) My BananaCloud Android Application 前言: 这算是我的第一个 完完全全 由自 ...
- HTML+CSS 项目总结
在过去的大概一个月的学习,基本掌握了HTML+CSS的用法和特性. 这个星期老师给我们布置了一个PC端的实战项目,并且要求在3-4天内完成,我不惜废寝忘食,在紧迫的时间内大致地完成了,但是有些效果不能 ...
- 获取 dhcp IP 过程分析 - 每天5分钟玩转 OpenStack(91)
前面我们已经讨论了 DHCP agent 的配置以及 namespace 如何隔离 dnsmasq 服务,本节将以 cirros-vm1 为例分析获取 DHCP IP 的详细过程. 在创建 insta ...
- C#——传值参数(2)
//我的C#是跟着猛哥(刘铁猛)(算是我的正式老师)<C#语言入门详解>学习的,微信上猛哥也给我讲解了一些不懂得地方,对于我来说简直是一笔巨额财富,难得良师! 这次与大家共同学习C#中的 ...
- 浅谈JSP注释
HTML注释 JSP文件是由HTML尿急和嵌入的Java程序片段组成的,所以在HTML中的注释同样可以在JSP文件中使用.注释格式:<!--注释内容--> <!-- 欢迎提示信息! ...
- eclipse如何添加Memory Analyzer
①启动Eclipse,并打开"Install New software..."对话框: ②点击Add,如图: ③点击OK,最后一直点next,完成
- CentOS:设置系统级代理(转)
原文地址:http://www.cnblogs.com/cocowool/archive/2012/07/05/2578487.html YUM代理设置 编辑/etc/yum.conf,在最后加入 # ...
- 【一起学OpenFOAM】03 OpenFOAM基本使用流程
OpenFOAM初学者常常对于软件的使用流程感到很迷惑,与其他的具有GUI的CFD软件不同,OpenFOAM的所有操作均为基于文本操作,譬如说里面各种计算模型.计算参数.流程控制参数等,均为通过修改对 ...
- WebAPI
WebAPI的Host OWIN IIS WebAPI 的handler和Filter有啥区别? WebAPI 常用 Filters Exception Filter Timer Filter Lo ...