原文地址:http://www.cnblogs.com/kross/p/3378395.html

今天断断续续的折腾了一下午到现在20:38,终于有点明白了。o(╯□╰)o

在Android开发中,我们往往对系统提供的控件并不是很满意,比如现在市面上很多应用的Tab都是一张图加上文本控件的形式。加入我们的页面上有5个tab,那么就有五个ImageView和五个TextView,看上去就有点恶心,从软件工程的重用性上来说也是不符合要求的。

前段时间,我看了一本《Android UI 基础教程》by:Jason Ostrander,里面提到一种<include>标签的形式可以有效的介绍xml中的重复代码,提高复用性。

但我使用<include>之后,xml中的代码确实减少了很多,但是在Java代码中,重复代码还是很多,仍然需要findViewById,然后给每个控件设置属性。

因为以上的情况,我认识到应该自己定义一个复合控件,设置属性的方法也直接封装起来,使用的时候就方便多了。人类果然是在进步啊O(∩_∩)O哈!

如何自定义控件呢?

本文讲述的是通过继承ViewGroup来自定义控件的,通过覆写onDraw方法来自定义控件的高端方式在下还没有接触……

光说如何实现的话,是非常简单的,就和你百度“Android 继承LinearLayout”后看到的大多数文章一样。

假设我们要自定义一个Tab控件,上面是一个ImageView,下面是一个TextView。

首先我们要写一个XML文件。tab.xml(/res/layout/tab.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="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="default"/>
</LinearLayout>

然后你需要自己定义一个类,继承LinearLayout,当然你继承其它的ViewGroup也行。代码如下Tab.java(/src/view/Tab.java):

public class Tab extends LinearLayout {

    public Tab(Context context, AttributeSet attrs) {
super(context, attrs); LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Service.LAYOUT_INFLATER_SERVICE); View v = layoutInflater.inflate(R.layout.tab, this, true);
}
}

