主界面:MainActivity
package com.yongdaimi.android.androidapitest; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import com.yongdaimi.android.androidapitest.fragment.HomePageFragment; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFragment(); } private void initFragment() { HomePageFragment homePageFragment = HomePageFragment.newInstance(); getSupportFragmentManager() .beginTransaction() .add(R.id.fl_main_container, homePageFragment, homePageFragment.getClass().getSimpleName()) .commit(); } }
主界面布局:activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/fl_main_container" />
主界面入口的Fragment: HomePageFragment.java
package com.yongdaimi.android.androidapitest.fragment; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.LabelVisibilityMode; import com.yongdaimi.android.androidapitest.R; import java.util.ArrayList; import java.util.List; /** * A simple {@link Fragment} subclass. * Activities that contain this fragment must implement the * HomePageFragment.OnFragmentInteractionListener interface * to handle interaction events. * Use the {@link HomePageFragment#newInstance} factory method to * create an instance of this fragment. */ public class HomePageFragment extends Fragment { public static final String TAG = "xp.chen"; private View mContentView; private ViewPager vp_pager; private BottomNavigationView nv_bottom_menu; private List<BaseViewController> mPageList; public static HomePageFragment newInstance() { return new HomePageFragment(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment mContentView = inflater.inflate(R.layout.fragment_home_page, container, false); return mContentView; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); initView(); initPages(); setListener(); } private void initPages() { mPageList = new ArrayList<>(); HomeViewController homeViewController = new HomeViewController(getActivity()); mPageList.add(homeViewController); ContactViewController contactViewController = new ContactViewController(getActivity()); mPageList.add(contactViewController); SearchViewController searchViewController = new SearchViewController(getActivity()); mPageList.add(searchViewController); SettingsViewController settingsViewController = new SettingsViewController(getActivity()); mPageList.add(settingsViewController); vp_pager.setAdapter(mPagerAdapter); } private void initView() { vp_pager = mContentView.findViewById(R.id.vp_pager); nv_bottom_menu = mContentView.findViewById(R.id.nv_bottom_menu); nv_bottom_menu.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED); nv_bottom_menu.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener); } private void setListener() { vp_pager.addOnPageChangeListener(mOnPageChangeListener); } private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) { vp_pager.setCurrentItem(menuItem.getOrder()); return true; } }; private PagerAdapter mPagerAdapter = new PagerAdapter() { @Override public int getCount() { return mPageList.size(); } @Override public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { return view == object; } @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { BaseViewController baseViewController = mPageList.get(position); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); container.addView(baseViewController, layoutParams); return baseViewController; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } @Override public int getItemPosition(Object object) { return super.getItemPosition(object); } @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); } }; private ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { Log.i(TAG, "onPageSelected: " + position); nv_bottom_menu.getMenu().getItem(position).setChecked(true); } @Override public void onPageScrollStateChanged(int state) { } }; }
主界面入口Fragment的布局:fragment_home_page.xml
<?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" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <androidx.viewpager.widget.ViewPager android:id="@+id/vp_pager" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/nv_bottom_menu" android:layout_width="match_parent" android:layout_height="50dip" android:layout_gravity="bottom" android:background="?android:attr/windowBackground" app:menu="@menu/menu_navigation" app:elevation="4dip" /> </LinearLayout>
BottomNavigationView的menu文件:menu_navigation.xml
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/item_tab1" android:icon="@drawable/ic_menu_home" android:title="Home" android:checked="true" android:orderInCategory="0" /> <item android:id="@+id/item_tab2" android:icon="@drawable/ic_menu_contact" android:title="Contact" android:orderInCategory="1" /> <item android:id="@+id/item_tab3" android:icon="@drawable/ic_menu_search" android:title="Search" android:orderInCategory="2" /> <item android:id="@+id/item_tab4" android:icon="@drawable/ic_menu_settings" android:title="Settings" android:orderInCategory="3" /> </menu>
这里有几个地方需要特别说明一下:
1. HomePageFragment.java 文件中的 nv_bottom_menu.setLabelVisibilityMode(LabelVisibilityMode.LABEL_VISIBILITY_LABELED); 用来设置BottomNavigationView在切换时的状态,如果不设置,在点击BottomNavigationView上的item在切换时文字可能会发生隐藏。
2. 上面menu文件中的 orderInCategory 属性用于设置菜单项的排列顺序,必须设置为大于或者等于0的整数值。数值小的排列在前,如果值相等,则按照XML中的顺序展现。 title 属性用于指定navigation Item的标题, icon 属性用于指定navigation item的图标。
3. fragment_home_page文件中的 app:elevation="4dip" 属性用于设置BottomNavigationView菜单顶部的模糊程度,如果不设置的话,BottomNavigationView的菜单顶部会很模糊。
最后是几个用于切换的Layout:
package com.yongdaimi.android.androidapitest.fragment; import android.content.Context; import android.widget.FrameLayout; import androidx.annotation.NonNull; public abstract class BaseViewController extends FrameLayout { public BaseViewController(@NonNull Context context) { super(context); } }
package com.yongdaimi.android.androidapitest.fragment; import android.content.Context; import android.view.LayoutInflater; import androidx.annotation.NonNull; import com.yongdaimi.android.androidapitest.R; public class HomeViewController extends BaseViewController { public HomeViewController(@NonNull Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.layout_home, this); } }
package com.yongdaimi.android.androidapitest.fragment; import android.content.Context; import android.view.LayoutInflater; import androidx.annotation.NonNull; import com.yongdaimi.android.androidapitest.R; public class ContactViewController extends BaseViewController { public ContactViewController(@NonNull Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.layout_contact, this); } }
package com.yongdaimi.android.androidapitest.fragment; import android.content.Context; import android.view.LayoutInflater; import androidx.annotation.NonNull; import com.yongdaimi.android.androidapitest.R; public class SearchViewController extends BaseViewController { public SearchViewController(@NonNull Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.layout_search, this); } }
package com.yongdaimi.android.androidapitest.fragment; import android.content.Context; import android.view.LayoutInflater; import androidx.annotation.NonNull; import com.yongdaimi.android.androidapitest.R; public class SettingsViewController extends BaseViewController { public SettingsViewController(@NonNull Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.layout_settings, this); } }
基本上都是一样的,它们的布局文件也很简单,就是一个布局里加一个TextView。
最终效果:
我这里的PageAdapter中并没有直接使用Fragment, 因为ViewPager在加载Fragment时,会同时初始化几个Fragment,这在实际开发中很不友好,目前正在想办法解决,这也是我什么只使用一个Fragment多个Layout的主要原因。如果希望ViewPager中添加的也是Fragment,可参考:
Android布局实现-BottomNavigationView+ViewPager+Fragment+整合