Android自学笔记:Tab控件源码剖析

此篇主要以画图的方式剖析Tab控件的使用方式和实现原理,以便对Tab控件有更深的理解和掌握。其实主要由于我的文笔太次,因此以图代字,不是有那么一句话么:一图胜千言;这种写作方式对技术文章来说,最为合适不过了。要有图,要有文字;要有火锅,要有美屡(这个不是错别字,有人能看懂)~~~

[size=large][b]一、Tab初印象[/b][/size]
看图中画的框和椭圆,并对照着右边的文字说明,尽量看懂图中的包含关系。

[color=red][b] [size=large]首先非常感谢 icoo1985 的指正,下面图中紫色框代表着是应用程序的范畴,也就是Acitivty,而不包含系统的状态栏。[/size][/b][/color]

[img]http://dl.iteye.com/upload/attachment/447247/41c8417e-a1bc-305a-b1a0-9b71c90c59bd.jpg[/img]

[size=large][b]二、Tab相关类的介绍及它们之间的关系[/b][/size]
在这一小节里,继续以图解的方式针对上一节中描述的包含关系和Tab构建过程进行阐述。
[b]1.TabActivity[/b]
[img]http://dl.iteye.com/upload/attachment/427860/63e6a905-afd1-3f92-907a-72b3a5b17360.jpg[/img]
[b]2.TabHost[/b]
[img]http://dl.iteye.com/upload/attachment/428126/7c52f123-97dc-343a-b374-c02de56494a6.jpg[/img]
[b]3.TabHost.TabSpec[/b]
TabSpec主要负责去生成所需要添加进TabWidget和FrameLayout 布局当中的View,但是TabSpec不会直接与它们两个进行协作,而是只与TobHost来完成这一组装过程。按我的理解这个像是一个MVC模型,TabHost是控制器,TabSpec是模型,TabWidget和FrameLayout是视图展示层。
下面两幅图是组装的过程展示,整体过程均在TabHost.addTab()完成。
[b]组装Indicator[/b]
[img]http://dl.iteye.com/upload/attachment/428128/b167a991-896a-3207-ba20-b2b941b30633.jpg[/img]
[b]组装Content[/b]
[img]http://dl.iteye.com/upload/attachment/428132/9713ea84-df2c-3519-9f74-4564ba9ec936.jpg[/img]

[size=large][b]三、剖析Tab控件的源码实现[/b][/size]
[b]1.TabHost[/b]
在第二节提到过一个方法setup (LocalActivityManager), 按照API文档上所说,仅在以Intent方式构建TabSpec时,才要显示调用这个方法,前提是你未继承TabActivity;换个简单的说法,继承了TabActivity,我们就不需要显式地调用这个方法。
其实在setup (LocalActivityManager)版本中调用了它的重载版本setup(),也就是说真正的初始化工作是由setup来完成的。
两者有个共特点就是一般都不会显示的调用它们,当它们不是以getTabHost()方式来创建的或者说就根本没继承TabActivity的话,则需要显示调用。比如下面的代码示例:
TabHost  mTabHost = (TabHost)findViewById(R.id.tabhost);
mTabHost.setup();//或者setup (getLocalActivityManager());
mTabHost.addTab(…);