最后一步,是需要能直接使用我们写好的复合控件嘛,那么在需要的xml里加上<view.Tab/>标签就好了~(注意标签的名字是和自己定义的控件类的完整类名是一样的

比如这样:

<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity" >
<View
  class="view.Tab"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"/>
<view.Tab
android:id="@+id/tab"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

上面两种方式都是可以的。

使用起来就是比较简单的了。但是,当我学会如何使用的时候就一直有一个疑惑,我自定义一个类继承LinearLayout,覆写一下构造函数,为什么它就可以把刚刚那个xml文件中的两个控件用上呢?它到底是什么样的原理。

这个不像Fragment那么好理解的样子,在学Fragment的时候,Fragment如果有视图的话,只需要覆写onCreateView方法返回一个View就好了,这个View就是Fragment的视图,非常的好理解。

探究的过程我就不细说了,我是通过给每个控件设置一个id,然后获取子控件,获取子控件的数量,把这些信息输出到Logcat来研究它的原理的。这里就说下我研究的一些结果与规律吧。

通过继承ViewGroup来自定义控件也有两种方式,一种是通过XML来加载控件,这种方式比较好。另一种则是通过纯Java代码来添加控件,这种方式就和Java的GUI没什么区别了。但两者是有一定的区别的。

通过纯Java代码来加载控件是比较好理解的。看下面的代码。

public class Tab extends LinearLayout {

    public Tab(Context context, AttributeSet attrs) {
super(context, attrs); int count1 = getChildCount();//count1 -> 0 TextView tv = new TextView(context);
tv.setText("aaaaa");
addView(tv); int count2 = getChildCount();//count2 -> 1
}
}

通过Java代码来操作的话比较好理解,上面这个类是继承与LinearLayout的,LinearLayout是一个ViewGroup,然后往里面添加了一个TextView对象。这样当你在xml中写上<view.Tab/>的时候,编译器会先把XML转成二进制的形式变成对象,那么它自然会去new我们自定义的这个Tab类,并且执行它里面的构造函数,理所应当的给里面添加了一个TextView,这个和如下的XML代码是木有区别的。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/box"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="aaaaa"/>
</LinearLayout>

我们用一个图来表示刚刚实现的Tab类,它的结构层次。

那么通过加载XML文件的形式来自定义控件是怎么个实现方式呢。

它最关键的就在于调用inflate这个方法的时候。

LayoutInflater li = (LayoutInflater)context.getSystemService(Service.LAYOUT_INFLATER_SERVICE);

li.inflate(R.layout.tab, this, true);

第二行代码的意思是:扩充tab.xml布局,并将它附着于本类上

也就是这行代码,相当于给Tab类绑定了一个XML的视图。当我了解到这行的作用后,我的困惑基本就解决了。

这行代码执行完成后,Tab对象就已经具有自己的视图的,当系统发现<view.Tab/>标签的时候,便会去找到Tab类,去实例化它,并调用它的构造函数,当执行完这句话之后,tab.xml的布局便会附着于Tab类自身(也就是附着于LinearLayout)。假设我们有如下的tab.xml。(留意给控件加的id

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/box"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="@+id/tab_icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/tab_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="aaaaa"/>
</LinearLayout>

ok,如果我们使用<view.Tab/>后,所形成的视图层次结构应该是这样的。

很显然,通过扩充XML的方式自定义的控件,比通过java代码操作来自定义控件的方式多了一层ViewGroup。

还有一个需要注意的地方就是,inflate(R.layout.tab, this, true)第三个参数为true的时候,这个函数返回的是root,如果是false,则返回的xml的root。

就是说,当是true的时候,返回的是外层的LinarLayout,也就是Tab类本身,如果需要找到TextView,需要向下找两层

当是false的时候,返回的是id为box的LinearLayout

OK,到这里为止,我对于通过继承ViewGroup的方式来自定义控件的方式理解的比较好了。希望能帮到和我有相同困惑的少年。

转载请注明出处:http://www.cnblogs.com/kross/p/3378395.html
新浪微博:http://weibo.com/KrossFord

探究Android中通过继承ViewGroup自定义控件的原理的更多相关文章

  1. Style在Android中的继承关系

    Style在Android中的继承关系 Android的Styles(样式)和Themes(主题)非常类似Web开发里的CSS,方便开发者将页面内容和布局呈现分开.Style和Theme在Androi ...

  2. Android中为APP创建快捷方式的原理(自己的理解)

    我们首先来看Android中为APP创建快捷方式的原理: 从图上可以看出,Android大致分7步完成快捷方式的创建: 第一步:Android系统的launcher程序会调用它的pickShortcu ...

  3. 《前端之路》- TypeScript (三) ES5 中实现继承、类以及原理

    目录 一.先讲讲 ES5 中构造函数(类)静态方法和多态 1-1 JS 中原型以及原型链 例子一 1-2 JS 中原型以及原型链中,我们常见的 constructor.prototype.**prot ...

  4. Android中View和ViewGroup介绍

    1. 概念Android中的View与我们以前理解的“视图”不同.在Android中,View比视图具有更广的含义,它包含了用户交互和显示,更像Windows操作系统中的window. ViewGro ...

  5. 探究Android中Listview显示错乱问题

    问题 最近在项目中遇到过一个很棘手的问题,就是ListView在滑动后就莫名其妙的显示错乱,网上查阅资料后问题很容易的就解决了,但是对于问题产生的原因仍是一知半解,所以不甘心的我定下心来,狠读源码,终 ...

  6. android 中组件继承关系图,一目了然

    View继承关系图 Adapter适配器继承关系图 Activity继承关系图

  7. android中fragment与activity之间通信原理以及例子

    参考文章 http://blog.csdn.net/guozh/article/details/25327685#comments Activity和fragment通信方式一般有3种方法 1.在fr ...

  8. Android菜鸟的成长笔记(15)—— Android中的状态保存探究(下)

    原文:Android菜鸟的成长笔记(15)-- Android中的状态保存探究(下) 在上一篇中我们简单了解关于Android中状态保存的过程和原理,这一篇中我们来看一下在系统配置改变的情况下保存数据 ...

  9. Android中的Touch事件

    Android中的Touch事件处理 主要内容 Activity或View类的onTouchEvent()回调函数会接收到touch事件. 一个完整的手势是从ACTION_DOWN开始,到ACTION ...

随机推荐

  1. 面试和工作中的map

    map是C++ STL中的关联容器,存储的是键值对(key-value),可以通过key快速索引到value.map容器中的数据是自动排序的,其排序方式是严格的弱排序(stick weak order ...

  2. Activiti5第一天——待更新

    一.概述 相关介绍资料可以参见:https://www.ibm.com/developerworks/cn/java/j-lo-activiti1/ http://blog.csdn.net/blue ...

  3. 20155230 2016-2017-2《Java程序设计》第一周学习总结

    第一周学习总结 考核方式 100分构成 翻转课堂考核12次(5*12 = 60):每次考试20-30道题目,考试成绩规格化成5分(比如总分20分就除以4) 注意:不轮什么原因,缺考没有补考机会,但有做 ...

  4. 20155235 2016-2017-2《Java程序设计》课程总结

    每周作业链接汇总 预备作业一:学期前作业 预备作业二:技能获取与C语言学习情况 预备作业三:安装虚拟机与Linux的学习 第一周作业:20155235 2006-2007-2 <Java程序设计 ...

  5. WPF 动画:同为控件不同命 - 简书

    原文:WPF 动画:同为控件不同命 - 简书 1. 及格与优秀 读大学的时候,有一门课的作业是用 PPT 展示. 但是我们很多同学都把 PPT 当做 Word 来用,就单纯地往里面堆文字. 大家都单纯 ...

  6. MySQL优化Explain命令简介(一)

    最近碰到MySQL需要写入大量数据并查询的场景,于是学习了一下MySQL的查询优化,想找关于explain命令的详细资料,然而网上并没有找全,最后终于在<高性能MySQL>中找到了对这一命 ...

  7. 【HNOI2014】世界树

    题面 题解 虚树好题(只是细节太多) 构出虚树后,一定要仔细梳理关键点之间的点是上面属于父亲,下面属于儿子. 然后二分出所有的点的所属就可以了 代码 #include<cstdio> #i ...

  8. 我们一起学习WCF 第一篇初识WCF(附源码供对照学习)

    前言:去年由于工作需要我学习了wcf的相关知识,初期对wcf的作用以及为何用怎么样都是一知半解,也许现在也不是非常的清晰.但是通过项目对wcf的运用在脑海里面也算有了初步的模型.今天我就把我从开始wc ...

  9. QT在子窗口外单击关闭子窗口

    网上查到了好多种方法,1.添加过滤器(eventFilter),2.重写鼠标单击事件,这两种方法都要判断鼠标单击位置是不是在子窗口上.而且在可编辑控件上如(下拉框.文本编辑框等),父窗口会直接传递到可 ...

  10. const与readonly常量

    const与readonly常量 const与readonly都是用来定义常量,但是它们有什么区别呢? 下面我们来简要的说明一下: const修饰的常量是编译时常量,如:public const St ...