Fragment + TextView 实现底部导航栏
经过前几章节的学习,我们对 Fragment
已经有了很深的了解,本章开始,我们将利用一些范例来加深对 Fragment
的印象
我们将使用 Fragment
来实现一个底部导航栏
实现底部导航栏方法有很多,比如全用 TextView
做,或者用 RadioButton
,又或者使用 TabLayout + RadioButton
但本章节使用 TextView
来实现
我们先来看看最后的效果图
-
创建一个 空的 Android 项目
cn.twle.android.FragmentTextView
-
下载解压 /static/i/android/fragment_tab_demo.zip,并把所有的图片拖到
res/drawable
目录下 -
从效果图上可以看到,底部的每一项点击的时候都有不同的效果,这些效果是通过判定是否
selected
来实现的,所以我们要对每一个tab
创建 TextView 状态的资源文件在
res/drawable
目录下新建四个文件tab_menu_home.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/tab_home_pressed" android:state_selected="true" /> <item android:drawable="@drawable/tab_home" /> </selector>
tab_menu_category.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/tab_category_pressed" android:state_selected="true" /> <item android:drawable="@drawable/tab_category" /> </selector>
tab_menu_cart.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/tab_cart_pressed" android:state_selected="true" /> <item android:drawable="@drawable/tab_cart" /> </selector>
tab_menu_discover.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/tab_discover_pressed" android:state_selected="true" /> <item android:drawable="@drawable/tab_discover" /> </selector>
-
在
res/drawable
目录下新建文件tab_menu_text.xml
创建文字资源因为文字也会根据是否选中而改变颜色
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="#1296db" android:state_selected="true" /> <item android:color="#515151" /> </selector>
-
在
res/drawable
目录下新建文件tab_menu_bg.xml
创建背景资源选中的 tab 背景会有点灰色
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="true"> <shape> <solid android:color="#ffdddddd" /> </shape> </item> <item> <shape> <solid android:color="#00FFFFFF" /> </shape> </item> </selector>
-
在
res/layout
目录下创建一个Fragment
的简单布局 fg_content.xml<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffff"> <TextView android:id="@+id/ms_txt_content" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:text="简单教程,简单编程" android:textColor="#333333" android:textSize="20sp"/> </LinearLayout>
-
资源方面我们已经准备的差不多了,现在开始添加布局
仔细看效果图,我们可以把整个页面分成三大块
- 顶部的 title 栏
- 中间的具体页面
- 底部的 tabbar
-
修改
activity_main.xml
添加布局布局<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:id="@+id/ms_topbar" android:layout_width="match_parent" android:layout_height="48dp" android:background="#FCFCFC"> <TextView android:id="@+id/ms_topbar_title" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:gravity="center" android:textSize="18sp" android:textColor="#694E42" android:text="信息"/> <View android:layout_width="match_parent" android:layout_height="2px" android:background="#E5E5E5" android:layout_alignParentBottom="true"/> </RelativeLayout> <LinearLayout android:id="@+id/ms_tab_bar" android:layout_width="match_parent" android:layout_height="56dp" android:layout_alignParentBottom="true" android:background="#ffffff" android:orientation="horizontal"> <TextView android:id="@+id/ms_tab_home" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/tab_menu_bg" android:drawablePadding="3dp" android:drawableTop="@drawable/tab_menu_home" android:gravity="center" android:padding="5dp" android:text="首页" android:textColor="@drawable/tab_menu_text" android:textSize="16sp" /> <TextView android:id="@+id/ms_tab_category" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/tab_menu_bg" android:drawablePadding="3dp" android:drawableTop="@drawable/tab_menu_category" android:gravity="center" android:padding="5dp" android:text="分类" android:textColor="@drawable/tab_menu_text" android:textSize="16sp" /> <TextView android:id="@+id/ms_tab_discover" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/tab_menu_bg" android:drawablePadding="3dp" android:drawableTop="@drawable/tab_menu_discover" android:gravity="center" android:padding="5dp" android:text="发现" android:textColor="@drawable/tab_menu_text" android:textSize="16sp" /> <TextView android:id="@+id/ms_tab_cart" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:background="@drawable/tab_menu_bg" android:drawablePadding="3dp" android:drawableTop="@drawable/tab_menu_cart" android:gravity="center" android:padding="5dp" android:text="购物车" android:textColor="@drawable/tab_menu_text" android:textSize="16sp"/> </LinearLayout> <View android:id="@+id/div_tab_bar" android:layout_width="match_parent" android:layout_height="2px" android:background="#E5E5E5" android:layout_above="@id/ms_tab_bar"/> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/ms_topbar" android:layout_above="@id/div_tab_bar" android:id="@+id/ms_content"> </FrameLayout> </RelativeLayout>
-
首先定义顶部标题栏的样式,
48dp
的LinearLayout
中间加上一个TextView
作为标题 -
接着定义一个大小为
56dp
的LinerLayout
对齐底部,在这个里面加入四个`TextView
,比例1:1:1:1
并且设置相关属性 -
接着在这个 LinearLayout 上加一条线段
-
最后以标题栏和底部导航栏为边界,写一个
FrameLayout
,宽高match_parent
,用做Fragment
的容器
-
-
隐藏顶部导航栏
修改
MainActivity.java
继承自Activity
而非AppCompatActivity
然后在
onCreate()
方法中的super.onCreate(savedInstanceState);
之前加上下列代码requestWindowFeature(Window.FEATURE_NO_TITLE);
最后在 AndroidManifest.xml 设置下 theme 属性
android:theme="@style/Theme.AppCompat.NoActionBar"
注意
把
requestWindowFeature(Window.FEATURE_NO_TITLE);
放在super.onCreate(savedInstanceState);
前面就可以隐藏ActionBar
而不报错 -
在
MainActivity.java
同一目录下新建文件MsFragment.java
package cn.twle.android.fragmenttextview; import android.annotation.SuppressLint; import android.app.Fragment; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; @SuppressLint("ValidFragment") public class MsFragment extends Fragment { private String content; public MsFragment(String content) { this.content = content; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fg_content,container,false); TextView txt_content = (TextView) view.findViewById(R.id.ms_txt_content); txt_content.setText(content); return view; } }
-
继续修改
MainActivity.java
继续修改代码之前,我们先问自己几个问题
Fragment
什么时候初始化和添加到容器中?什么时候 hide 和 show ?- 如何让 TextView 被选中?选中一个 TextView 后,要做一些什么操作?
- 刚进入 MainActivity 怎么样让一个 TextView 处于 Selected 的状态 ?
这都是好问题,我们一一来解答
-
我们选中
TextView
后对对应的Fragment
进行判空,如果为空,初始化,并添加到容器中而 hide 的话,我们定义一个方法 hide() 所有的 Fragment,每次触发点击事件就先调用这个 hideAll() 方法隐藏所有 Fragment,然后如果 TextView 对应的 Fragment 不为空,我们就将这个 Fragment 显示出来
-
通过点击事件来实现,点击
TextView
后先重置所有TextView
的选中状态为false
,然后设置点击的TextView
的选中状态为true
-
通过点击事件来设置选中的,那么在
onCreate()
方法里加个触发点击事件的 方法txt_channel.performClick();
-
继续修改
MainActivity.java
package cn.twle.android.fragmenttextview; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.view.View; import android.view.Window; import android.widget.FrameLayout; import android.widget.TextView; public class MainActivity extends Activity implements View.OnClickListener{ //UI Object private TextView ms_topbar_title; private TextView ms_tab_home; private TextView ms_tab_category; private TextView ms_tab_discover; private TextView ms_tab_cart; private FrameLayout ms_content; //Fragment Object private MsFragment fg1,fg2,fg3,fg4; private FragmentManager fManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); fManager = getFragmentManager(); bindViews(); ms_tab_home.performClick(); //模拟一次点击,既进去后选择第一项 } //UI组件初始化与事件绑定 private void bindViews() { ms_topbar_title = (TextView) findViewById(R.id.ms_topbar_title); ms_tab_home = (TextView) findViewById(R.id.ms_tab_home); ms_tab_category = (TextView) findViewById(R.id.ms_tab_category); ms_tab_discover = (TextView) findViewById(R.id.ms_tab_discover); ms_tab_cart = (TextView) findViewById(R.id.ms_tab_cart); ms_content = (FrameLayout) findViewById(R.id.ms_content); ms_tab_home.setOnClickListener(this); ms_tab_category.setOnClickListener(this); ms_tab_discover.setOnClickListener(this); ms_tab_cart.setOnClickListener(this); } //重置所有文本的选中状态 private void setSelected(){ ms_tab_home.setSelected(false); ms_tab_category.setSelected(false); ms_tab_discover.setSelected(false); ms_tab_cart.setSelected(false); } //隐藏所有 Fragment private void hideAllFragment(FragmentTransaction fragmentTransaction){ if(fg1 != null) fragmentTransaction.hide(fg1); if(fg2 != null) fragmentTransaction.hide(fg2); if(fg3 != null) fragmentTransaction.hide(fg3); if(fg4 != null) fragmentTransaction.hide(fg4); } @Override public void onClick(View v) { FragmentTransaction fTransaction = fManager.beginTransaction(); hideAllFragment(fTransaction); switch (v.getId()){ case R.id.ms_tab_home: setSelected(); ms_tab_home.setSelected(true); if(fg1 == null){ fg1 = new MsFragment("第一个 Fragment"); fTransaction.add(R.id.ms_content,fg1); }else{ fTransaction.show(fg1); } break; case R.id.ms_tab_category: setSelected(); ms_tab_category.setSelected(true); if(fg2 == null){ fg2 = new MsFragment("第二个 Fragment"); fTransaction.add(R.id.ms_content,fg2); }else{ fTransaction.show(fg2); } break; case R.id.ms_tab_discover: setSelected(); ms_tab_discover.setSelected(true); if(fg3 == null){ fg3 = new MsFragment("第三个 Fragment"); fTransaction.add(R.id.ms_content,fg3); }else{ fTransaction.show(fg3); } break; case R.id.ms_tab_cart: setSelected(); ms_tab_cart.setSelected(true); if(fg4 == null){ fg4 = new MsFragment("第四个 Fragment"); fTransaction.add(R.id.ms_content,fg4); }else{ fTransaction.show(fg4); } break; } fTransaction.commit(); } }
注意:
FragmentTransaction
只能使用一次,每次使用都要调用FragmentManager
的beginTransaction()
方法获得FragmentTransaction
事务对象