Andriod 自定义控件之创建可以复用的组合控件
前面已学习了一种自定义控件的实现,是Andriod 自定义控件之音频条,还没学习的同学可以学习下,学习了的同学也要去温习下,一定要自己完全的掌握了,再继续学习,贪多嚼不烂可不是好的学习方法,我们争取学习了一种技术就会一种技术,而且不光看了就算了,最好的方法就是看完我自己再练习下,再扩展下,在原来的基础上在添加一些东西,比如,增加一些功能实现等等。
今天我们打算学习下另外一种自定义控件,就是创建可重复使用的组合控件,那么问题来了:
- 什么是可重复使用?
就是在应用中,可以在多个地方共同使用一套代码。这样不仅能减少我们的工作量,而且还能保持应用风格的一致,这种应用最多最直接的体现就是统一风格样式的标题栏。 - 那什么又是组合控件呢?
组合控件,顾名思义就是多个控件组合在一起,相互协作共同完成某些特定的功能。
下面我们就针对app应用中风格统一的标题栏来开始我们的学习。
首先,既然是一组组合的控件,那就必须有一个可以来包含这些控件的容器,我们所接触的可以存放控件的容器很多,比如LinearLayout、RelativeLayout等等多种Layout,今天我们就选择RelativeLayout来做我们的容器。和以前一样,我们先定义一个CompositeViews类来继承RelativeLayout类,并重写它的构造方法,代码如下:
public class CompositeViews extends RelativeLayout{
public CompositeViews(Context context) {
this(context,null);
}
public CompositeViews(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CompositeViews(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
接下来,我们再定义三个TextView控件,分别是mLefeText,mRightText,textTitle用来显示“返回”,“搜索”以及“标题”并且使用LayoutParams规定它们在容器里面的对齐方式。请看代码:
public class CompositeViews extends RelativeLayout{
private TextView mLefeText;
private TextView mRightText;
private TextView textTitle;
private LayoutParams leftLayoutParams;
private LayoutParams ridhtLayoutParams;
private LayoutParams titleLayoutParams;
public CompositeViews(Context context) {
this(context,null);
}
public CompositeViews(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CompositeViews(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
initView(context)方法中是用来初始化三个组合控件的,请看:
private void initView(Context context) {
mLefeText = new TextView(context);
mRightText = new TextView(context);
textTitle = new TextView(context);
/*
* 左按钮位置
*/
leftLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
leftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
mLefeText.setText("返回");
mLefeText.setTextSize(22);
/*
* 右按钮位置
*/
ridhtLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ridhtLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
mRightText.setText("搜索");
mRightText.setTextSize(22);
/*
* 中间标题位置
*/
titleLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
titleLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
textTitle.setText("这是一个标题");
textTitle.setTextSize(22);
}
ok,以上的代码已经实现了组合控件的显示和对齐方式,我们把定义的View添加到布局文件中并在Activity加载吧
activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:background="#999999"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.sanhuimusic.mycustomview.view.CompositeViews
android:id="@+id/topBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
MainActivity:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
我们先来运行看下结果
ok,已显示出来了,但是相信大家也看出来了,这上面的代码中,各个控件中的属性是都是我们固定写死的,既然我们是创建可服用的控件,固定写死的东西肯定是不可取的,那么我们怎么可以灵活地获取控件的属性,以至于能达到复用呢?
这就必须要接触另外一种技术了,就是自定义属性。用我们自定义的属于可以在每次使用我们定义的控件时为其分配属性即可。下面我们来学习下自定义属性。
自定义属性其实也是相当的简单,首先,我们现在资源文件res下values目录下新建一个attrs.xml文件(eclipse自带,as自建),新建的attrs.xml是一个包含如下代码的文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>
在resources中有各种属性供我们使用,同学们可以自己看下。根据我们现在的需求,我们选择使用declare-styleable来声明我们的属性集,然后为其定义特有的name属性,这个name是供我们在使用自定义属性时,通过它可以查找到里面的所有属性。请看如下代码:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CompositeViews">
<attr name="titleText" format="string"/>
<attr name="titleTextSize" format="dimension"/>
<attr name="titleColor" format="color"/>
<attr name="titleBackground" format="color|reference"/>
<attr name="leftTextColor" format="color"/>
<attr name="leftBackground" format="color|reference"/>
<attr name="leftText" format="string"/>
<attr name="leftTextSize" format="dimension"/>
<attr name="rightTextColor" format="color"/>
<attr name="rightBackground" format="color|reference"/>
<attr name="rightText" format="string"/>
<attr name="rightTextSize" format="dimension"/>
</declare-styleable>
</resources>
单独拿一行属性来解析下它所代表的含义:如 :attr代表的是一个属性,它里面所包含name字段是这条属性名,通过该属性名可以获取以format的约束为真的属性值;formate是该属性的格式,分别包含string,dimension,color,reference等等,分别代表字符串,大小,颜色,引用。其他的大家可以自行学习resources所包含的属性。
好了,自定义属性我们已学习完毕,那么该怎么使用我们自己定义的属性呢?其实也很简单,在我们的activity_main.xml文件中直接使用我们定义的属性就可以了,但是在使用是之前必须在指定引用第三方控件的命名空间,在跟布局文件中添加如下一行代码:
xmlns:custom="http://schemas.android.com/apk/res-auto"
custom是我们第三方命名空间的名字,可以任意命名,我们在使用自定义属性时必须以它开头。请看代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:background="#999999"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.sanhuimusic.mycustomview.view.CompositeViews
android:id="@+id/topBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:titleText="@string/titleText"
custom:titleColor="#000000"
custom:titleTextSize="@dimen/titleTextSize"
custom:titleBackground="#999999"
custom:leftText="@string/leftText"
custom:leftTextColor="#FFFFFF"
custom:leftBackground="#666666"
custom:leftTextSize="@dimen/leftTextSize"
custom:rightText="@string/rightText"
custom:rightTextColor="#FFFFFF"
custom:rightBackground="#666666"
custom:rightTextSize="@dimen/rightTextSize"
/>
</LinearLayout>
我们是使用custom加上我们自定义属性里面< attr name="titleText" format="string"/>里的name值来动态设置属性值的,如:custom:titleText="@string/titleText"。
ok,在我们xml文件中已设定好属性值,那么该怎么显示出来呢?这个是需要通过一个类型组TypedArray来获取的,它里面包含各种从AttributeSet属性集中获取属性的方法,所以我们修改上面的构造方法和initView(context)方法,如下所示:
private void initView(Context context, AttributeSet attrs) {
mLefeText = new TextView(context);
mRightText = new TextView(context);
textTitle = new TextView(context);
/**
* 获取自定义属性
*/
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CompositeViews);
String titleText = typedArray.getString(R.styleable.CompositeViews_titleText);
float titleTextSize = typedArray.getDimension(R.styleable.CompositeViews_titleTextSize, 16);
int titleColor = typedArray.getColor(R.styleable.CompositeViews_titleColor,0);
Drawable titleBackground = typedArray.getDrawable(R.styleable.CompositeViews_titleBackground);
String leftText = typedArray.getString(R.styleable.CompositeViews_leftText);
int leftTextColor = typedArray.getColor(R.styleable.CompositeViews_leftTextColor, 0);
float leftTextSize = typedArray.getDimension(R.styleable.CompositeViews_leftTextSize, 16);
Drawable leftBackground = typedArray.getDrawable(R.styleable.CompositeViews_leftBackground);
String rightText = typedArray.getString(R.styleable.CompositeViews_rightText);
int rightTextColor = typedArray.getColor(R.styleable.CompositeViews_rightTextColor, 0);
float rightTextSize = typedArray.getDimension(R.styleable.CompositeViews_rightTextSize, 16);
Drawable rightBackground = typedArray.getDrawable(R.styleable.CompositeViews_rightBackground);
typedArray.recycle();
/*
* 左按钮位置
*/
leftLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
leftLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT,TRUE);
mLefeText.setText(leftText);
mLefeText.setTextColor(leftTextColor);
mLefeText.setTextSize(leftTextSize);
mLefeText.setBackground(leftBackground);
addView(this.mLefeText,leftLayoutParams);
/*
* 右按钮位置
*/
ridhtLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ridhtLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT,TRUE);
mRightText.setText(rightText);
mRightText.setTextColor(rightTextColor);
mRightText.setTextSize(rightTextSize);
mRightText.setBackground(rightBackground);
addView(mRightText,ridhtLayoutParams);
/*
* 中间标题位置
*/
titleLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
titleLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT,TRUE);
textTitle.setText(titleText);
textTitle.setTextSize(titleTextSize);
textTitle.setTextColor(titleColor);
textTitle.setBackground(titleBackground);
addView(textTitle,titleLayoutParams);
}
代码解释:首先通过上下文context获取到属性存放到TypedArray 中,然后通过TypedArray 里封装好的各种方法获取对应的属性值,然后再分别为我们的控件设置属性。这样就完成了,自定义属性的使用,并且复用度高,每当需要使用标题栏是都只需要在xml中添加我们定义的View控件,为其配置属性即可使用,节约了开发时间,提高了效率,并且还保持的app风格的一致。
好,到这里感觉已经讲完了整个过程吧,其实还有一个重要的实现还没有讲。我们的控件已经可以呈现出来了,但是怎么完成里面控件的作用呢?
这里比较常见的做法是利用回调机制来实现功能的开发,首先我们先定义一个接口,创建两个方法,用于左右控件的点击事件。
public interface TopBarClickListener{
void leftClickListener();
void rightClickListener();
}
然后在构造方法中为左右控件添加点击事件,但不实现功能,等待调用者自己实现:
private void setListener() {
mLefeText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mTopBarClickListener.leftClickListener();
}
});
mRightText.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mTopBarClickListener.rightClickListener();
}
});
}
再者,把定义好的接口暴露给调用者:
public void setOnTopBarClickListener(TopBarClickListener topBarClickListener){
mTopBarClickListener = topBarClickListener;
}
最后,谁调用,谁实现。这就完成了不同界面复用控件实现不同的功能的便利。在这里我们只在MainActivity中打印Toast就可以了。
public class MainActivity extends AppCompatActivity {
private CompositeViews topBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
topBar = (CompositeViews) findViewById(R.id.topBar);
topBar.setOnTopBarClickListener(new CompositeViews.TopBarClickListener(){
@Override
public void leftClickListener() {
ToastUtil.makeText(MainActivity.this,"您点击了返回键",Toast.LENGTH_SHORT).show();
}
@Override
public void rightClickListener() {
ToastUtil.makeText(MainActivity.this,"您点击了搜索键",Toast.LENGTH_SHORT).show();
}
});
}
}
OK,看看结果吧
好,已经可以实现我们的需求了,是不是学会很多呢。
今天主要讲了android自定义View中另一种的实现,并且还学习了自定义属性,同学们下去好好消化下,并自己动手现实一两个例子吧,好了,今天就讲到这里,谢谢大家。
更多资讯请关注微信平台,有博客更新会及时通知。爱学习爱技术。
Andriod 自定义控件之创建可以复用的组合控件的更多相关文章
- ASP.NET自定义控件组件开发 第四章 组合控件开发CompositeControl
原文:ASP.NET自定义控件组件开发 第四章 组合控件开发CompositeControl 第四章 组合控件开发CompositeControl 大家好,今天我们来实现一个自定义的控件,之前我们已经 ...
- ASP.NET自定义控件组件开发 第四章 组合控件开发CompositeControl 后篇 --事件冒泡
原文:ASP.NET自定义控件组件开发 第四章 组合控件开发CompositeControl 后篇 --事件冒泡 CompositeControl 后篇 --事件冒泡 系列文章链接: ASP.NET ...
- ASP.NET自定义控件组件开发 第五章 模板控件开发
原文:ASP.NET自定义控件组件开发 第五章 模板控件开发 第五章 模板控件开发 系列文章链接: ASP.NET自定义控件组件开发 第一章 待续 ASP.NET自定义控件组件开发 第一章 第二篇 接 ...
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇 第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列 ...
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇 第三章 为控件添加事件 后篇 前一篇文章只是简单的说了下事件,但是大家应该方法,在ASP.NET自定义控件中只是简单那么定义事件是 ...
- Android自定义控件之自定义组合控件
前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...
- Android自定义控件之自定义组合控件(三)
前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...
- 自定义控件之--组合控件(titlebar)
自定义控件相关知识从郭霖等大神身上学习,这里只不过加上自己的理解和实践,绝非抄袭. 组合控件是自定义控件中最简单的方式,但是是入门自定义控件和进阶的过程: 那么常见的组合控件有那些? 比如titl ...
- Android开发技巧——自定义控件之组合控件
Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...
随机推荐
- 【腾讯Bugly干货分享】Android Patch 方案与持续交付
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57a31921ac3a1fb613dd40f3 Android 不仅系统版本众多 ...
- mysql集群(主从)
本文主要记录mysql 主从配置. 经典的原理图 0.环境: 采用阿里云ECS服务器,同区同配置,操作系统为ubuntus 14 64位,服务器如下: 服务器A: 内网IP: 10.44.94.219 ...
- 《深入理解Java虚拟机》调优案例分析与实战
上节学习回顾 在上一节当中,主要学习了Sun JDK的一些命令行和可视化性能监控工具的具体使用,但性能分析的重点还是在解决问题的思路上面,没有好的思路,再好的工具也无补于事. 本节学习重点 在书本上本 ...
- Entity Framework 6 Recipes 2nd Edition(11-5)译 -> 从”模型定义”函数返回一个匿名类型
11-5. 从”模型定义”函数返回一个匿名类型 问题 想创建一个返回一个匿名类型的”模型定义”函数 解决方案 假设已有游客(Visitor) 预订(reservation)房间(hotel ) 的模型 ...
- nodejs模块中exports和module.exports的区别
通过Node.js的官方API可以看到Node.js本身提供了很多核心模块 http://nodejs.org/api/ ,这些核心模块被编译成二进制文件,可以require('模块名')去获取:核心 ...
- bootstrap表格
Bootstrap 实例 - 边框表格 < 建立日期 2015-5-27 录入人员 test1 处理人员 test2 问题报障人 部门/城市公司 联系电话 问题类型 处理状态 ...
- 将Json数据保存在静态脚本文件中读取
一些常用的数据例如一些网站的区域信息被改变的可能性不大,一般不通过请求获取,于是我们选择存在静态文件中,例如以下Demo: 1.动态加载Json数据显示到前台 [HttpPost] public Ac ...
- SVN:Previous operation has not finished; run 'cleanup' if it was interrupted
异常处理汇总-开发工具 http://www.cnblogs.com/dunitian/p/4522988.html cleanup failed to process the following ...
- ng-directive-选择数据
本文是用angularjs指令写的一个简易数据选择功能,其实就是两个下拉框,把两边的数据相互交换而已,这样的功能最早应该是用jquery写过,但移动端js框架angularjs如果还嵌套jquery来 ...
- geotrellis使用(二十四)将Geotrellis移植到CDH中必须要填的若干个坑
目录 前言 若干坑 总结 一.前言 近期干了一件事情,将geotrellis程序移植到CDH中(关于CDH,可以参考安装ClouderaManager以及使用ClouderaManage ...