Android学习系列(23)--App主界面实现

十度 Android 2015年12月01日 收藏

在上篇文章《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. //mainTabContainer是一个空布局,做为每个tab的容器
  2. //activity是每个tab对应的activity
  3. //getDecorView是对应的activity的视图,添加到tab容器中,就能实现切换activity的效果了
  4. mainTabContainer.removeAllViews();
  5. mainTabIntent = new Intent(this,activity);
  6. mainTabContainer.addView(localActivityManager.startActivity(id, mainTabIntent).getDecorView());

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

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

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

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

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

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


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

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

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

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

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

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

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

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