Android底部导航栏—FragmentTabHost+Fragment
2017-02-07 11:01
447 查看
介绍
Android开发中使用底部菜单栏的频次非常高,主要的实现手段有以下:
- TabWidget
- 隐藏TabWidget,使用RadioGroup和RadioButton
- FragmentTabHost
- 5.0以后的TabLayout
- 最近推出的 Bottom navigation
案例1:简单使用
简单使用 FragmentTabHost1 在布局文件使用 FragmentTabHost,并提供 Fragment 的容器
2 在 Activity 里查找 FragmentTabHost,并使用 setUp 方法关联到 Fragment 的容器
3 向 FragmentTabHost 里添加 Tab,注意 Tab 需要设置 indicate 文本
4 使用newInstance创建fragment
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="cn.itcast.demo.MainActivity"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#f0f"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_marginRight="10dp" android:src="@drawable/actionbar_search_icon" /> </android.support.v7.widget.Toolbar> <android.support.v4.widget.DrawerLayout android:id="@+id/drawerlayout" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#f00" android:orientation="vertical"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" //特殊之处,高设置0dp weight设置1 android:layout_height="0dp" android:layout_weight="1"></FrameLayout> <android.support.v4.app.FragmentTabHost android:id="@+id/tabhost" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" android:background="#00f"></LinearLayout> </android.support.v4.widget.DrawerLayout> </LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setTitle("heihehie"); getSupportActionBar().setDisplayHomeAsUpEnabled(true); DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawerlayout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.app_name,R.string.app_name); drawerLayout.addDrawerListener(toggle);// 面板展开的监听设置给 Toogle toggle.syncState();// 初始化 toogle 的绘制内容 // 设置底部栏 FragmentTabHost tabHost = (FragmentTabHost) findViewById(R.id.tabhost); tabHost.setup(this,getSupportFragmentManager(),R.id.container);// 初始化 tabhost // 添加一个 tab TabHost.TabSpec tab1 = tabHost.newTabSpec("news");// 创建tab对象 tab1.setIndicator("资讯");// 设置tab 内容 Bundle arg = new Bundle(); arg.putString("content","资讯界面"); tabHost.addTab(tab1,TestFragment.class,arg);// 将tab添加到底部栏 // 再添加一个 Tab TabHost.TabSpec tab2 = tabHost.newTabSpec("tweet"); tab2.setIndicator("动弹"); Bundle arg2 = new Bundle(); arg2.putString("content","动弹界面"); tabHost.addTab(tab2,TestFragment.class,arg2); } @Override // 为 Activity 生成菜单,ToolBar已经被设置为标题栏,这个菜单会自动显示到 ToolBar 上 public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main,menu); return super.onCreateOptionsMenu(menu); } @Override // 目录菜单的点击响应 public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.menu_search: Toast.makeText(this, "跳转到搜索界面", Toast.LENGTH_SHORT).show(); break; } return super.onOptionsItemSelected(item); } }
TestFragment
public class TestFragment extends Fragment { public static TestFragment newInstance(String content){ Bundle arg = getBundle(content); TestFragment fragment = new TestFragment(); fragment.setArguments(arg); return fragment; } @NonNull public static Bundle getBundle(String content) { Bundle arg = new Bundle(); arg.putString("content",content); return arg; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Bundle arg = getArguments(); String content = arg.getString("content"); TextView textView = new TextView(getActivity()); textView.setText(content); return textView; } }
案例 2
1.调用FragmentTabHost的setup方法;2.创建TabHost.TabSpec对象,每一个对象对应一个item;
3.调用FragmentTabHost的addTab方法,把TabHost.TabSpec对象添加进来。
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/realtabcontent" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" /> <android.support.v4.app.FragmentTabHost android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="wrap_content" > <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="0" /> </android.support.v4.app.FragmentTabHost> </LinearLayout>
这个文件中需要注意的是,FragmentTabHost跟它里面的FrameLayout中的id都是引入的id,而真正的id写在FragmentTabHost外面的FrameLayout中
tab_item代码如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <ImageView android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" > </ImageView> <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="2dp" android:textColor="@drawable/selector_tab_text" > </TextView> </LinearLayout>
我们定义了五个子item,每个item对应一张图片跟一个标题,相信上面的布局大家都能看懂。每个item对应一个fragment,那么接下来我们把fragment的代码写出来,这里因为f五个ragment的代码差不多,我只给出HomeFragment的代码。fragment_home.xml布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="首页Fragment" android:textSize="20sp" /> </LinearLayout></span>
HomeFragment.Java代码如下所示:
public class HomeFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_home, container, false); } }
HomeFragment.java,这个里面不用讲吧,就是把fragment_home.xml文件中的内容填充到fragment中。接下来,我们还要继续写状态选择器selector,Eclipse在res下新建一个文件夹drawable,里面创建5个图片状态选择器跟一个文字状态选择器,这里图片状态选择器我只贴出一个,selector_icon_home.xml代码如下所示:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Non focused states --> <item android:drawable="@drawable/icon_home" android:state_focused="false" android:state_pressed="false" android:state_selected="false"/> <item android:drawable="@drawable/icon_home_press" android:state_focused="false" android:state_pressed="false" android:state_selected="true"/> <!-- Focused states --> <item android:drawable="@drawable/icon_home_press" android:state_focused="true" android:state_pressed="false" android:state_selected="false"/> <item android:drawable="@drawable/icon_home_press" android:state_focused="true" android:state_pressed="false" android:state_selected="true"/> <!-- Pressed --> <item android:drawable="@drawable/icon_home_press" android:state_pressed="true" android:state_selected="true"/> <item android:drawable="@drawable/icon_home_press" android:state_pressed="true"/> </selector>
这里简单介绍一下,android:state_focused=”false”是没有聚焦状态,相反若为true,则为获得焦点状态。
android:state_pressed=”false”是没有按下状态,相反若为true,则为按下状态。
android;state_selected=”false”是没有被选择状态,相反则被选择。
最后,到了最重要的MainActivity,代码如下:
public class MainActivity extends FragmentActivity { /** * 定义FragmentTabHost对象 */ private FragmentTabHost mTabHost; /** * 定义一个布局填充器对象 */ private LayoutInflater mInflater; /** * 定义一个ArrayList来存放Tab */ private List<Tab> mTabs = new ArrayList<Tab>(5); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); initTab(); } /** * 初始化Tab */ private void initTab() { Tab tab_home = new Tab(HomeFragment.class, R.string.home, R.drawable.selector_icon_home); Tab tab_hot = new Tab(HotFragment.class, R.string.hot, R.drawable.selector_icon_hot); Tab tab_category = new Tab(CategoryFragment.class, R.string.catagory, R.drawable.selector_icon_category); Tab tab_cart = new Tab(CartFragment.class, R.string.cart, R.drawable.selector_icon_cart); Tab tab_mine = new Tab(MineFragment.class, R.string.mine, R.drawable.selector_icon_mine); mTabs.add(tab_home); mTabs.add(tab_hot); mTabs.add(tab_category); mTabs.add(tab_cart); mTabs.add(tab_mine); mInflater = LayoutInflater.from(this); mTabHost = (FragmentTabHost) this.findViewById(android.R.id.tabhost); mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent); for (Tab tab : mTabs) { TabHost.TabSpec tabSpec = mTabHost.newTabSpec(getString(tab.getTitle())); tabSpec.setIndicator(buildIndicator(tab)); mTabHost.addTab(tabSpec, tab.getFragment(), null); } } /** * 给Tab设置图标跟文字 * * @param tab * @return */ private View buildIndicator(Tab tab) { View view = mInflater.inflate(R.layout.tab_item, null); ImageView img = (ImageView) view.findViewById(R.id.imageview); TextView text = (TextView) view.findViewById(R.id.textview); img.setBackgroundResource(tab.getIcon()); text.setText(tab.getTitle()); return view; } }
看到MainActivity中的Tab了吗?我们创建一个package:com.example.fragmenttabhostdemo.bean,然后在该目录下新建一个java文件,Tab代码如下所示:
public class Tab { private int title; private int icon; private Class fragment; public Tab(Class fragment, int title, int icon) { super(); this.title = title; this.icon = icon; this.fragment = fragment; } public int getTitle() { return title; } public void setTitle(int title) { this.title = title; } public int getIcon() { return icon; } public void setIcon(int icon) { this.icon = icon; } public Class getFragment() { return fragment; } public void setFragment(Class fragment) { this.fragment = fragment; } }
案例3:彷新浪微博底部栏
1、主tab布局界面,main_tab_layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/realtabcontent" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" /> <android.support.v4.app.FragmentTabHost android:id="@android:id/tabhost" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/maintab_toolbar_bg"> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="0dp" android:layout_height="0dp" android:layout_weight="0" /> </android.support.v4.app.FragmentTabHost> </LinearLayout>
2、Tab按钮选项布局,tab_item_view.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:orientation="vertical" > <ImageView android:id="@+id/imageview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:focusable="false" android:padding="3dp" android:src="@drawable/tab_home_btn"> </ImageView> <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="首页" android:textSize="10sp" android:textColor="#ffffff"> </TextView> </LinearLayout>
3、fragment布局界面,这里只列出一个,fragment_1.xml:
<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageView android:id="@+id/imageview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="fitCenter" android:src="@drawable/xianjian01" > </ImageView> </LinearLayout></span>
4、Tab选项的自定义按钮资源文件,列出其中一个按钮,tab_home_btn:
<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/icon_home_sel" android:state_selected="true"/> <item android:drawable="@drawable/icon_home_nor"/> </selector></span>
5、Tab选项按钮背景资源文件,selector_tab_background.xml:
<span style="font-size:12px;"><?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/home_btn_bg" android:state_pressed="true"/> <item android:drawable="@drawable/home_btn_bg" android:state_selected="true"/> </selector></span>
6、主Activity类,MainTabActivity.Java:
public class MainTabActivity extends FragmentActivity{ //定义FragmentTabHost对象 private FragmentTabHost mTabHost; //定义一个布局 private LayoutInflater layoutInflater; //定义数组来存放Fragment界面 private Class fragmentArray[] = {FragmentPage1.class,FragmentPage2.class,FragmentPage3.class,FragmentPage4.class,FragmentPage5.class}; //定义数组来存放按钮图片 private int mImageViewArray[] = {R.drawable.tab_home_btn,R.drawable.tab_message_btn,R.drawable.tab_selfinfo_btn, R.drawable.tab_square_btn,R.drawable.tab_more_btn}; //Tab选项卡的文字 private String mTextviewArray[] = {"首页", "消息", "好友", "广场", "更多"}; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_tab_layout); initView(); } /** * 初始化组件 */ private void initView(){ //实例化布局对象 layoutInflater = LayoutInflater.from(this); //实例化TabHost对象,得到TabHost mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost); mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent); //得到fragment的个数 int count = fragmentArray.length; for(int i = 0; i < count; i++){ //为每一个Tab按钮设置图标、文字和内容 TabSpec tabSpec = mTabHost.newTabSpec(mTextviewArray[i]).setIndicator(getTabItemView(i)); //将Tab按钮添加进Tab选项卡中 mTabHost.addTab(tabSpec, fragmentArray[i], null); //设置Tab按钮的背景 mTabHost.getTabWidget().getChildAt(i).setBackgroundResource(R.drawable.selector_tab_background); } } /** * 给Tab按钮设置图标和文字 */ private View getTabItemView(int index){ View view = layoutInflater.inflate(R.layout.tab_item_view, null); ImageView imageView = (ImageView) view.findViewById(R.id.imageview); imageView.setImageResource(mImageViewArray[index]); TextView textView = (TextView) view.findViewById(R.id.textview); textView.setText(mTextviewArray[index]); return view; } }
7、Fragment页面,FragmentPage1.java:
public class FragmentPage1 extends Fragment{ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_1, null); } }
案例4:顶部栏导航
主要包括:
(1) 自定义Tab的图片资源和去掉分割线.
(2) 缓存Fragment的布局, 减少填充.
在切换页面时, 控件会调用Fragment的onCreateView, 重新创建页面.
通过缓存页面, 可以增强性能.
1. 布局
FragmentTabHost是原生控件, 并不需要添加其他的maven库.
包括标签组Tabs和页面TabContainer, 标签组固定大小, 页面填充.
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.app.FragmentTabHost android:id="@android:id/tabhost" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:layout_gravity="bottom"/> <FrameLayout android:id="@android:id/tabcontent" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1"/> </LinearLayout> </android.support.v4.app.FragmentTabHost>
注意控件的id必须是Android提供的标准id, 即”@android:id”.
Fragment布局, 包含一行文字提示.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tab_tv_text" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:textSize="40sp" tools:text="Test"/> </LinearLayout>
Tab布局, 包含一个图片控件.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/tab_iv_image" android:padding="12dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@null" tools:src="@drawable/tab_assistant"/> </LinearLayout>
2. 主页
setup设置页面组合, 却掉分割线etDividerDrawable(null), 设置Tab.
使用自定义的图片资源, newTabSpec设置Fragment的Tag标签.
/** * 主页, 切换Tab标签显示不同页面. * * @author C.L.Wang */ public class MainActivity extends AppCompatActivity { @Bind(android.R.id.tabhost) FragmentTabHost mTabHost; // 图片 @DrawableRes private int mImages[] = { R.drawable.tab_counter, R.drawable.tab_assistant, R.drawable.tab_contest, R.drawable.tab_center }; // 标题 private String mFragmentTags[] = { "counter", "assistant", "contest", "center" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent); mTabHost.getTabWidget().setDividerDrawable(null); // 去掉分割线 for (int i = 0; i < mImages.length; i++) { // Tab按钮添加文字和图片 TabHost.TabSpec tabSpec = mTabHost.newTabSpec(mFragmentTags[i]).setIndicator(getImageView(i)); // 添加Fragment mTabHost.addTab(tabSpec, FragmentTab.class, null); // 设置Tab按钮的背景 mTabHost.getTabWidget().getChildAt(i).setBackgroundResource(R.color.pedo_actionbar_bkg); } } // 获得图片资源 private View getImageView(int index) { @SuppressLint("InflateParams") View view = getLayoutInflater().inflate(R.layout.view_tab_indicator, null); ImageView imageView = (ImageView) view.findViewById(R.id.tab_iv_image); imageView.setImageResource(mImages[index]); return view; } }
3. 切换页
显示不同Tag标签. 缓存页面, 注意关联前, 删除父控件关联. 页面显示Tag信息.
public class FragmentTab extends Fragment { @Bind(R.id.tab_tv_text) TextView mTvText; private View mViewContent; // 缓存视图内容 @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { if (mViewContent == null) { mViewContent = inflater.inflate(R.layout.fragment_tab, container, false); } // 缓存View判断是否含有parent, 如果有需要从parent删除, 否则发生已有parent的错误. ViewGroup parent = (ViewGroup) mViewContent.getParent(); if (parent != null) { parent.removeView(mViewContent); } ButterKnife.bind(this, mViewContent); return mViewContent; } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 显示Fragment的Tag信息 mTvText.setText(String.valueOf("Page: " + getTag())); } @Override public void onDestroyView() { super.onDestroyView(); ButterKnife.unbind(this); } }
相关文章推荐
- Android博客挑错系列之一FragmentTabHost和ViewPager实现底部导航栏
- Android底部导航栏——FragmentTabHost+ViewPager+Fragment
- Android之底部導航欄--RadioGroup、TabHost、Fragment
- android底部选项卡(二)FragmentTabHost +Fragment 实现
- 【Android UI设计与开发】第08期:底部菜单栏(三)Fragment+FragmentTabHost实现仿新浪微博底部菜单栏
- Android基础入门教程——5.2.3 Fragment实例精讲——底部导航栏的实现(方法3)
- FragmentTabHost+viewPager实现底部导航栏
- Android TabHost +Fragment 实现底部菜单栏 .
- #Android笔记#fragment+fragmentTabHost实现底部菜单栏
- 【Android UI设计与开发】Fragment+FragmentTabHost实现仿新浪微博底部菜单栏
- (4.1.8.3)【Android UI设计与开发】第08期:底部菜单栏(三)Fragment+FragmentTabHost实现仿新浪微博底部菜单栏
- Android基础入门教程——5.2.2 Fragment实例精讲——底部导航栏的实现(方法2)
- 【Android UI设计与开发】第08期:底部菜单栏(三)Fragment+FragmentTabHost实现仿新浪微博底部菜单栏
- 『ANDROID』FragmentTabhost底部显示
- Android典型界面设计——FragmentTabHost+Fragment实现底部tab切换
- Android应用底部导航栏(选项卡)实例(TabHost&TabActivity)
- Android之底部導航欄--RadioGroup、TabHost、Fragment
- 【Android UI设计与开发】第08期:底部菜单栏(三)Fragment+FragmentTabHost实现仿新浪微博底部菜单栏
- 【Android UI设计与开发】第08期:底部菜单栏(三)Fragment+FragmentTabHost实现仿新浪微博底部菜单栏
- 【Android UI】FragmentTabHost 实现底部菜单