`
zhanglfat
  • 浏览: 91414 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

ActivityGroup 替代tabActivity

 
阅读更多
转载自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的逻辑代码都能正确执行。
分享到:
评论

相关推荐

    ActivityGroup 代替TabActivity的用法

    ActivityGroup 代替 TabActivity 的用法 Android 开发中,TabActivity 已经被废除,而使用 ActivityGroup 来代替 TabActivity,可以更加自由灵活,少很多限制和依赖。ActivityGroup 是 Google 提供的一个非常优秀的...

    tabhost和ActivityGroup的组合使用

    随着Android API的发展,现在更倾向于使用Fragment和FragmentTransaction来替代ActivityGroup,以实现更灵活和可维护的代码结构。理解和掌握这些知识点,对于Android开发者的技能树是不可或缺的。

    Android源码——TabHost内嵌ActivityGroup界面管理源码_new_17.7z

    总结来说,虽然现代Android开发倾向于使用Fragment替代ActivityGroup,但理解TabHost和ActivityGroup的历史以及它们的工作方式对于深入学习Android系统和历史演进非常重要。通过对"Android TabHost内嵌ActivityGroup...

    使用ActivityGroup来切换Activity和Layout

    `ActivityGroup`是`TabActivity`的一种替代方案,它允许我们在一个父`Activity`中嵌套多个子`Activity`,并在需要时动态替换内容。 首先,我们需要了解`ActivityGroup`的核心概念。`ActivityGroup`是`Sherlock`库中...

    ActivityGroup框架

    总的来说,ActivityGroup框架是Android早期对复杂界面组织的一种尝试,尽管现在已经被更先进的Fragment机制所替代,但对于理解Android应用的架构和生命周期管理仍有参考价值。在学习和使用过程中,应结合最新的开发...

    Android Fragment应用实战Demo,向ActivityGroup说再见 源码程序

    本教程通过一个名为"FragmentDemo"的实战项目,向我们展示了如何有效地使用Fragment替代旧式的ActivityGroup,以构建更加灵活和动态的应用程序界面。 Fragment的主要功能包括: 1. **模块化设计**:Fragment允许...

    TabActivity使用实例

    `TabActivity`是Android 2.x版本中的一部分,它基于`ActivityGroup`,允许在一个`Activity`中嵌套多个子`Activity`,每个子`Activity`对应一个标签页。这种方式现在看来并不推荐,因为它带来了复杂的生命周期管理...

    ActivityGroup demo

    ActivityGroup是在Android早期版本中,为了在TabHost或者TabActivity中实现嵌套Activity而设计的一种特殊类型的Activity。它允许在一个Activity内部启动其他Activity,形成一个嵌套的Activity结构。然而,随着...

    基于GridView和ActivityGroup实现的TAB分页

    在“基于GridView和ActivityGroup实现的Tab分页”场景下,ViewPager可以替代TabHost,用户可以在水平滑动时平滑地在各个页面之间切换。 6. 文件名“TabActivity”: 这可能是项目中的主Activity文件,负责设置和...

    ActivityGroup + GridView + ViewFlipper 实现选项卡.zip

    `ActivityGroup`继承自`TabActivity`,并提供了`LocalActivityManager`来管理和控制子`Activity`。然而,`ActivityGroup`在API Level 13已经被弃用,现在推荐使用`Fragment`或者`FragmentTabHost`来替代,因为`...

    自定义Tabctivity

    7. **`ActivityGroup`**: 在早期版本的Android中,`ActivityGroup`可以用来在一个`Activity`内管理多个子`Activity`。虽然现在已不推荐使用,但在自定义`TabActivity`的场景下,开发者可能会利用它来实现多`Tab`之间...

    Android入门之ActivityGroup+GridView实现Tab分页标签的方法

    传统的做法可能使用TabHost和TabActivity,但实际应用中,更常用的是结合GridView和ActivityGroup。这种组合方式允许用户在点击GridView的不同项时,动态加载相应的Activity内容。 首先,我们要理解ActivityGroup的...

    android的tabhost的一个例子

    总之,Android的TabHost结合ActivityGroup可以创建一个具有多页面选项卡的用户界面,虽然在现代Android开发中,通常建议使用Fragment替代ActivityGroup,但理解这种旧的实现方式对理解Android历史和一些老项目依然很...

    Android应用源码之TabActivityDemo.zip

    7. **Fragment**:在新的Android开发中,Fragment是替代ActivityGroup进行界面组件化的重要组件。虽然TabActivityDemo可能不包含Fragment的使用,但理解如何将Fragment与TabHost结合是现代Android开发的关键。 8. *...

    Android编程实现将tab选项卡放在屏幕底部的方法

    8. **替代方案**: - 如描述中提到,有时可能需要使用ButtonBar或自定义控件来达到类似效果,特别是当需要高度定制的底部导航栏时。Android Design Support Library的`BottomNavigationView` 或 Material Design ...

Global site tag (gtag.js) - Google Analytics