在上篇文章《Android学习系列(22)--App主界面比较》中我们浅略的分析了几个主界面布局,选了一个最大众化的经典布局。
今天我们就这个经典布局,用代码具体的实现它。

1.预览图
先看下最终的界面设计图:

  

上面顶部是一个9patch背景图片+标题文字;
下面底部是5个tab标签,表示应用的5大模块。
中间内容部分则是各个模块的具体内容,可以再分类,或者直接显示内容。

2.准备素材
按照上篇文章的界面,我们需要事先提供两大方面的素材:顶部+底部。
顶部的素材非常简单,最重要的是背景(9patch的图片):


底部的素材稍微多一点:
(1).每个tab的背景都需要正常和选中两种,一共10张图片;
(2).每个tab之间有一张分割线,1张图片;
(3).为了自适应屏幕宽度,并保持图形不变形,必须tab背景和下面botton这个背景色一致,所以需要1张同背景的背景图片。
如下:
(1).

         

(2).

(3).

在这里呢,我再三考量,决定还是把图片和文字放在一起,这样一能大大降低代码的复杂性,而且能保证漂亮的样式,我们通过Photoshop来控制,灵活性大大增强。
以上是我在网上随便找了几张照片,稍微处理了一下,作为下面我们实现的素材。

3.实现原理
这里,我采用了getDecorView方法,发现这种方法布局和代码比较简洁,看上去性能也不错(待查)。
用核心代码来说明一下原理:

1
2
3
4
5
6
//mainTabContainer是一个空布局,做为每个tab的容器
//activity是每个tab对应的activity
//getDecorView是对应的activity的视图,添加到tab容器中,就能实现切换activity的效果了
mainTabContainer.removeAllViews();
mainTabIntent = new Intent(this,activity);
mainTabContainer.addView(localActivityManager.startActivity(id, mainTabIntent).getDecorView());

  通过切换不同的activity的decorView,实现tab的视图切换。

4.基本框架
布局界面思路非常清晰,顶部+底部+中间tab内容
我采用相对布局(相对于线性布局,我经常选择帧布局和相对布局,我更喜欢这两个小巧的布局):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout android:id="@+id/main_tab_banner"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dip"
        android:orientation="horizontal"
        android:gravity="center"
        android:background="@drawable/main_banner_bg"
        android:layout_alignParentTop="true">
        <!-- 标题 -->
    </LinearLayout>
    <LinearLayout android:id="@+id/main_tab"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:background="@drawable/tab_bg"
        android:layout_alignParentBottom="true">
        <!-- 内容 -->
    </LinearLayout>
    <LinearLayout android:id="@+id/main_tab_container"
        android:layout_above="@id/main_tab"
        android:layout_below="@id/main_tab_banner"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#FFFFE0">
    </LinearLayout>
</RelativeLayout>

看起来很复杂的东西,分解一下就简单的多了。
在标题处,加上一个TextView,做为标题显示。
在内容处,我们需要填充5个Tab背景和1个分割线,请参考《Android学习系列(5)--App布局初探之简单模型》 中的模型四,使用了layout_weight的属性,平均分割了5个tab.
最终我们的布局文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <LinearLayout android:id="@+id/main_tab_banner"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dip"
        android:orientation="horizontal"
        android:gravity="center"
        android:background="@drawable/main_banner_bg"
        android:layout_alignParentTop="true">
        <TextView android:id="@+id/main_tab_banner_title"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="欣赏美花"
            android:textSize="20dip"
            android:textColor="#000000"/>
    </LinearLayout>
    <LinearLayout android:id="@+id/main_tab"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:background="@drawable/tab_bg"
        android:layout_alignParentBottom="true">
        <ImageView android:id="@+id/appreciate_tab_btn"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:gravity="center_horizontal|bottom"
            android:src="@drawable/appreciate_press"/>
        <ImageView android:gravity="center"
            android:layout_gravity="center_vertical"
            android:layout_width="5dip"
            android:layout_height="wrap_content"
            android:src="@drawable/tab_split"/>
        <ImageView android:id="@+id/discuss_tab_btn"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal|bottom"
            android:textSize="16dip"
            android:src="@drawable/discuss_normal"
            android:textColor="#000000"/>
        <ImageView android:gravity="center"
            android:layout_gravity="center_vertical"
            android:layout_width="5dip"
            android:layout_height="wrap_content"
            android:src="@drawable/tab_split"/>
        <ImageView android:id="@+id/identification_tab_btn"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal|bottom"
            android:textSize="16dip"
            android:src="@drawable/identification_normal"
            android:textColor="#000000"/>
        <ImageView android:gravity="center"
            android:layout_gravity="center_vertical"
            android:layout_width="5dip"
            android:layout_height="wrap_content"
            android:src="@drawable/tab_split"/>
        <ImageView android:id="@+id/favorite_tab_btn"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal|bottom"
            android:textSize="16dip"
            android:textColor="#000000"
            android:src="@drawable/favorite_normal"/>
        <ImageView android:gravity="center"
            android:layout_gravity="center_vertical"
            android:layout_width="5dip"
            android:layout_height="wrap_content"
            android:src="@drawable/tab_split"/>
        <ImageView android:id="@+id/setting_tab_btn"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal|bottom"
            android:textSize="16dip"
            android:src="@drawable/setting_normal"
            android:textColor="#000000"/>
    </LinearLayout>
    <LinearLayout android:id="@+id/main_tab_container"
        android:layout_above="@id/main_tab"
        android:layout_below="@id/main_tab_banner"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#FFFFE0">
    </LinearLayout>
