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

本文以图解形式深入剖析Tab控件的使用方式和实现原理,包括TabActivity、TabHost等核心组件及其相互关系,帮助读者更好地理解和应用Tab控件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://chirs1012f.iteye.com/blog/939322

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

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

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



二、Tab相关类的介绍及它们之间的关系
        在这一小节里,继续以图解的方式针对上一节中描述的包含关系和Tab构建过程进行阐述。
        1.TabActivity

        2.TabHost

        3.TabHost.TabSpec
        TabSpec主要负责去生成所需要添加进TabWidget和FrameLayout 布局当中的View,但是TabSpec不会直接与它们两个进行协作,而是只与TobHost来完成这一组装过程。按我的理解这个像是一个MVC模型,TabHost是控制器,TabSpec是模型,TabWidget和FrameLayout是视图展示层。
        下面两幅图是组装的过程展示,整体过程均在TabHost.addTab()完成。
        组装Indicator

        组装Content


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

Java代码 复制代码  收藏代码
  1. TabHost  mTabHost = (TabHost)findViewById(R.id.tabhost);   
  2. mTabHost.setup();//或者setup (getLocalActivityManager());   
  3. mTabHost.addTab(…);  
TabHost  mTabHost = (TabHost)findViewById(R.id.tabhost);
mTabHost.setup();//或者setup (getLocalActivityManager());
mTabHost.addTab(…);

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


接下来我们看下indicator策略,如下:
Java代码 复制代码  收藏代码
  1. //TabHost.TabSpec 部分源码   
  2. //indicator view 构建策略引用   
  3. private IndicatorStrategy mIndicatorStrategy;   
  4. //设置了indicator名,直接调用了TextView.setText()   
  5. public TabSpec setIndicator(CharSequence label) {   
  6.     mIndicatorStrategy = new LabelIndicatorStrategy(label);   
  7.     return this;   
  8. }   
  9. //设置了indicator名和indicator图片,直接调用了TextView.setText()和ImageView. setImageDrawable(),图片是icon类型的。   
  10. public TabSpec setIndicator(CharSequence label, Drawable icon) {   
  11.     mIndicatorStrategy = new LabelAndIconIndicatorStrategy(label, icon);   
  12.     return this;   
  13. }   
  14. //设置定制化的indicator view,API没有明确说明个方法,而只提到了上面的两个策略,不过定制view的思想还是值得提倡的。   
  15.  public TabSpec setIndicator(View view) {   
  16.     mIndicatorStrategy = new ViewIndicatorStrategy(view);   
  17.     return this;   
  18. }  
//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()方法上,
Java代码 复制代码  收藏代码
  1. //也是仅用一行代码完成了获取indicator view的操作。   
  2. //由此看出来,学习设计模式固然重要,但写出优雅的代码势在必行!   
  3. View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();  
//也是仅用一行代码完成了获取indicator view的操作。
//由此看出来,学习设计模式固然重要,但写出优雅的代码势在必行!
View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();


四、总结
        经过一番分析,可以看出Tab控件是由多个控件组合而成的,每个控件各司其职,共同完成整体上的功能。
        个人感觉到分析的还是不太成熟,比较肤浅,思考的不够细致,怎么说呢,就是太感性了。今后会在分析问题上再多多下功夫,多多读源码,挖掘那些隐藏在表象之后的本质。
