转自http://www.cnblogs.com/answer1991/archive/2012/05/08/2489844.html
ActivityGroup在实际的开发中是十分常见的,在我使用过的Android应用中,十个应用里面有九个应用的主界面都是使用ActivityGroup的。说起ActivityGroup,在国内好像直接使用它开发的并不多,基本都是使用TabActivity,它是ActivityGroup唯一的一个子类。Android端新浪微博的主界面就是用TabActivity来实现的,还有其它的一些应用也几乎都用TabActivity来实现。在我眼里,TabActivity是Google提供的一个非常失败的API(至少我现在这么认为,下文我会说它失败在哪里),但中国几乎所有的应用都使用TabActivity,我不禁在思考这是巧合还是复制粘贴的产物。使用ActivityGroup(或者说TabActivity)开发出来的主界面效果图如下(涉及版权问题,我这里就不粘微博的主界面了,我粘我自己的,虽然比较难看,有兴趣可以去参考新浪微博,微信等Android客户端):
可以说ActivityGroup是Google提供的一个非常优秀的API,但它需要做稍微复杂的重写才能用起来比较方便,本文拟将实现这个稍微复杂的重写。TabActivity作为ActivityGroup唯一的子类却让人大失所望。
首先来说ActivityGroup的优秀之处以及它的必要性,它为开发者提供了一种可能,这种可能不将Activity作为屏幕的顶级元素(Context)呈现,而是嵌入到ActivityGroup当中。这是一种极大的飞跃,它将场景(Context)细分化了,ActivityGroup是一个主场景,而用户可以通过导航按钮来切换想要的子场景。如使用微博功能,它是一个相当宏大的场景,具有看最新的广播信息、自己发微博、修改资料等子场景,用户可以通过按钮来切换到想要的子场景,而这个子场景仍活动于主场景之中。让一个主场景能拥有多个逻辑处理模块,主场景不再负责子场景逻辑,主场景只负责切换场景的逻辑,即每一个Activity(子场景)拥有一个逻辑处理模块,一个ActivityGroup有多个Activity,却不干预Activity的逻辑,这无疑细分化和模块化了逻辑代码。ActivityGroup和它将要内嵌的Activity所要实现的功能完全可以用只一个Activity来完成,你可以试想,当你把一个ActivityGroup和它所拥有的Activity的逻辑代码放在一个Activity中时,那这个Activity会拥有多少行代码,为维护带来非常的不便。
再来说说TabActivity的不足之处,首先,TabActivity自己独有的视图几乎没人使用(也就是难看的标签页按钮形式),国内开发者用到的特性几乎都是从ActivityGroup继承下来的。还有就是TabActivity的强制依赖关系,它的布局文件必须将TabHost作根标签,并且id必须为"@android:id/tabhost",必须有TabWidget标签,且它的id必须是"@android:id/tabs",还有加载Activity的View容器,id必须为@android:id/tabcontent。光是强制依赖关系,我就觉得不是很舒服。不仅仅是TabActivity,在一些特殊的Activity中,如ListActivity都存在这种强制依赖关系,ListActivity必须有id为xxx(想不起来了)的ListView,我想这些弊端应该获得Google开发者的重视。
那么我下面我就将自己实现ActivityGroup,告别强制依赖关系,并随心所欲的建立视图。下面这个类是一个抽象类,开发者只需对这个抽象类稍做修改,并加以实现自己的视图就能告别TabActivity。
package com.chenjun.demo.abstracttabactivity; import android.app.Activity; import android.app.ActivityGroup; import android.app.LocalActivityManager; import android.content.Intent; import android.os.Bundle; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.RadioButton; /** * 自己实现的一个通用ActivityGroup。 * 可以通过简单的重写它来制作有导航按钮和用导航按钮控制动态加载Activity的ActivityGroup。 * 开发者需要在实现类中实现三个方法: * 1.指定动态加载Activity的容器的对象,getContainer()方法。 * 2.初始化所有的导航按钮,initRadioBtns()方法,开发者要遍历所有的导航按钮并执行initRadioBtn(int id)方法。 * 3.实现导航按钮动作监听器的具体方法,onCheckedChanged(...)方法。这个方法将实现某个导航按钮与要启动对应的Activity的关联关系,可以调用setContainerView(...)方法。 * @author zet * */ public abstract class AbstractMyActivityGroup extends ActivityGroup implements CompoundButton.OnCheckedChangeListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initRadioBtns(); } //加载Activity的View容器,容器应该是ViewGroup的子类 private ViewGroup container; private LocalActivityManager localActivityManager; /** * 加载Activity的View容器的id并不是固定的,将命名规则交给开发者 * 开发者可以在布局文件中自定义其id,通过重写这个方法获得这个View容器的对象 * @return */ abstract protected ViewGroup getContainer(); /** * 供实现类调用,根据导航按钮id初始化按钮 * @param id */ protected void initRadioBtn(int id){ RadioButton btn = (RadioButton) findViewById(id); btn.setOnCheckedChangeListener(this); } /** * 开发者必须重写这个方法,来遍历并初始化所有的导航按钮 */ abstract protected void initRadioBtns(); /** * 为启动Activity初始化Intent信息 * @param cls * @return */ private Intent initIntent(Class<?> cls){ return new Intent(this, cls).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); } /** * 供开发者在实现类中调用,能将Activity容器内的Activity移除,再将指定的某个Activity加入 * @param activityName 加载的Activity在localActivityManager中的名字 * @param activityClassTye 要加载Activity的类型 */ protected void setContainerView(String activityName, Class<?> activityClassTye){ if(null == localActivityManager){ localActivityManager = getLocalActivityManager(); } if(null == container){ container = getContainer(); } //移除内容部分全部的View container.removeAllViews(); Activity contentActivity = localActivityManager.getActivity(activityName); if (null == contentActivity) { localActivityManager.startActivity(activityName, initIntent(activityClassTye)); } //加载Activity container.addView( localActivityManager.getActivity(activityName) .getWindow().getDecorView(), new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); } }
需要重写的方法以及为什么需要重写我都已在原代码中标明。下面我们来具体的实现这个类,来达到我们想要的预期。
package com.chenjun.demo.abstracttabactivity; import android.os.Bundle; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.RadioButton; public class TestMyActivityGroup extends AbstractMyActivityGroup{ //加载的Activity的名字,LocalActivityManager就是通过这些名字来查找对应的Activity的。 private static final String CONTENT_ACTIVITY_NAME_0 = "contentActivity0"; private static final String CONTENT_ACTIVITY_NAME_1 = "contentActivity1"; private static final String CONTENT_ACTIVITY_NAME_2 = "contentActivity2"; private static final String CONTENT_ACTIVITY_NAME_3 = "contentActivity3"; private static final String CONTENT_ACTIVITY_NAME_4 = "contentActivity4"; @Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.my_activity_group); super.onCreate(savedInstanceState); ((RadioButton)findViewById(R.id.radio_button0)).setChecked(true); } /** * 找到自定义id的加载Activity的View */ @Override protected ViewGroup getContainer() { return (ViewGroup) findViewById(R.id.container); } /** * 初始化按钮 */ @Override protected void initRadioBtns() { initRadioBtn(R.id.radio_button0); initRadioBtn(R.id.radio_button1); initRadioBtn(R.id.radio_button2); initRadioBtn(R.id.radio_button3); initRadioBtn(R.id.radio_button4); } /** * 导航按钮被点击时,具体发生的变化 */ @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { switch (buttonView.getId()) { case R.id.radio_button0: setContainerView(CONTENT_ACTIVITY_NAME_0, ContentActivity0.class); break; case R.id.radio_button1: setContainerView(CONTENT_ACTIVITY_NAME_1, ContentActivity1.class); break; case R.id.radio_button2: setContainerView(CONTENT_ACTIVITY_NAME_2, ContentActivity2.class); break; case R.id.radio_button3: setContainerView(CONTENT_ACTIVITY_NAME_3, ContentActivity3.class); break; case R.id.radio_button4: setContainerView(CONTENT_ACTIVITY_NAME_4, ContentActivity4.class); break; default: break; } } } }
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginTop="0.0px" xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:id="@+id/container" android:layout_width="fill_parent" android:layout_height="0.0dip" android:layout_weight="1.0" /> <RadioGroup android:gravity="center_vertical" android:layout_gravity="bottom" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/radio_button0" android:layout_marginTop="2.0dip" android:text="按钮1" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_call" /> <RadioButton android:id="@+id/radio_button1" android:layout_marginTop="2.0dip" android:text="按钮2" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_camera" /> <RadioButton android:id="@+id/radio_button2" android:layout_marginTop="2.0dip" android:text="按钮3" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_agenda" /> <RadioButton android:id="@+id/radio_button3" android:layout_marginTop="2.0dip" android:text="按钮4" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_delete" /> <RadioButton android:id="@+id/radio_button4" android:layout_marginTop="2.0dip" android:text="按钮5" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_help" /> </RadioGroup> </LinearLayout> </LinearLayout>
具体的实现效果(这里Activity基本没有内容的,就加了一行字):
具体的代码演示就差不多了,这里要做一些说明的:
1.开发者在自己的实现类中的onCreate方法中,必须先设置视图,再调用super.oncreate(...)方法。具体为什么看了抽象类的源代码我相信读者应该会明白。
2.关于导航按钮使用RadioButton。Android没有特意为我们定制适合我们在这种场合下使用的按钮,也就是上面可以设置简笔画,下面有文字说明。解决方案:1)使用ImageButton,将简笔画和文字说明P在一张图片里面,但这样有一个非常明显的弊端,文字说明的文字字体是固定的,是P在图片里的,那么不和系统的文字一样。如果用户使用一些比较花哨的系统文字,而导航按钮却是宋体,在上面的内容部分是他的系统文字,那么我很难想象他下一次是否还会打开您所开发的应用。2)自己去实现一个View,去代替RadioButton,出于学习目的这是好的。最佳的解决方案我还是认为是用RadioButton,只需对它稍做修改即可,具体可以参照新浪微博的资源文件。
缺陷反思:这些代码都是我从重构得来的,当时开发的时候并没有设计好开发流程(我是先有那个实现类,才有了那个抽象类的)。自己写的ActivityGroup与TabActivity相比,优点显而易见,缺点就是可能不稳定,但暂时没有发现Bug,动态加载的Activity的逻辑代码都能正确执行。
相关推荐
Android TabHost内嵌ActivityGroup界面管理源码.zip源码资源下载Android TabHost内嵌ActivityGroup界面管理源码.zip源码资源下载
"Android Gallery+ActivityGroup实现滑动TAB独立Activity"是一个设计模式,它利用Android的Gallery组件和ActivityGroup来达到这一目的。在这个模式中,每个Tab都对应一个独立的Activity,从而提供更灵活和丰富的用户...
在Android开发中,TabHost和ActivityGroup是两个关键组件,常用于实现多页面、多视图的应用界面。这里我们深入探讨这两个组件以及如何将它们结合使用来管理界面。 TabHost是Android提供的一种UI控件,它允许开发者...
在Android应用开发中,ActivityGroup、GridView和ViewFlipper是三个关键组件,它们共同作用可以构建出具有选项卡切换功能的用户界面。本教程将详细解释这三个组件的工作原理及其在实际项目中的应用。 首先,...
这个压缩包"Android TabHost内嵌ActivityGroup界面管理源码.zip"提供了具体的实现示例,帮助开发者理解这种复杂的界面管理机制。 TabHost的工作原理是通过一个名为TabWidget的组件显示标签,而另一个名为...
在Android开发中,TabHost和ActivityGroup是两个关键组件,常用于实现多页面的切换和管理,特别是构建具有标签页式界面的应用。本资源“Android TabHost内嵌ActivityGroup界面管理源码.rar”提供了一个实际的示例,...
在Android开发中,ActivityGroup是一种特殊的Activity容器,它允许在一个Activity内部嵌套多个子Activity,形成类似多级菜单或者Tab效果。本示例"android ActivityGroup的demo"将深入探讨如何使用ActivityGroup来...
在Android应用开发中,TabHost和ActivityGroup是两个重要的组件,它们被用来实现多标签页的界面管理和在各个标签页间切换活动。本压缩包提供的源码是关于如何在Android项目中利用TabHost内嵌ActivityGroup来管理多个...
在Android开发中,TabHost和ActivityGroup是两个重要的组件,常用于实现多页面的应用界面管理。这份"Android TabHost内嵌ActivityGroup界面管理源码"是针对学生毕业设计的学习资源,旨在帮助他们理解和掌握这两种...
Android TabHost内嵌ActivityGroup界面管理源码.zip项目安卓应用源码下载Android TabHost内嵌ActivityGroup界面管理源码.zip项目安卓应用源码下载 1.适合学生毕业设计研究参考 2.适合个人学习研究参考 3.适合公司...
在Android开发中,TabHost和ActivityGroup是两个关键组件,常用于实现多页面的导航和界面管理。在本文中,我们将深入探讨如何使用TabHost内嵌ActivityGroup来创建复杂的用户界面,并通过源码分析理解其工作原理。 ...
ActivityGroup的用法,头部和底部为固定布局中间动态显示界面 并在中间布局实现手势滑动效果,自定义SlidingDrawer(抽屉)效果,在ActivityGroup里执行跳转等功能。至于在Android在ActivityGroup里执行跳转详细介绍 ...
这份"安卓Android源码——ActivityGroup + GridView + ViewFlipper 实现选项卡.rar"的压缩包文件,显然是提供了一个示例,用于演示如何结合这些组件来创建选项卡式的用户界面。 首先,我们来详细了解这三个核心概念...
本资源“安卓Android源码——TabHost内嵌ActivityGroup界面管理源码.zip”提供了一个实例,帮助开发者理解如何在TabHost中嵌入ActivityGroup来实现复杂的界面管理。 TabHost是Android SDK提供的一种用于创建多标签...