</RelativeLayout>

其中的main_tab_container是容器布局,到时候动态存放切换的activity的视图。
这时候,效果图如下:


中间的内容为空,tab点击也没有任何效果,我们继续实现。
这就是布局文件main_tab_frame.xml.

5.事件效果
现在我们把点击效果,切换标题,这些效果关联起来。
选择不同的tab,显示不同的标题,同时切换不同的activity.
以点击评花的主要代码为例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//评花
discussImageView.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        //标题
        mainTabTitleTextView.setText("评花论花");
        //切换内容
        setContainerView("discuss", DiscussTabActivity.class);
        //切换tab页背景                  
        appreciateImageView.setImageResource(R.drawable.appreciate_normal);
        discussImageView.setImageResource(R.drawable.discuss_press);
        identificationImageView.setImageResource(R.drawable.identification_normal);
        favoriteImageView.setImageResource(R.drawable.favorite_normal);
        settingImageView.setImageResource(R.drawable.setting_normal);
    }
});
//切换activity
public void setContainerView(String id,Class<?> activity){
    mainTabContainer.removeAllViews();
    mainTabIntent = new Intent(this,activity);
    mainTabContainer.addView(localActivityManager.startActivity(id, mainTabIntent).getDecorView());
}

我们继承ActivityGroup这个类,实现这个完整的类MainTabFrame.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public class MainTabFrame extends ActivityGroup {
     
    //Tab Activity Layout
    private LocalActivityManager localActivityManager = null;
    private LinearLayout mainTabContainer = null;
    private Intent mainTabIntent = null;
 
    //Tab banner title
    private TextView mainTabTitleTextView = null;
    //Tab ImageView
    private ImageView appreciateImageView = null;
    private ImageView discussImageView = null;
    private ImageView identificationImageView = null;
    private ImageView favoriteImageView = null;
    private ImageView settingImageView = null;
     
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_tab_frame);
         
        mainTabContainer = (LinearLayout)findViewById(R.id.main_tab_container);
        localActivityManager = getLocalActivityManager();
        setContainerView("appreciate", AppreciateTabActivity.class);
         
        initTab();
         
    }
 
     
    /**
     * 初始化Tab项
     */
    private void initTab() {
        mainTabTitleTextView = (TextView)findViewById(R.id.main_tab_banner_title);
        appreciateImageView = (ImageView)findViewById(R.id.appreciate_tab_btn);
        discussImageView = (ImageView)findViewById(R.id.discuss_tab_btn);
        identificationImageView = (ImageView)findViewById(R.id.identification_tab_btn);
        favoriteImageView = (ImageView)findViewById(R.id.favorite_tab_btn);
        settingImageView = (ImageView)findViewById(R.id.setting_tab_btn);
         
        //赏花
        appreciateImageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mainTabTitleTextView.setText("欣赏美花");
                setContainerView("appreciate", AppreciateTabActivity.class);
                appreciateImageView.setImageResource(R.drawable.appreciate_press);
                discussImageView.setImageResource(R.drawable.discuss_normal);
                identificationImageView.setImageResource(R.drawable.identification_normal);
                favoriteImageView.setImageResource(R.drawable.favorite_normal);
                settingImageView.setImageResource(R.drawable.setting_normal);
            }
        });
         
        //评花
        discussImageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mainTabTitleTextView.setText("评花论花");
                setContainerView("discuss", DiscussTabActivity.class);
                appreciateImageView.setImageResource(R.drawable.appreciate_normal);
                discussImageView.setImageResource(R.drawable.discuss_press);
                identificationImageView.setImageResource(R.drawable.identification_normal);
                favoriteImageView.setImageResource(R.drawable.favorite_normal);
                settingImageView.setImageResource(R.drawable.setting_normal);
            }
        });
         
        //识花
        identificationImageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mainTabTitleTextView.setText("亮眼识花");
                setContainerView("identification", IdentificationTabActivity.class);
                appreciateImageView.setImageResource(R.drawable.appreciate_normal);
                discussImageView.setImageResource(R.drawable.discuss_normal);
                identificationImageView.setImageResource(R.drawable.identification_press);
                favoriteImageView.setImageResource(R.drawable.favorite_normal);
                settingImageView.setImageResource(R.drawable.setting_normal);
            }
        });
         
        //收藏
        favoriteImageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mainTabTitleTextView.setText("我的收藏");
                setContainerView("favorite", FavoriteTabActivity.class);
                appreciateImageView.setImageResource(R.drawable.appreciate_normal);
                discussImageView.setImageResource(R.drawable.discuss_normal);
                identificationImageView.setImageResource(R.drawable.identification_normal);
                favoriteImageView.setImageResource(R.drawable.favorite_press);
                settingImageView.setImageResource(R.drawable.setting_normal);
            }
        });
         
        //设置
        settingImageView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mainTabTitleTextView.setText("定义设置");
                setContainerView("setting", SettingTabActivity.class);
                appreciateImageView.setImageResource(R.drawable.appreciate_normal);
                discussImageView.setImageResource(R.drawable.discuss_normal);
                identificationImageView.setImageResource(R.drawable.identification_normal);
                favoriteImageView.setImageResource(R.drawable.favorite_normal);
                settingImageView.setImageResource(R.drawable.setting_press);
            }
        });
    }
     
    public void setContainerView(String id,Class<?> activity){
        mainTabContainer.removeAllViews();
        mainTabIntent = new Intent(this,activity);
        mainTabContainer.addView(localActivityManager.startActivity(id, mainTabIntent).getDecorView());
    }
}

  具体的每个activity怎么显示的,再通过AppreciateTabActivity,DiscussTabActivity,IdentificationTabActivity,FavoriteTabActivity,SettingTabActivity这些独自实现,不再累述。