资源下载链接为: https://pan.quark.cn/s/abbae039bf2a 无锡平芯微半导体科技有限公司生产的A1SHB三极管(全称PW2301A)是一款P沟道增强型MOSFET,具备低内阻、高重复雪崩耐受能力以及高效电源切换设计等优势。其技术规格如下:最大漏源电压(VDS)为-20V,最大连续漏极电流(ID)为-3A,可在此条件下稳定工作;栅源电压(VGS)最大值为±12V,能承受正反向电压;脉冲漏极电流(IDM)可达-10A,适合处理短暂高电流脉冲;最大功率耗散(PD)为1W,可防止器件过热。A1SHB采用3引脚SOT23-3封装,小型化设计利于空间受限的应用场景。热特性方面,结到环境的热阻(RθJA)为125℃/W,即每增加1W功率损耗,结温上升125℃,提示设计电路时需考虑散热。 A1SHB的电气性能出色,开关特性优异。开关测试电路及波形图(图1、图2)展示了不同条件下的开关性能,包括开关上升时间(tr)、下降时间(tf)、开启时间(ton)和关闭时间(toff),这些参数对评估MOSFET在高频开关应用中的效率至关重要。图4呈现了漏极电流(ID)与漏源电压(VDS)的关系,图5描绘了输出特性曲线,反映不同栅源电压下漏极电流的变化。图6至图10进一步揭示性能特征:转移特性(图7)显示栅极电压(Vgs)对漏极电流的影响;漏源开态电阻(RDS(ON))随Vgs变化的曲线(图8、图9)展现不同控制电压下的阻抗;图10可能涉及电容特性,对开关操作的响应速度和稳定性有重要影响。 A1SHB三极管(PW2301A)是高性能P沟道MOSFET,适用于低内阻、高效率电源切换及其他多种应用。用户在设计电路时,需充分考虑其电气参数、封装尺寸及热管理,以确保器件的可靠性和长期稳定性。无锡平芯微半导体科技有限公司提供的技术支持和代理商服务,可为用户在产品选型和应用过程中提供有
资源下载链接为: https://pan.quark.cn/s/9648a1f24758 在 JavaScript 中实现点击展开与隐藏效果是一种非常实用的交互设计,它能够有效提升用户界面的动态性和用户体验。本文将详细阐述如何通过 JavaScript 实现这种功能,并提供一个完整的代码示例。为了实现这一功能,我们需要掌握基础的 HTML 和 CSS 知识,以便构建基本的页面结构和样式。 在这个示例中,我们有一个按钮和一个提示框(prompt)。默认情况下,提示框是隐藏的。当用户点击按钮时,提示框会显示出来;再次点击按钮时,提示框则会隐藏。以下是 HTML 部分的代码: 接下来是 CSS 部分。我们通过设置提示框的 display 属性为 none 来实现默认隐藏的效果: 最后,我们使用 JavaScript 来处理点击事件。我们利用事件监听机制,监听按钮的点击事件,并通过动态改变提示框的 display 属性来实现展开和隐藏的效果。以下是 JavaScript 部分的代码: 为了进一步增强用户体验,我们还添加了一个关闭按钮(closePrompt),用户可以通过点击该按钮来关闭提示框。以下是关闭按钮的 JavaScript 实现: 通过以上代码,我们就完成了点击展开隐藏效果的实现。这个简单的交互可以通过添加 CSS 动画效果(如渐显渐隐等)来进一步提升用户体验。此外,这个基本原理还可以扩展到其他类似的交互场景,例如折叠面板、下拉菜单等。 总结来说,JavaScript 实现点击展开隐藏效果主要涉及 HTML 元素的布局、CSS 的样式控制以及 JavaScript 的事件处理。通过监听点击事件并动态改变元素的样式,可以实现丰富的交互功能。在实际开发中,可以结合现代前端框架(如 React 或 Vue 等),将这些交互封装成组件,从而提高代码的复用性和维护性。
一、AutoCAD 2016的工作界面 组成要素:由应用程序菜单、标题栏、快速访问工具栏、菜单栏、功能区、命令窗口、绘图窗口和状态栏组成。 1. 切换至AutoCAD 2016 1)工作空间 模式类型:提供草图与注释、三维基础、三维建模三种工作空间模式 二维绘图功能:在草图与注释空间中可使用默认、插入、注释、参数化、视图管理等选项卡进行二维图形绘制 切换方法: 快速访问工具栏→工作空间按钮下拉列表 状态栏→切换工作空间按钮下拉列表 三维功能:三维基础空间包含可视化、坐标、长方体等三维建模工具 2)应用程序菜单 位置:位于界面左上角 核心功能: 搜索命令 文件操作(新建/打开/保存/另存为/输出/发布/打印/关闭) 最近文档管理(可按日期/大小/类型排序) 选项设置(打开选项对话框) 3)标题栏 显示内容:当前程序名称(Autodesk AutoCAD 2016)和文件名称 信息中心功能: 帮助搜索 Autodesk账户登录 软件更新检查 窗口控制(最小化/最大化/关闭) 4)菜单栏 显示设置:通过自定义快速访问工具栏→显示菜单栏选项启用 菜单结构:包含文件、编辑、视图、插入等11个主菜单项 命令示例: 绘图→直线:进入直线绘制模式 绘图→圆弧:提供三点、起点-圆心-端点等11种绘制方式 5)选项卡和面板 组织结构: 选项卡(默认/插入/注释等) 面板(绘图/修改/注释等) 命令按钮(直线/多段线/圆等) 操作流程:单击命令按钮→绘图区操作→Enter键确认 6)工具栏 调用方式:工具→工具栏→AutoCAD→选择所需工具栏 控制方法: 显示:勾选对应工具栏选项 隐藏:取消勾选或点击工具栏关闭按钮 示例操作:绘图工具栏包含直线、构造线等绘图工具按钮 7)绘图窗口 主要功能:核心绘图工作区域 导航控制: 滚动条调整视图 模型/布局空间切换 显示
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值