先看一下QQ、新浪微博、支付宝钱包这三个非常有名的应用,都有一个底部导航栏,我们一般称之‘选项卡’。google官方会叫他们为fixed tab,不过国内好像很好这么叫的。其实,在anroid 4.x时代,google官方更希望应用的导航放在顶部,通过滑屏和点击标签来切换界面。但是随着ios的的跟风以及用户习惯的养成,这种设计风格的形成也就变成历史遗留问题。在这里我们不讨论哪一个风格好,哪一个风格不好。做为开发人员,我们可能更关注这种司空见惯的界面设计是怎么实现的呢?
在android 2.x时代,我们可能会地使用ActivityGroup来实现这种,但是随着jelly bean的市场份额超过50%,我们会发现有一种新的组建出现了,它叫Fragment(http://developer.android.com/reference/android/app/Fragment.html)。而这种底部选项卡的风格界面的实现也由ActivityGroup转向了Fragment。先了,费话不多说了,下面我会一步一步教您怎么实现这个界面。在动手之前,我可能需要把我做好的样式图给你看一下,以遍让您有一个心里预期。
其实,我们这个界面的实现,基本没有什么java 代码。不信,你看下面就是主界面的代码:
public class MainActivity extends Activity { private FragmentManager fragmentManager; private RadioGroup radioGroup; @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.weibo_tab); fragmentManager = getFragmentManager(); radioGroup = (RadioGroup) findViewById(R.id.rg_tab); radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { FragmentTransaction transaction = fragmentManager.beginTransaction(); Fragment fragment = FragmentFactory.getInstanceByIndex(checkedId); transaction.replace(R.id.content, fragment); transaction.commit(); } }); } }
2.我们要准备这五个选项卡图片。包括正常和被按下两种状态。所以共十张图片。
3.你要为这五个按钮分别编写selector,文件名为weibolist_attention_selector.xml
<?xml version="1.0" encoding="utf-8"?><!-- 微博列表界面tab栏关注 按钮 --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/weibo_listab_attention_on" android:state_pressed="true" /> <item android:drawable="@drawable/weibo_listab_attention_on" android:state_checked="true" /> <item android:drawable="@drawable/weibo_listab_attention_off" /> </selector>这个xml文件挺简单的啊,但是细心的同学可能会地现,你这里为什么要用android:state_checked的啊?为什么不是其他的,比如android:state_selected哦
我想说,其实我这个底部用的是RadioGroup,我怎么判断当前选中的是哪个呢,不选中的是哪个呢。这里最好的选择就是使用RadioGroup,它的最大好处就是彼此的RadioButton具有互斥性,这样就费掉用java代码判断和处理的麻烦了。
4.下面,就要考虑为整个界面布局了。文件名为activity.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <FrameLayout android:id="@+id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" /> <RadioGroup android:id="@+id/rg_tab" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_weibo_listab"> <RadioButton style="@style/weibo_tab" android:drawableTop="@drawable/weibolist_attention_selector" android:text="微博" /> <RadioButton style="@style/weibo_tab" android:drawableTop="@drawable/weibolist_atme_selector" android:text="\@我" /> <RadioButton style="@style/weibo_tab" android:drawableTop="@drawable/weibolist_comment_selector" android:text="评论" /> <RadioButton style="@style/weibo_tab" android:drawableTop="@drawable/weibolist_mylist_selector" android:text="我的" /> <RadioButton style="@style/weibo_tab" android:drawableTop="@drawable/weibolist_global_selector" android:text="全站" /> </RadioGroup> </LinearLayout>这里可能就有几个问题啦
(1) FrameLayout是干什么的,可能用别的父控件吗?
这个是一个预留的内容控件,以后会用它去呈现Fragment的内容。理论上是可以用别的父控件的,没试过。
(2)android:weight=1,怎么只有一个呢,这样做有对LinearLayout有意义吗?
首先对FragLayout一定要写,不写就会变成这样。但是第二个RadioGroup的weight一定不要写,不然会很难调的。这个我猜测应该会有一个很好weight缺省值自适应的。
我只是猜测啦,具体原因求解释。
(3)weibo_tab样式里面什么内容?
什么内容,如下就可以啦:
<style name="weibo_tab"> <item name="android:layout_height">wrap_content</item> <item name="android:layout_width">match_parent</item> <item name="android:layout_weight">1</item> <item name="android:gravity">center</item> <item name="android:textSize">14sp</item> <item name="android:paddingTop">8dip</item> <item name="android:paddingBottom">4dip</item> <item name="android:background">@drawable/weibolist_bottombar_itembg_selector</item> <item name="android:textColor">@color/weibolist_bottombar_textcolor_selector</item> <item name="android:button">@null</item> </style>这个style的目地是为了把button相同的设置提取出来,进行很好地重构。
textColor,这是为是控制底部颜色变化的。button,这个一定要设置,否则就会这样
做到这里,我建议你就跑起来,试试效果。虽然没有用到Fragment,但是底部选项已经可以任意切换啦。
你是不是已经把它很好的搞定了呢,先恭喜你咯。
5.下一步我来内容填充的界面视图,文件名为fragment.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/txt_content" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:padding="10dp" android:text="" android:textSize="20sp" /> </RelativeLayout>这个没有什么解释的,略过。
6.下面我们做五个Fragment内容的呈现,如下:
先有一个BaseFragment抽象类,以被具体的子类继承和实现。
</pre><pre code_snippet_id="79670" snippet_file_name="blog_20131124_6_1564569" name="code" class="java">public abstract class BaseFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment, null); TextView textView = (TextView) view.findViewById(R.id.txt_content); textView.setText(initContent()); return view; } public abstract String initContent(); }
(1)关注界面
public class AttentionFragment extends BaseFragment { @Override public String initContent() { return "这是关注我界面"; } }(2)@界面
public class AtmeFragment extends BaseFragment { @Override public String initContent() { return "这是@我界面"; } }(3)评论界面
public class CommentFragment extends BaseFragment { @Override public String initContent() { return "这是评论我界面"; } }(4)我的界面
public class MyListFragment extends BaseFragment { @Override public String initContent() { return "这是我的列表界面"; } }(5)全部界面
public class GlobalFragment extends BaseFragment { @Override public String initContent() { return "这是全站界面"; } }
public class FragmentFactory { public static Fragment getInstanceByIndex(int index) { Fragment fragment = null; switch (index) { case 1: fragment = new AttentionFragment(); break; case 2: fragment = new AtmeFragment(); break; case 3: fragment = new CommentFragment(); break; case 4: fragment = new MyListFragment(); break; case 5: fragment = new GlobalFragment(); break; } return fragment; } }
9.哦,我们还是放上配置文件,贡大家参考。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mydream.fragment" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> <application android:allowBackup="true" android:label="@string/app_name" android:icon="@drawable/ic_launcher"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
以上代码可能会修改,请以github上的为准。谢谢
github:https://github.com/ihrthk/TabsFragment