探究Android中通过继承ViewGroup自定义控件的原理
原文地址: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自定义控件的原理的更多相关文章
- Style在Android中的继承关系
Style在Android中的继承关系 Android的Styles(样式)和Themes(主题)非常类似Web开发里的CSS,方便开发者将页面内容和布局呈现分开.Style和Theme在Androi ...
- Android中为APP创建快捷方式的原理(自己的理解)
我们首先来看Android中为APP创建快捷方式的原理: 从图上可以看出,Android大致分7步完成快捷方式的创建: 第一步:Android系统的launcher程序会调用它的pickShortcu ...
- 《前端之路》- TypeScript (三) ES5 中实现继承、类以及原理
目录 一.先讲讲 ES5 中构造函数(类)静态方法和多态 1-1 JS 中原型以及原型链 例子一 1-2 JS 中原型以及原型链中,我们常见的 constructor.prototype.**prot ...
- Android中View和ViewGroup介绍
1. 概念Android中的View与我们以前理解的“视图”不同.在Android中,View比视图具有更广的含义,它包含了用户交互和显示,更像Windows操作系统中的window. ViewGro ...
- 探究Android中Listview显示错乱问题
问题 最近在项目中遇到过一个很棘手的问题,就是ListView在滑动后就莫名其妙的显示错乱,网上查阅资料后问题很容易的就解决了,但是对于问题产生的原因仍是一知半解,所以不甘心的我定下心来,狠读源码,终 ...
- android 中组件继承关系图,一目了然
View继承关系图 Adapter适配器继承关系图 Activity继承关系图
- android中fragment与activity之间通信原理以及例子
参考文章 http://blog.csdn.net/guozh/article/details/25327685#comments Activity和fragment通信方式一般有3种方法 1.在fr ...
- Android菜鸟的成长笔记(15)—— Android中的状态保存探究(下)
原文:Android菜鸟的成长笔记(15)-- Android中的状态保存探究(下) 在上一篇中我们简单了解关于Android中状态保存的过程和原理,这一篇中我们来看一下在系统配置改变的情况下保存数据 ...
- Android中的Touch事件
Android中的Touch事件处理 主要内容 Activity或View类的onTouchEvent()回调函数会接收到touch事件. 一个完整的手势是从ACTION_DOWN开始,到ACTION ...
随机推荐
- cocos2d-x安装
mac 安装2.2.6为例 1.进入cocos2d-x/tools/project-creator/ 2.输入 python create_project.py -project HelloWorld ...
- 两组数据的均值是否具有显著差异的T检验
最近在做分析的时候,遇到了T检验,然而对于没有统计学背景的人来说完全不知如何下手 当然了,遇到问题第一反应就是百度. 果然百度出来了很多链接,当时第一次直接选择了用Excel去做T检验.下面是源数据 ...
- .Net 两个对像之间的映射 (一 )
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Java使用POI导出excel(下)——实例与小技巧
[更新]:thinkgem的导出工具类: /** * Copyright © 2012-2016 <a href="https://github.com/thinkgem/jeesit ...
- 2016-2017-20155329 《Java程序设计》第十周学习总结
学号 2016-2017-20155329 <Java程序设计>第十周学习总结 教材学习内容总结 学习目标 了解计算机网络基础 OSI分层(7层):物理层.数据链路层.网络层.传输层.会话 ...
- 20155330 2016-2017-2 《Java程序设计》第四周学习总结
20155330 2016-2017-2 <Java程序设计>第四周学习总结 教材学习内容总结 学习目标 理解封装.继承.多态的关系 理解抽象类与接口的区别 掌握S.O.L.I.D原则 了 ...
- gitlab在push代码的时候报错
一.问题报错 gitlab在执行git pull origin master,拉取代码的时候报如下错误. $ git pull origin master remote: Counting objec ...
- PHP 中call_user_func相关函数的使用
call_user_func 官方的解释是:把第一个参数作为回调函数(callback),并且将其余的参数作为回调函数的参数. 第一个参数可以是函数名,后面的均为作为该函数使用的参数. 1. call ...
- 写一个 setter 方法用于完成 @property (nonatomic, retain) NSString *name,
写一个 setter 方法用于完成 @property (nonatomic, retain) NSString *name 写一个 setter 方法用于完成 @property (nonatomi ...
- 「日常训练」Magic Stones(CodeForces-1110E)
题意 给定两个数组c和t,可以对c数组中的任何元素变换\(c_i\)成\(c_{i+1}+c_{i-1}-c_i\),问c数组在若干次变换后能否变换成t数组. 分析 这种魔法题目我是同样的没做过. ...