造成这种繁琐细节的主要原因是在于继承与不继承TabActivity。那么TabActivity已经为我们完成了这么的繁琐工作,那我们为什么不领这个情呢?
[b]2.TabHost.TabSpec[/b]
在第二节中提到了TabHost.TabSpec的用途,就是用来构建View的,并只与TabHost打交道。它负责构建两种View,一个是indicator view,另一个则是content view。观其源码,很容易地看出来针对这两种View,都采用了策略模式。
从实际用途的角度来考虑,创建Tab控件有共三种方式,这三种方式就对应着content view的三种策略,那么就先介绍它吧。
//TabHost.TabSpec 部分源码
//content view 构建策略引用
private ContentStrategy mContentStrategy;
//通过资源ID构建View
public TabSpec setContent(int viewId) {
//该策略直截了当地调用findViewById()方法返回指定的视图
mContentStrategy = new ViewIdContentStrategy(viewId);
return this;
}
//通过资源Intent构建View
public TabSpec setContent(Intent intent) {
//该策略LocalActivityManager.startActivity(String tabName, Intent intent)方法返回的Window,并通过Window的getDecorView() 返回最终的视图。
mContentStrategy = new IntentContentStrategy(mTag, intent);
return this;
}
//通过资源TabContentFactory构建View,它可以让你自由定制View
public TabSpec setContent(TabContentFactory contentFactory) {
// FactoryContentStrategy,其内部维护着一个TabContentFactory引用,并调用createTabContent(tagName)方法返回定制化的视图。
// 这个地方淋漓尽致地诠释了那句耳熟能详的名言,“将具体实现延迟到子类”,现在已经深有感触了。
// 就拿这个例子来说,一开始不要想模式,先根据业务需求来考虑,这个content view会跟根据indicator而变化,所以要做一些if else的处理;为了考虑代码的可维护性和健壮性,代码写的一定要优雅,慢慢地慢慢地就重构成非常满意的效果了,一看,嘿!这不是工厂模式么。
// 还有另外一种思考方式:当具备了一定的编码经验和设计经验之后,遇到该做活的地方一定要有一个意识,就好比提取出一个方法,我往这个方法传递各种参数已达到灵活的目的,慢慢地培养这种意识,代码就会越写越舒服了。
mContentStrategy = new FactoryContentStrategy(mTag, contentFactory);
return this;
}

我不得不感叹TabHost.TabSpec中的策略模式设计得如此巧妙,仅仅通过重载方法就能够自由切换不同策略来构建View,继续往下看,我们回到TabHost. setCurrentTab(int index) 方法上,
// 仅用一行代码就获取了content view,非常优雅。
// mCurrentView是TabHost的成员变量,意指当前的content view。
// spec是通过index获取到的TabHost.Tabspec。
// mContentStrategy就是上面看到的策略接口引用。
// spec.mContentStrategy内部类的特性。
mCurrentView = spec.mContentStrategy.getContentView();


接下来我们看下indicator策略,如下:
//TabHost.TabSpec 部分源码
//indicator view 构建策略引用
private IndicatorStrategy mIndicatorStrategy;
//设置了indicator名,直接调用了TextView.setText()
public TabSpec setIndicator(CharSequence label) {
mIndicatorStrategy = new LabelIndicatorStrategy(label);
return this;
}
//设置了indicator名和indicator图片,直接调用了TextView.setText()和ImageView. setImageDrawable(),图片是icon类型的。
public TabSpec setIndicator(CharSequence label, Drawable icon) {
mIndicatorStrategy = new LabelAndIconIndicatorStrategy(label, icon);
return this;
}
//设置定制化的indicator view,API没有明确说明个方法,而只提到了上面的两个策略,不过定制view的思想还是值得提倡的。
public TabSpec setIndicator(View view) {
mIndicatorStrategy = new ViewIndicatorStrategy(view);
return this;
}

通过观察,indicator view策略与content view策略如出一辙,但后者更为实用一些。
我们回到TabHost.addTab()方法上,

//也是仅用一行代码完成了获取indicator view的操作。
//由此看出来,学习设计模式固然重要,但写出优雅的代码势在必行!
View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();


[size=large][b]四、总结[/b][/size]
经过一番分析,可以看出Tab控件是由多个控件组合而成的,每个控件各司其职,共同完成整体上的功能。
个人感觉到分析的还是不太成熟,比较肤浅,思考的不够细致,怎么说呢,就是太感性了。今后会在分析问题上再多多下功夫,多多读源码,挖掘那些隐藏在表象之后的本质。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值