6.扩展建议
这里补充两点:
(1).标题栏在上述示例中,我是放在MainTabFrame,这样做的好处是,统一了,方便了;这样做的缺点是,如果每个activity的标题栏是不同的按钮,不同的操作,会有些膨胀。所以,标题栏放在主Acvtivity和子Activity中,考虑一下即可。
(2).tab的切换效果,我做的非常简单,具体的图片阴影,凹凸,文字色彩区分都没有去做(本人对Photoshop实在不熟),美化方面还可以大大改进。

7.小结 
通过实现这么个简单的主界面框架,能使我们快速的开始我们相应的感兴趣项目,提供了一种大众化得参考,是android学习者必备基础。 
这种东西的积累和分析也是能提高我们感觉应用的审美感。

Android学习系列(23)--App主界面实现的更多相关文章

  1. Android学习系列(22)--App主界面比较

    本文算是一篇漫谈,谈一谈当前几个流行应用的主界面布局,找个经典的布局我们自己也来实现一个.不是为了追求到底有多难,而是为了明白我们确实需要这么做. 走个题,android的UI差异化市场依然很大,依然 ...

  2. Android学习系列(7)--App轮询服务器消息

    这篇文章是android开发人员的必备知识. 1.轮询服务器     一般的应用,定时通知消息可以采用轮询的方法从服务器拿取消息,当然实时消息通知的话,建议采用推送服务.    其中需要注意轮询的频率 ...

  3. Android学习系列(15)--App列表之游标ListView(索引ListView)

    游标ListView,提供索引标签,使用户能够快速定位列表项.      也可以叫索引ListView,有的人称也为Tweaked ListView,可能更形象些吧.      一看图啥都懂了: 1. ...

  4. Android学习系列(17)--App列表之圆角ListView(续)

    http://www.cnblogs.com/qianxudetianxia/archive/2011/09/19/2068760.html   本来这篇文章想并到上篇Android学习系列(16)- ...

  5. Android学习系列(10)--App列表之拖拽ListView(上)

     研究了很久的拖拽ListView的实现,受益良多,特此与尔共飨.      鉴于这部分内容网上的资料少而简陋,而具体的实现过程或许对大家才有帮助,为了详尽而不失真,我们一步一步分析,分成两篇文章. ...

  6. Android学习系列(18)--App工程结构搭建

     本文算是一篇漫谈,谈一谈关于Android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构.      关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的 ...

  7. Android学习系列(11)--App列表之拖拽ListView(下)

    接着上篇Android学习系列(10)--App列表之拖拽ListView(上)我们继续实现ListView的拖拽效果. 7.重写onTouchEvent()方法.     在这个方法中我们主要是处理 ...

  8. Android学习系列(3)--App自动更新之自定义进度视图和内部存储

    友好的视觉感知和稳定的不出错表现,来自于我们追求美感和考虑的全面性,博客园从技术的角度,一直我都很欣赏.这篇文章是android开发人员的必备知识,是我特别为大家整理和总结的,不求完美,但是有用. 这 ...

  9. Android学习系列(37)--App调试内存泄露之Context篇(下)

    接着<Android学习系列(36)--App调试内存泄露之Context篇(上)>继续分析. 5. AsyncTask对象 我N年前去盛大面过一次试,当时面试官极力推荐我使用AsyncT ...

