Android--RadioGroup和Tablayout兩種實現底部導航的方式詳解
自從Android3.0引入Fargment之後,在Activity中使用底部導航進行Fragment的切換已經越來越普遍,或者可以說已經成為了移動應用的標配,而本篇文章我總結了專案中常用的幾種實現導航的方式,分別是RadioGroup、Tablayout、RadioGroup+反射和FragmentTabHost四種實現方式,包含底部和頂部的雙導航介面的實現,實現的結果類似下圖所示:
一、使用RadioGroup+Fragment實現底部導航,使用TabLayout+Fragment實現頂部導航
1,RadioGroup+Fragment的形式是之前開發中比較受歡迎,使用比較多的一種實現形式,所以把它排到第一位。好了不扯了,直接程式碼走起:
首先,在radiogroup.xml中的佈局檔案是:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.linktestproject.RadioGroupActivity">
<FrameLayout
android:id="@+id/fl_radio_show"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
></FrameLayout>
<RadioGroup
android:id="@+id/rg_radio_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="bottom">
<RadioButton
android:id="@+id/rb_radio_homepage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:drawableTop="@drawable/homepage"
android:text="@string/homepage"
android:button="@null"
android:drawablePadding="5dp"
android:textColor="@drawable/radio_button_selector"
/>
<RadioButton
android:id="@+id/rb_radio_subscription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:drawableTop="@drawable/subscription"
android:text="@string/subscription"
android:button="@null"
android:drawablePadding="5dp"
android:textColor="@drawable/radio_button_selector"
/>
<RadioButton
android:id="@+id/rb_radio_find"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:drawableTop="@drawable/find"
android:text="@string/find"
android:button="@null"
android:drawablePadding="5dp"
android:textColor="@drawable/radio_button_selector"
/>
<RadioButton
android:id="@+id/rb_radio_mine"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:drawableTop="@drawable/mine"
android:text="@string/mine"
android:button="@null"
android:drawablePadding="5dp"
android:textColor="@drawable/radio_button_selector"
/>
</RadioGroup>
</LinearLayout>
我們需要對每個RadioButton的圖片資源做一個選擇器,在drawable資料夾下新增四個選擇器,內容是(這是首頁的圖片選擇器,其他與此類似就不貼程式碼了):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@mipmap/tab4_down"></item>
<item android:state_checked="false" android:drawable="@mipmap/tab4"></item>
</selector>
之後我們還要對選中的RadioButton新增字型顏色的選擇器:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="@color/textChecked"></item>
<item android:state_checked="false" android:color="@color/textUnChecked"></item>
</selector>
之後進入Actiivity中,實現程式碼:
public class RadioGroupActivity extends AppCompatActivity implements RadioGroup.OnCheckedChangeListener {
private RadioGroup mRadioGroup;
private Fragment[] mFragments;
private FrameLayout mLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_radio_group);
getSupportActionBar().hide();
initView();
initFragment();
setListener();
}
private void setListener() {
//對RadioGroup設定監聽事件(監聽點選選擇)
mRadioGroup.setOnCheckedChangeListener(this);
}
private void initFragment() {
//初始化要顯示的Fragment陣列
mFragments=new Fragment[4];
mFragments[0]=new HomepageFragment();
mFragments[1]=new SubscriptionFragment();
mFragments[2]=new FindFragment();
mFragments[3]=new MineFragment();
//獲取Fragment管理器
FragmentManager manager=getSupportFragmentManager();
//獲取事物(使用v4包下)
FragmentTransaction transaction=manager.beginTransaction();
//預設選中HomepageFragment替換Framelayout
transaction.replace(R.id.fl_radio_show,mFragments[0]);
//提交事物
transaction.commit();
//預設點選首頁
mRadioGroup.check(R.id.rb_radio_homepage);
}
private void initView() {
mRadioGroup= (RadioGroup) findViewById(R.id.rg_radio_navigation);
mLayout= (FrameLayout) findViewById(R.id.fl_radio_show);
}
@Override
public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
//寫法與預設點選頁面的相同
FragmentManager manager=getSupportFragmentManager();
FragmentTransaction transaction=manager.beginTransaction();
switch(checkedId){
case R.id.rb_radio_homepage:
transaction.replace(R.id.fl_radio_show,mFragments[0]);
break;
case R.id.rb_radio_subscription:
transaction.replace(R.id.fl_radio_show,mFragments[1]);
break;
case R.id.rb_radio_find:
transaction.replace(R.id.fl_radio_show,mFragments[2]);
break;
case R.id.rb_radio_mine:
transaction.replace(R.id.fl_radio_show,mFragments[3]);
break;
}
transaction.commit();
}
}
到此底部導航就已經完成,實現效果如下圖所示:
2,在HomepageFragment中使用Tablayout+Fragment實現頂部導航
首先Tablayout是Android5.0釋出的Design包中的元件,所以我們在使用之前必須加入Design包的依賴(這裡不會就自己百度吧)。然後在Homepage的資原始檔中的佈局如下:
<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"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.example.linktestproject.fragments.HomepageFragment">
<android.support.design.widget.TabLayout
android:id="@+id/tl_homepage_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/colorPrimary"
app:tabSelectedTextColor="@color/textChecked"
app:tabTextColor="@color/textUnChecked"
>
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp_homepage_show"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</android.support.v4.view.ViewPager>
</LinearLayout>
Tablayout的屬性(此處只是給出用到的三個屬性,其他常用的屬性會在Tablayout+Fragment中給出):
app:tabIndicatorColor="@color/colorPrimary" //下邊指示橫線的顏色
app:tabSelectedTextColor="@color/textChecked" //Text選中的文字顏色
app:tabTextColor="@color/textUnChecked" //沒有選中的文字顏色
在HomepageFragment中的程式碼是:
public class HomepageFragment extends Fragment {
private ViewPager mViewPager;
private TabLayout mTabLayout;
private List<Fragment>mFragments;
private List<String>mTitles;
private HomepageAdapter mAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_homepage, container, false);
initView(view);
initData(view);
setData();
return view;
}
private void setData() {
mViewPager.setAdapter(mAdapter);
//設定Viewpager和Tablayout進行聯動
mTabLayout.setupWithViewPager(mViewPager);
// //將標題設定可以左右搖動而不是移動
// mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
// //設定預載入頁數
// mViewPager.setOffscreenPageLimit(3);
}
private void initData(View view) {
//初始化導航標題,如果是title在json資料中,在初始化的時候可以使用非同步任務載入的形式新增
mTitles=new ArrayList<>();
mTitles.add("熱門");
mTitles.add("分類");
mTitles.add("榜單");
//初始化Fragment
mFragments=new ArrayList<>();
for (int i = 0; i <mTitles.size() ; i++) {
if(i==0){
mFragments.add(new HotFragment());
}else if(i==1){
mFragments.add(new ClassifyFragment());
}else if(i==2){
mFragments.add(new ListingFragment());
}
}
//getSupportFragmentManager()是Activity巢狀fragment時使用
//getChildFragmentManager()是Fragment巢狀Fragment時使用
mAdapter=new HomepageAdapter(getChildFragmentManager(),mFragments,mTitles);
mAdapter.notifyDataSetChanged();
}
private void initView(View view) {
mViewPager= (ViewPager) view.findViewById(R.id.vp_homepage_show);
mTabLayout= (TabLayout) view.findViewById(R.id.tl_homepage_navigation);
}
}
用到的Viewpager的Adapter程式碼是:
public class HomepageAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragments;
private List<String> mTitles;
public HomepageAdapter(FragmentManager fm, List<Fragment> framents, List<String> titles) {
super(fm);
mFragments = framents;
mTitles = titles;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments == null ? 0 : mFragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitles.get(position);
}
}
最後所實現的效果如下所示:
二、使用Tablayout+Fragment實現底部導航
注意:之後三種實現方式不再實現頂部導航,只實現底部導航,而在第二種方式中將詳細介紹Tablyout的用法及屬性。
1,首先我們先總結一下Tablayout的基本使用(和頂部導航不同的方式實現)
第一種使用先看佈局檔案:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.linktestproject.TablayoutActivity">
<android.support.design.widget.TabLayout
android:id="@+id/tl_tablayout_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</android.support.design.widget.TabLayout>
</android.support.constraint.ConstraintLayout>
在程式碼中:
mTabLayout.addTab(mTabLayout.newTab().setText("Title1"));
mTabLayout.addTab(mTabLayout.newTab().setText("Title2"));
mTabLayout.addTab(mTabLayout.newTab().setText("Title3"));
mTabLayout.addTab(mTabLayout.newTab().setText("Title4"));
第二種使用方式(佈局):
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.linktestproject.TablayoutActivity">
<android.support.design.widget.TabLayout
android:id="@+id/tl_tablayout_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title1"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title2"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title3"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title4"/>
</android.support.design.widget.TabLayout>
</android.support.constraint.ConstraintLayout>
二種方式實現的結果相同,如下圖所示:
下邊我們總結一下Tablayout的屬性用法:
app:tabSelectedTextColor=" " //改變選中字型的顏色
app:tabTextColor=" " //改變未選中字型的顏色
app:tabBackground=" " //改變整個TabLayout的顏色app:tabIndicatorColor=" " //改變指示器下標的顏色
app:tabTextAppearance=" " //改變Tablayout的內部字型大小
app:tabPadding="xxdp" //內部子控制元件的Padding值
app:paddingEnd="xxdp" //設定整個Tablayout的Padding值
app:paddingStart="xxdp"
app:tabMaxWidth="xxdp" //設定最小和最大的tab寬度
app:tabMinWidth="xxdp"
app:tabContentStart="xxdp" //設定Tablayout開始位置的偏移量
tabLayout.addTab(tabLayout.newTab().setText("Tab 1").setIcon(R.mipmap.ic_launcher));//為Tablayout新增圖片
app:tabMode=“scrollable” //這個屬性用於tab比較多的情況下,實現的結果如下圖:
app:tabIndicatorHeight=" "//改變指示器下標的高度
app:tabIndicatorHeight="0dp"//當設定為0時就會去掉指示器下標
2,通過Tablayout+Fragment實現底部導航
首先佈局檔案很簡單:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.linktestproject.TablayoutActivity">
<android.support.v4.view.ViewPager
android:id="@+id/vp_tablayout_show"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
></android.support.v4.view.ViewPager>
<android.support.design.widget.TabLayout
android:id="@+id/tl_tablayout_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
style="@style/CustomTablayout"
>
</android.support.design.widget.TabLayout>
</LinearLayout>
給Tablayout設定style樣式:
<style name="CustomTablayout" parent="Widget.Design.TabLayout">
<item name="tabIndicatorHeight">0dp</item>
<item name="tabSelectedTextColor">@color/textChecked</item>
<item name="tabTextColor">@color/textUnChecked</item>
</style>
切換頁面使用了ViewPager和Fragment,所以我們的介面卡Adapter的內容:
public class TablayoutAdapter extends FragmentPagerAdapter {
private List<Fragment>mFragments;
private List<String>mTitles;
public TablayoutAdapter(FragmentManager fm, List<Fragment>mFragments, List<String>mTitles) {
super(fm);
this.mFragments=mFragments;
this.mTitles=mTitles;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments==null?0:mFragments.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitles.get(position);
}
}
最後是TablayoutActivity中的程式碼:
public class TablayoutActivity extends AppCompatActivity {
private TabLayout mTabLayout;
private ViewPager mViewPager;
private List<Fragment>mFragments;
private TablayoutAdapter mAdapter;
private List<String>mTitles;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tablayout);
getSupportActionBar().hide();
initView();
initData();
setData();
setListener();
}
private void setListener() {
}
private void setData() {
mViewPager.setAdapter(mAdapter);
mTabLayout.setupWithViewPager(mViewPager);
for (int i = 0; i <mTabLayout.getTabCount() ; i++) {
TabLayout.Tab tab=mTabLayout.getTabAt(i);
Drawable drawable=null;
switch(i){
case 0:
//圖片資源我們同樣要使用選擇器,選擇器我們不能使用state_checked屬性,而應該使用state_selected屬性
drawable=getResources().getDrawable(R.drawable.hometablayout);
break;
case 1:
drawable=getResources().getDrawable(R.drawable.subtablayout);
break;
case 2:
drawable=getResources().getDrawable(R.drawable.findtablayout);
break;
case 3:
drawable=getResources().getDrawable(R.drawable.minetablayout);
break;
}
tab.setIcon(drawable);
}
}
private void initData() {
mTitles=new ArrayList<>();
mTitles.add("首頁");
mTitles.add("訂閱");
mTitles.add("發現");
mTitles.add("我的");
mFragments=new ArrayList<>();
for (int i = 0; i <mTitles.size(); i++) {
if(i==0){
mFragments.add(new HomepageFragment());
}else if(i==1){
mFragments.add(new SubscriptionFragment());
}else if(i==2){
mFragments.add(new FindFragment());
}else if(i==3){
mFragments.add(new MineFragment());
}
}
mAdapter=new TablayoutAdapter(getSupportFragmentManager(),mFragments,mTitles);
mAdapter.notifyDataSetChanged();
}
private void initView() {
mTabLayout= (TabLayout) findViewById(R.id.tl_tablayout_navigation);
mViewPager= (ViewPager) findViewById(R.id.vp_tablayout_show);
}
}
drawable=getResources().getDrawable(R.drawable.hometablayout);//這是一個已經過時的方法,
//現在是最新的方法是傳入兩個引數,但需要的最低版本有限制(Call requires API level 21 (current min is 15)):
drawable=getResources().getDrawable(R.drawable.hometablayout,null);所以自己根據需要自己選擇
上邊註釋的圖片選擇器要根據控制元件選擇對應的屬性,選擇器內容如下:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@mipmap/tab4_down"></item>
<item android:state_selected="false" android:drawable="@mipmap/tab4"></item>
</selector>
以上就是Tablayout+ViewPager+Fragment實現底部導航的所有內容,實現的結果如下圖所示:本來準備四種方式實現底部導航,但篇幅較大所以把下邊兩種方式寫到下一遍部落格中,我在原始碼中新增各個Fragment的生命週期方法Log,可以自己執行比較這幾種實現方式。從我自己的開發經歷以及Fragment的生命週期的比較,我推薦使用反射機制實現底部導航,並且反射也是你進階需要掌握的一個知識點。