文档章节

Android实现通用的ActivityGroup(效果类似Android微博客户端主界面),...

嘴嘴2015
 嘴嘴2015
发布于 2013/04/18 12:10
字数 2051
阅读 416
收藏 36

2012050811241811

可以说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基本没有内容):

2012050811252853

具体的代码演示就差不多了,这里要做一些说明的:

  1.开发者在自己的实现类中的onCreate方法中,必须先设置视图,再调用super.oncreate(...)方法。具体为什么看了抽象类的源代码我相信读者应该会明白。

  2.关于导航按钮使用RadioButton。Android没有特意为我们定制适合我们在这种场合下使用的按钮,也就是上面可以设置简笔画,下面有文字说明。解决方案:1)使用ImageButton,将简笔画和文字说明P在一张图片里面,但这样有一个非常明显的弊端,文字说明的文字字体是固定的,是P在图片里的,那么不和系统的文字一样。如果用户使用一些比较花哨的系统文字,而导航按钮却是宋体,在上面的内容部分是他的系统文字,那么我很难想象他下一次是否还会打开您所开发的应用。2)自己去实现一个View,去代替RadioButton,出于学习目的这是好的。最佳的解决方案我还是认为是用RadioButton,只需对它稍做修改即可,具体可以参照新浪微博的资源文件。

  缺陷反思:这些代码都是我从重构得来的,当时开发的时候并没有设计好开发流程(我是先有那个实现类,才有了那个抽象类的)。自己写的ActivityGroup与TabActivity相比,优点显而易见,缺点就是可能不稳定,但暂时没有发现Bug,动态加载的Activity的逻辑代码都能正确执行。

© 著作权归作者所有

嘴嘴2015
粉丝 0
博文 12
码字总数 13344
作品 0
东城
私信 提问
加载中

评论(2)

春洋
春洋
收藏了
Glide
Glide
学习了。
ActivityGroup + GridView 实现Tab分页标签

本文转自http://blog.csdn.net/hellogv/ 很多客户端软件和浏览器软件都喜欢用Tab分页标签来搭建界面框架。读者也许会马上想到使用TabHost 与 TabActivity的组合,其实最常用的不是它们,而是...

JavaGG
2011/06/28
1K
1
Android入门第十五篇之ActivityGroup + GridView 实现Tab分页标签

本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处! 很多客户端软件和浏览器软件都喜欢用Tab分页标签来搭建界面框架。读者也许会马上想到使用TabHost 与 TabActivity的组合,其实最...

晨曦之光
2012/03/14
1K
0
Android实现不重复启动APP的方法

类似QQ、微信这样的APP,一般都不会打开两个实例。   比如:打开QQ客户端,进入到好友聊天界面,然后按HOME返回桌面 这时候有两种情况:   1、如果你在最近任务中切换会QQ客户端,那么还...

莫铭
2015/11/14
1K
1
怎样解决Tabhost与activitygroup点击返回键的冲突问题

我想让Tabhost一直显示在主界面,虽然已经用activitygroup实现了,但是这样就会破坏按返回键退出程序时弹出是否退出的对话框的效果。发现京东的android客户端已经实现了这样的功能,不知哪位...

沉虹默雨
2013/05/11
406
0
Android编码规范(转载)

一、Android编码规范 1.java代码中不出现中文,最多注释中可以出现中文 2.局部变量命名、静态成员变量命名 只能包含字母,单词首字母出第一个外,都为大写,其他字母都为小写 3.常量命名 只能...

xilinchen
2012/07/16
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Boot + Mybatis + Ehcache 二级缓存实例

二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕...

xiaolyuh
5分钟前
1
0
Spring源码学习(二)哎呦,按菜谱做菜与AbstractAutowireCapableBeanFactory.createBean流程差不多

记得跟老婆谈恋爱时,有一天心血来潮给老婆做饭,按照菜谱一步一步的做,结果差点把厨房烧了!!! 这事至今老婆还记得。 入口 上一篇说了,AbstractBeanFactory.getBean的主流程 ,今天来说下...

温安适
7分钟前
15
0
前端UI攻城狮 你们该抛弃jQuery了

你不再需要jQuery! Web工程师太依赖jQuery了,某种意义上说jQuery已经成了JavaScript的同义词。但是我们真的需要他么?或许我们应该反思一下什么时候才真的需要jQuery。 对我个人而言开始使...

前端老手
9分钟前
1
0
六、Java设计模式之工厂方法

工厂方法定义: 定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行 类型:创建型 工厂方法-使用场景: 创建对象需要大量重复的代码 ...

东风破2019
51分钟前
5
0
win服务器管理遇到的一系列问题记录

有些小伙伴在使用iis7远程桌面管理工具的时候总是会遇到一系列的问题,下面就是为大家介绍一下服务器日常管理过程中出现的问题及我的解决办法和心得。希望能帮到大家。   拒绝服务器重新启...

1717197346
58分钟前
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部