随机推荐

  1. Nginx upstream的5种权重分配方式

    .轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,后端服务器down掉,能自动剔除 .weight 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况. upstre ...

  2. WebApi中帮助页Description的中文显示

    转自:http://edi.wang/post/2013/10/28/auto-generate-help-document-aspnet-webapi 我选择Web API的一个重要原因就是因为可以 ...

  3. Excel与SqlServer的导入导出问题总结

    1.Excel导入到SqlServer中,如果Excel里面的某一列,即有文本,又有数字,导入SqlServer中这一列的类型将会是float,Excel里面对应的文本值将导不进来,为null. 解决 ...

  4. Hibernate从入门到精通(四)基本映射

    映射的概念 在上次的博文Hibernate从入门到精通(三)Hibernate配置文件我们已经讲解了一下Hibernate中的两种配置文件,其中提到了两种配置文件的主要区别就是XML可以配置映射.这里 ...

  5. NSInvocation的使用(转)

    转载自:http://www.cnblogs.com/pengyingh/articles/2359199.html http://blog.iosxcode4.com/?p=125 在 iOS中可以 ...

  6. 1063: [Noi2008]道路设计 - BZOJ

    Description Z 国坐落于遥远而又神奇的东方半岛上,在小Z 的统治时代公路成为这里主要的交通手段.Z 国共有n 座城市,一些城市之间由双向的公路所连接.非常神奇的是Z 国的每个城市所处的经度 ...

  7. uva 165

    回溯  参考了一下别人的解法  1 必须存在  再枚举下一个数字的时候  从当前可取到的最小数字加一枚举到当前可取到的最大数字加一 /********************************* ...

  8. Kafka 之 async producer (2) kafka.producer.async.DefaultEventHandler

    上次留下来的问题 如果消息是发给很多不同的topic的, async producer如何在按batch发送的同时区分topic的 它是如何用key来做partition的? 是如何实现对消息成批量的 ...

  9. sql删除wordpress没用的postmeta记录

    支持多作者的wordpress博客,有时需要审核他们的文章内容,虽然UGC(User-generated content)整体是好的,但是也要控制一下质量,实在不相关或spam的文章就要毫不手软的删除 ...

  10. AAC ADTS AAC LATM 格式分析

    http://blog.csdn.net/tx3344/article/details/7414543# 目录(?)[-] ADTS是个啥 ADTS内容及结构 将AAC打包成ADTS格式 1.ADTS ...